From 68bfdc2ca784bc094b8b32444b9db97c62051717 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Mon, 12 Jun 2023 23:33:53 -0400 Subject: [PATCH 001/269] Start role preferences rewrite --- code/__DEFINES/role_preferences.dm | 76 ++++++++++++++++++- .../role_preference/_role_preference.dm | 73 ++++++++++++++++++ .../role_preference/role_operative.dm | 41 ++++++++++ .../role_preference/role_traitor.dm | 36 +++++++++ 4 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 code/modules/antagonists/role_preference/_role_preference.dm create mode 100644 code/modules/antagonists/role_preference/role_operative.dm create mode 100644 code/modules/antagonists/role_preference/role_traitor.dm diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index 4c54c1be527be..f078bce9270ed 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -9,10 +9,14 @@ #define MINUTES_REQUIRED_ADVANCED 900 //For jobs that aren't command, but hold a similar level of importance to either their department or the round as a whole. #define MINUTES_REQUIRED_COMMAND 1200 //For command positions, to be weighed against the relevant department + +// Banning snowflake - global antag ban. Badly named. +#define ROLE_SYNDICATE "Syndicate" + //These are synced with the Database, if you change the values of the defines //then you MUST update the database! -#define ROLE_SYNDICATE "Syndicate" #define ROLE_TRAITOR "Traitor" + #define ROLE_OPERATIVE "Operative" #define ROLE_CHANGELING "Changeling" #define ROLE_WIZARD "Wizard" @@ -51,6 +55,9 @@ #define ROLE_HOLOPARASITE "Holoparasite" #define ROLE_TERATOMA "Teratoma" #define ROLE_EXPERIMENTAL_CLONE "Experimental Clone" + + + #define ROLE_SPIDER "Spider" #define ROLE_SWARMER "Swarmer" #define ROLE_MORPH "Morph" @@ -100,7 +107,74 @@ GLOBAL_LIST_INIT(special_roles, list( ROLE_FUGITIVE_HUNTER, )) +GLOBAL_LIST_INIT(poll, list( + +)) + + +#define POLL_IGNORE_ALIEN_LARVA "alien_larva" +#define POLL_IGNORE_ASHWALKER "ashwalker" +#define POLL_IGNORE_CLOCKWORK "clockwork" +#define POLL_IGNORE_CONSTRUCT "construct" +#define POLL_IGNORE_CONTRACTOR_SUPPORT "contractor_support" +#define POLL_IGNORE_DEFECTIVECLONE "defective_clone" +#define POLL_IGNORE_DRONE "drone" +#define POLL_IGNORE_EXPERIMENTAL_CLONE "experimental_clone" +#define POLL_IGNORE_GOLEM "golem" +#define POLL_IGNORE_HOLOPARASITE "holoparasite" +#define POLL_IGNORE_HOLYCARP "holy_carp" +#define POLL_IGNORE_HOLYUNDEAD "holy_undead" +#define POLL_IGNORE_IMAGINARYFRIEND "imaginary_friend" +#define POLL_IGNORE_PAI "pai" +#define POLL_IGNORE_POSIBRAIN "posibrain" +#define POLL_IGNORE_POSSESSED_BLADE "possessed_blade" +#define POLL_IGNORE_PYROSLIME "slime" +#define POLL_IGNORE_SENTIENCE_POTION "sentience_potion" +#define POLL_IGNORE_SHADE "shade" +#define POLL_IGNORE_SPECTRAL_BLADE "spectral_blade" +#define POLL_IGNORE_SPIDER "spider" +#define POLL_IGNORE_SPLITPERSONALITY "split_personality" +#define POLL_IGNORE_SWARMER "swarmer" +#define POLL_IGNORE_SYNDICATE "syndicate" + +GLOBAL_LIST_INIT(poll_ignore_desc, list( + POLL_IGNORE_ALIEN_LARVA = "Xenomorph larva", + POLL_IGNORE_ASHWALKER = "Ashwalker eggs", + POLL_IGNORE_CONSTRUCT = "Construct", + POLL_IGNORE_CONTRACTOR_SUPPORT = "Contractor Support Unit", + POLL_IGNORE_DEFECTIVECLONE = "Defective clone", + POLL_IGNORE_DRONE = "Drone shells", + POLL_IGNORE_EXPERIMENTAL_CLONE = "Experimental clone", + POLL_IGNORE_GOLEM = "Golems", + POLL_IGNORE_HOLOPARASITE = "Holoparasite", + POLL_IGNORE_HOLYCARP = "Holy Carp", + POLL_IGNORE_HOLYUNDEAD = "Holy Undead", + POLL_IGNORE_IMAGINARYFRIEND = "Imaginary Friend", + POLL_IGNORE_PAI = "Personal AI", + POLL_IGNORE_POSIBRAIN = "Positronic brain", + POLL_IGNORE_POSSESSED_BLADE = "Possessed blade", + POLL_IGNORE_PYROSLIME = "Slime", + POLL_IGNORE_SENTIENCE_POTION = "Sentience potion", + POLL_IGNORE_SHADE = "Shade", + POLL_IGNORE_SPECTRAL_BLADE = "Spectral blade", + POLL_IGNORE_SPIDER = "Spiders", + POLL_IGNORE_SPLITPERSONALITY = "Split Personality", + POLL_IGNORE_SWARMER = "Swarmer shells", + POLL_IGNORE_SYNDICATE = "Syndicate", +)) +GLOBAL_LIST_INIT(poll_ignore, init_poll_ignore()) + + +/proc/init_poll_ignore() + . = list() + for (var/k in GLOB.poll_ignore_desc) + .[k] = list() + //Job defines for what happens when you fail to qualify for any job during job selection #define BEOVERFLOW 1 #define BERANDOMJOB 2 #define RETURNTOLOBBY 3 + +#define ROLE_PREFERENCE_CATEGORY_ANAGONIST "Antagonists" +#define ROLE_PREFERENCE_CATEGORY_MIDROUND_LIVING "Midrounds (Living)" +#define ROLE_PREFERENCE_CATEGORY_MIDROUND_GHOST "Midrounds (Ghost Poll)" diff --git a/code/modules/antagonists/role_preference/_role_preference.dm b/code/modules/antagonists/role_preference/_role_preference.dm new file mode 100644 index 0000000000000..b48394fee5be8 --- /dev/null +++ b/code/modules/antagonists/role_preference/_role_preference.dm @@ -0,0 +1,73 @@ +/datum/role_preference + var/name + /// A brief description of this role, to display in the preferences menu. + var/description + /// The ROLE_X define for this preference. Optional, but must have a poll_ignore_key otherwise. + var/role_key + /// The main gamemode that spawns this ROLE_X roundstart. + /// This is used to get exp_living requirements by the prefs menu. + /// TODO tgui-prefs replace this + var/gamemode + /// The POLL_IGNORE_X define for this preference. Optional, but must have a role_key otherwise. + var/poll_ignore_key + /// What heading to display this entry under in the preferences menu. Use ROLE_PREFERENCE_CATEGORY defines. + var/category + /// The typepath for the outfit to show in the preview for the preferences menu. + var/preview_outfit + /// If this preference is enabled by default. This should be true for ghost polled antagonists, + /// but disabled for roundstart, latejoin, or midround assigned antagonists. + var/enabled_by_default = FALSE + /// The base abstract path for this subtype. + var/abstract_type = /datum/role_preference + +/// Creates an icon from the preview outfit. +/// Custom implementors of `get_preview_icon` should use this, as the +/// result of `get_preview_icon` is expected to be the completed version. +/datum/role_preference/proc/render_preview_outfit(datum/outfit/outfit, mob/living/carbon/human/dummy) + dummy = dummy || new /mob/living/carbon/human/dummy/consistent + dummy.equipOutfit(outfit, visualsOnly = TRUE) + COMPILE_OVERLAYS(dummy) + var/icon = getFlatIcon(dummy) + + // We don't want to qdel the dummy right away, since its items haven't initialized yet. + SSatoms.prepare_deletion(dummy) + + return icon + +/// Given an icon, will crop it to be consistent of those in the preferences menu. +/// Not necessary, and in fact will look bad if it's anything other than a human. +/datum/role_preference/proc/finish_preview_icon(icon/icon) + // Zoom in on the top of the head and the chest + // I have no idea how to do this dynamically. + icon.Scale(115, 115) + + // This is probably better as a Crop, but I cannot figure it out. + icon.Shift(WEST, 8) + icon.Shift(SOUTH, 30) + + icon.Crop(1, 1, ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE) + + return icon + +/// Returns the icon to show on the preferences menu. +/datum/role_preference/proc/get_preview_icon() + if (isnull(preview_outfit)) + return null + + return finish_preview_icon(render_preview_outfit(preview_outfit)) + +/// Includes latejoin and roundstart antagonists +/datum/role_preference/antagonist + category = ROLE_PREFERENCE_CATEGORY_ANAGONIST + abstract_type = /datum/role_preference/antagonist + +/// Includes autotraitor and gamemode midround assignments - being forced into an antagonist during a round (does not apply to conversion antags). +/datum/role_preference/midround_living + category = ROLE_PREFERENCE_CATEGORY_MIDROUND_LIVING + abstract_type = /datum/role_preference/midround_living + +/// Includes anything polled from ghosts. +/datum/role_preference/midround_ghost + category = ROLE_PREFERENCE_CATEGORY_MIDROUND_GHOST + abstract_type = /datum/role_preference/midround_ghost + enabled_by_default = TRUE diff --git a/code/modules/antagonists/role_preference/role_operative.dm b/code/modules/antagonists/role_preference/role_operative.dm new file mode 100644 index 0000000000000..b2ed0ee153e8e --- /dev/null +++ b/code/modules/antagonists/role_preference/role_operative.dm @@ -0,0 +1,41 @@ +#define OPERATIVE_DESC "Congratulations, agent. You have been chosen to join the Syndicate \ + Nuclear Operative strike team. Your mission, whether or not you choose \ + to accept it, is to destroy Nanotrasen's most advanced research facility! \ + That's right, you're going to Space Station 13. \ + Retrieve the nuclear authentication disk, use it to activate the nuclear \ + fission explosive, and destroy the station." + +/datum/role_preference/antagonist/nuclear_operative + name = "Nuclear Operative" + description = OPERATIVE_DESC + role_key = ROLE_OPERATIVE + gamemode = /datum/game_mode/nuclear + +/datum/role_preference/antagonist/nuclear_operative/get_preview_icon() + return get_nuclear_operative_icon() + +/datum/role_preference/midround_ghost/nuclear_operative + name = "Nuclear Operative (Assailant)" + description = OPERATIVE_DESC + role_key = ROLE_OPERATIVE + +/datum/role_preference/midround_ghost/nuclear_operative/get_preview_icon() + return get_nuclear_operative_icon() + +#undef OPERATIVE_DESC + +/proc/get_nuclear_operative_icon() + var/icon/final_icon = render_preview_outfit(/datum/outfit/nuclear_operative) + var/icon/teammate = render_preview_outfit(/datum/outfit/nuclear_operative) + teammate.Blend(rgb(128, 128, 128, 128), ICON_MULTIPLY) + + final_icon.Blend(teammate, ICON_OVERLAY, -world.icon_size / 4, 0) + final_icon.Blend(teammate, ICON_OVERLAY, world.icon_size / 4, 0) + + return finish_preview_icon(final_icon) + +/datum/outfit/nuclear_operative + name = "Nuclear Operative (Preview only)" + + suit = /obj/item/clothing/suit/space/hardsuit/syndi + head = /obj/item/clothing/head/helmet/space/hardsuit/syndi diff --git a/code/modules/antagonists/role_preference/role_traitor.dm b/code/modules/antagonists/role_preference/role_traitor.dm new file mode 100644 index 0000000000000..c07ba621579d5 --- /dev/null +++ b/code/modules/antagonists/role_preference/role_traitor.dm @@ -0,0 +1,36 @@ +#define TRAITOR_DESC "An unpaid debt. A score to be settled. Maybe you were just in the wrong \ + place at the wrong time. Whatever the reasons, you were selected to \ + infiltrate Space Station 13. Start with a set of sinister objectives and an uplink to purchase \ + items to get the job done." + +/datum/role_preference/antagonist/traitor + name = "Traitor" + description = TRAITOR_DESC + role_key = ROLE_TRAITOR + gamemode = /datum/game_mode/traitor + preview_outfit = /datum/outfit/traitor + +/datum/role_preference/midround_living/traitor + name = "Traitor (Sleeper Agent)" + description = TRAITOR_DESC + role_key = ROLE_TRAITOR + preview_outfit = /datum/outfit/traitor + +#undef TRAITOR_DESC + +/datum/outfit/traitor + name = "Traitor (Preview only)" + + uniform = /obj/item/clothing/under/syndicate + gloves = /obj/item/clothing/gloves/combat + mask = /obj/item/clothing/mask/gas + l_hand = /obj/item/melee/transforming/energy/sword + r_hand = /obj/item/gun/energy/kinetic_accelerator/crossbow + +/datum/outfit/traitor/post_equip(mob/living/carbon/human/H, visualsOnly) + var/obj/item/melee/transforming/energy/sword/sword = locate() in H.held_items + sword.icon_state = "swordred" + H.update_inv_hands() + H.hair_style = "Messy" + H.hair_color = "111" + H.update_hair() From bf39b585dec186a53c7bc2937ff2a453273ce271 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Tue, 13 Jun 2023 03:17:57 -0400 Subject: [PATCH 002/269] Actually tie it to the preferences menu and stuff --- beestation.dme | 5 ++ code/__DEFINES/role_preferences.dm | 63 ------------------- .../role_preference/_role_preference.dm | 38 ----------- .../role_preference/role_changeling.dm | 7 +++ .../role_preference/role_operative.dm | 22 ------- .../role_preference/role_traitor.dm | 19 ------ .../role_preference/role_wizard.dm | 6 ++ 7 files changed, 18 insertions(+), 142 deletions(-) create mode 100644 code/modules/antagonists/role_preference/role_changeling.dm create mode 100644 code/modules/antagonists/role_preference/role_wizard.dm diff --git a/beestation.dme b/beestation.dme index 1de63bf640215..9884cd5f3e1e7 100644 --- a/beestation.dme +++ b/beestation.dme @@ -1922,6 +1922,11 @@ #include "code\modules\antagonists\revenant\revenant_blight.dm" #include "code\modules\antagonists\revenant\revenant_spawn_event.dm" #include "code\modules\antagonists\revolution\revolution.dm" +#include "code\modules\antagonists\role_preference\_role_preference.dm" +#include "code\modules\antagonists\role_preference\role_changeling.dm" +#include "code\modules\antagonists\role_preference\role_operative.dm" +#include "code\modules\antagonists\role_preference\role_traitor.dm" +#include "code\modules\antagonists\role_preference\role_wizard.dm" #include "code\modules\antagonists\roundstart_special\special_antagonist.dm" #include "code\modules\antagonists\roundstart_special\undercover\undercover.dm" #include "code\modules\antagonists\santa\santa.dm" diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index f078bce9270ed..d2073bdd7a0b2 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -107,69 +107,6 @@ GLOBAL_LIST_INIT(special_roles, list( ROLE_FUGITIVE_HUNTER, )) -GLOBAL_LIST_INIT(poll, list( - -)) - - -#define POLL_IGNORE_ALIEN_LARVA "alien_larva" -#define POLL_IGNORE_ASHWALKER "ashwalker" -#define POLL_IGNORE_CLOCKWORK "clockwork" -#define POLL_IGNORE_CONSTRUCT "construct" -#define POLL_IGNORE_CONTRACTOR_SUPPORT "contractor_support" -#define POLL_IGNORE_DEFECTIVECLONE "defective_clone" -#define POLL_IGNORE_DRONE "drone" -#define POLL_IGNORE_EXPERIMENTAL_CLONE "experimental_clone" -#define POLL_IGNORE_GOLEM "golem" -#define POLL_IGNORE_HOLOPARASITE "holoparasite" -#define POLL_IGNORE_HOLYCARP "holy_carp" -#define POLL_IGNORE_HOLYUNDEAD "holy_undead" -#define POLL_IGNORE_IMAGINARYFRIEND "imaginary_friend" -#define POLL_IGNORE_PAI "pai" -#define POLL_IGNORE_POSIBRAIN "posibrain" -#define POLL_IGNORE_POSSESSED_BLADE "possessed_blade" -#define POLL_IGNORE_PYROSLIME "slime" -#define POLL_IGNORE_SENTIENCE_POTION "sentience_potion" -#define POLL_IGNORE_SHADE "shade" -#define POLL_IGNORE_SPECTRAL_BLADE "spectral_blade" -#define POLL_IGNORE_SPIDER "spider" -#define POLL_IGNORE_SPLITPERSONALITY "split_personality" -#define POLL_IGNORE_SWARMER "swarmer" -#define POLL_IGNORE_SYNDICATE "syndicate" - -GLOBAL_LIST_INIT(poll_ignore_desc, list( - POLL_IGNORE_ALIEN_LARVA = "Xenomorph larva", - POLL_IGNORE_ASHWALKER = "Ashwalker eggs", - POLL_IGNORE_CONSTRUCT = "Construct", - POLL_IGNORE_CONTRACTOR_SUPPORT = "Contractor Support Unit", - POLL_IGNORE_DEFECTIVECLONE = "Defective clone", - POLL_IGNORE_DRONE = "Drone shells", - POLL_IGNORE_EXPERIMENTAL_CLONE = "Experimental clone", - POLL_IGNORE_GOLEM = "Golems", - POLL_IGNORE_HOLOPARASITE = "Holoparasite", - POLL_IGNORE_HOLYCARP = "Holy Carp", - POLL_IGNORE_HOLYUNDEAD = "Holy Undead", - POLL_IGNORE_IMAGINARYFRIEND = "Imaginary Friend", - POLL_IGNORE_PAI = "Personal AI", - POLL_IGNORE_POSIBRAIN = "Positronic brain", - POLL_IGNORE_POSSESSED_BLADE = "Possessed blade", - POLL_IGNORE_PYROSLIME = "Slime", - POLL_IGNORE_SENTIENCE_POTION = "Sentience potion", - POLL_IGNORE_SHADE = "Shade", - POLL_IGNORE_SPECTRAL_BLADE = "Spectral blade", - POLL_IGNORE_SPIDER = "Spiders", - POLL_IGNORE_SPLITPERSONALITY = "Split Personality", - POLL_IGNORE_SWARMER = "Swarmer shells", - POLL_IGNORE_SYNDICATE = "Syndicate", -)) -GLOBAL_LIST_INIT(poll_ignore, init_poll_ignore()) - - -/proc/init_poll_ignore() - . = list() - for (var/k in GLOB.poll_ignore_desc) - .[k] = list() - //Job defines for what happens when you fail to qualify for any job during job selection #define BEOVERFLOW 1 #define BERANDOMJOB 2 diff --git a/code/modules/antagonists/role_preference/_role_preference.dm b/code/modules/antagonists/role_preference/_role_preference.dm index b48394fee5be8..415375b67e580 100644 --- a/code/modules/antagonists/role_preference/_role_preference.dm +++ b/code/modules/antagonists/role_preference/_role_preference.dm @@ -12,50 +12,12 @@ var/poll_ignore_key /// What heading to display this entry under in the preferences menu. Use ROLE_PREFERENCE_CATEGORY defines. var/category - /// The typepath for the outfit to show in the preview for the preferences menu. - var/preview_outfit /// If this preference is enabled by default. This should be true for ghost polled antagonists, /// but disabled for roundstart, latejoin, or midround assigned antagonists. var/enabled_by_default = FALSE /// The base abstract path for this subtype. var/abstract_type = /datum/role_preference -/// Creates an icon from the preview outfit. -/// Custom implementors of `get_preview_icon` should use this, as the -/// result of `get_preview_icon` is expected to be the completed version. -/datum/role_preference/proc/render_preview_outfit(datum/outfit/outfit, mob/living/carbon/human/dummy) - dummy = dummy || new /mob/living/carbon/human/dummy/consistent - dummy.equipOutfit(outfit, visualsOnly = TRUE) - COMPILE_OVERLAYS(dummy) - var/icon = getFlatIcon(dummy) - - // We don't want to qdel the dummy right away, since its items haven't initialized yet. - SSatoms.prepare_deletion(dummy) - - return icon - -/// Given an icon, will crop it to be consistent of those in the preferences menu. -/// Not necessary, and in fact will look bad if it's anything other than a human. -/datum/role_preference/proc/finish_preview_icon(icon/icon) - // Zoom in on the top of the head and the chest - // I have no idea how to do this dynamically. - icon.Scale(115, 115) - - // This is probably better as a Crop, but I cannot figure it out. - icon.Shift(WEST, 8) - icon.Shift(SOUTH, 30) - - icon.Crop(1, 1, ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE) - - return icon - -/// Returns the icon to show on the preferences menu. -/datum/role_preference/proc/get_preview_icon() - if (isnull(preview_outfit)) - return null - - return finish_preview_icon(render_preview_outfit(preview_outfit)) - /// Includes latejoin and roundstart antagonists /datum/role_preference/antagonist category = ROLE_PREFERENCE_CATEGORY_ANAGONIST diff --git a/code/modules/antagonists/role_preference/role_changeling.dm b/code/modules/antagonists/role_preference/role_changeling.dm new file mode 100644 index 0000000000000..7ea7c463d6d83 --- /dev/null +++ b/code/modules/antagonists/role_preference/role_changeling.dm @@ -0,0 +1,7 @@ +/datum/role_preference/antagonist/changeling + name = "Changeling" + description = "A highly intelligent alien predator that is capable of altering their \ + shape to flawlessly resemble a human. Transform yourself or others into different identities, and buy from an \ + arsenal of biological weaponry with the DNA you collect." + role_key = ROLE_CHANGELING + gamemode = /datum/game_mode/changeling diff --git a/code/modules/antagonists/role_preference/role_operative.dm b/code/modules/antagonists/role_preference/role_operative.dm index b2ed0ee153e8e..a2646a4a0a1f3 100644 --- a/code/modules/antagonists/role_preference/role_operative.dm +++ b/code/modules/antagonists/role_preference/role_operative.dm @@ -11,31 +11,9 @@ role_key = ROLE_OPERATIVE gamemode = /datum/game_mode/nuclear -/datum/role_preference/antagonist/nuclear_operative/get_preview_icon() - return get_nuclear_operative_icon() - /datum/role_preference/midround_ghost/nuclear_operative name = "Nuclear Operative (Assailant)" description = OPERATIVE_DESC role_key = ROLE_OPERATIVE -/datum/role_preference/midround_ghost/nuclear_operative/get_preview_icon() - return get_nuclear_operative_icon() - #undef OPERATIVE_DESC - -/proc/get_nuclear_operative_icon() - var/icon/final_icon = render_preview_outfit(/datum/outfit/nuclear_operative) - var/icon/teammate = render_preview_outfit(/datum/outfit/nuclear_operative) - teammate.Blend(rgb(128, 128, 128, 128), ICON_MULTIPLY) - - final_icon.Blend(teammate, ICON_OVERLAY, -world.icon_size / 4, 0) - final_icon.Blend(teammate, ICON_OVERLAY, world.icon_size / 4, 0) - - return finish_preview_icon(final_icon) - -/datum/outfit/nuclear_operative - name = "Nuclear Operative (Preview only)" - - suit = /obj/item/clothing/suit/space/hardsuit/syndi - head = /obj/item/clothing/head/helmet/space/hardsuit/syndi diff --git a/code/modules/antagonists/role_preference/role_traitor.dm b/code/modules/antagonists/role_preference/role_traitor.dm index c07ba621579d5..a1a67e8766711 100644 --- a/code/modules/antagonists/role_preference/role_traitor.dm +++ b/code/modules/antagonists/role_preference/role_traitor.dm @@ -8,29 +8,10 @@ description = TRAITOR_DESC role_key = ROLE_TRAITOR gamemode = /datum/game_mode/traitor - preview_outfit = /datum/outfit/traitor /datum/role_preference/midround_living/traitor name = "Traitor (Sleeper Agent)" description = TRAITOR_DESC role_key = ROLE_TRAITOR - preview_outfit = /datum/outfit/traitor #undef TRAITOR_DESC - -/datum/outfit/traitor - name = "Traitor (Preview only)" - - uniform = /obj/item/clothing/under/syndicate - gloves = /obj/item/clothing/gloves/combat - mask = /obj/item/clothing/mask/gas - l_hand = /obj/item/melee/transforming/energy/sword - r_hand = /obj/item/gun/energy/kinetic_accelerator/crossbow - -/datum/outfit/traitor/post_equip(mob/living/carbon/human/H, visualsOnly) - var/obj/item/melee/transforming/energy/sword/sword = locate() in H.held_items - sword.icon_state = "swordred" - H.update_inv_hands() - H.hair_style = "Messy" - H.hair_color = "111" - H.update_hair() diff --git a/code/modules/antagonists/role_preference/role_wizard.dm b/code/modules/antagonists/role_preference/role_wizard.dm new file mode 100644 index 0000000000000..cb48535f95f7d --- /dev/null +++ b/code/modules/antagonists/role_preference/role_wizard.dm @@ -0,0 +1,6 @@ +/datum/role_preference/antagonist/wizard + name = "Wizard" + description = "GREETINGS. WE'RE THE WIZARDS OF THE WIZARD'S FEDERATION. \ + Choose between a variety of powerful spells in order to cause chaos among Space Station 13." + role_key = ROLE_WIZARD + gamemode = /datum/game_mode/wizard From f4513837224194440c060c851d7cd19a1daf6a6b Mon Sep 17 00:00:00 2001 From: itsmeow Date: Tue, 13 Jun 2023 21:19:47 -0400 Subject: [PATCH 003/269] Today is the day I nuked antag prefs --- code/__DEFINES/antagonists.dm | 5 + code/__DEFINES/preferences.dm | 4 +- code/__DEFINES/role_preferences.dm | 283 ++++++++++++------ code/__HELPERS/game.dm | 28 +- code/_globalvars/lists/poll_ignore.dm | 1 + code/controllers/subsystem/pai.dm | 2 +- code/datums/brain_damage/imaginary_friend.dm | 2 +- code/datums/brain_damage/mrat.dm | 2 +- code/datums/brain_damage/split_personality.dm | 2 +- code/datums/diseases/transformation.dm | 7 +- code/datums/mind.dm | 12 +- code/game/gamemodes/brother/traitor_bro.dm | 4 +- code/game/gamemodes/changeling/changeling.dm | 22 +- .../game/gamemodes/changeling/traitor_chan.dm | 23 +- code/game/gamemodes/clock_cult/clockcult.dm | 9 +- code/game/gamemodes/cult/cult.dm | 7 +- code/game/gamemodes/devil/devil_game_mode.dm | 5 +- .../gamemodes/dynamic/dynamic_rulesets.dm | 26 +- .../dynamic/dynamic_rulesets_latejoin.dm | 22 +- .../dynamic/dynamic_rulesets_midround.dm | 57 ++-- .../dynamic/dynamic_rulesets_roundstart.dm | 60 ++-- .../gamemodes/eldritch_cult/eldritch_cult.dm | 7 +- code/game/gamemodes/game_mode.dm | 47 +-- code/game/gamemodes/gangs/gangs.dm | 3 +- code/game/gamemodes/hivemind/hivemind.dm | 7 +- code/game/gamemodes/incursion/incursion.dm | 10 +- code/game/gamemodes/nuclear/nuclear.dm | 7 +- code/game/gamemodes/overthrow/overthrow.dm | 7 +- code/game/gamemodes/revolution/revolution.dm | 5 +- code/game/gamemodes/traitor/double_agents.dm | 3 +- code/game/gamemodes/traitor/traitor.dm | 18 +- code/game/gamemodes/wizard/wizard.dm | 9 +- code/game/machinery/cloning.dm | 2 +- code/game/machinery/ecto_sniffer.dm | 2 +- .../machinery/porta_turret/portable_turret.dm | 4 +- code/game/objects/effects/anomalies.dm | 2 +- code/game/objects/effects/spiders.dm | 4 +- code/game/objects/items/holy_weapons.dm | 2 +- .../structures/fugitive_role_spawners.dm | 5 +- .../objects/structures/ghost_role_spawners.dm | 121 +------- code/game/objects/structures/spawner.dm | 2 +- code/modules/admin/antag_panel.dm | 6 +- code/modules/admin/fun_balloon.dm | 2 +- code/modules/admin/secrets.dm | 4 +- code/modules/admin/sql_ban_system.dm | 19 +- code/modules/admin/verbs/one_click_antag.dm | 19 +- code/modules/admin/verbs/randomverbs.dm | 10 +- .../antagonists/_common/antag_datum.dm | 16 +- .../antagonists/_common/antag_spawner.dm | 8 +- code/modules/antagonists/abductor/abductor.dm | 2 +- .../antagonists/ashwalker/ashwalker.dm | 2 +- code/modules/antagonists/blob/blob.dm | 2 +- code/modules/antagonists/blob/blob_mobs.dm | 5 +- .../blob/blobstrains/explosive_lattice.dm | 2 +- code/modules/antagonists/blob/overmind.dm | 4 +- code/modules/antagonists/blob/powers.dm | 8 +- .../antagonists/blob/structures/_blob.dm | 2 +- .../antagonists/brainwashing/brainwashing.dm | 2 +- code/modules/antagonists/brother/brother.dm | 9 +- .../antagonists/changeling/changeling.dm | 8 +- .../antagonists/changeling/powers/teratoma.dm | 2 +- .../antagonists/changeling/teratoma.dm | 3 +- .../antagonists/clock_cult/mobs/cogscarab.dm | 2 +- .../scriptures/sigil_of_vitality.dm | 2 +- .../clock_cult/scriptures/summon_marauder.dm | 2 +- .../clock_cult/servant_of_ratvar.dm | 2 +- .../clock_cult/structure/eminence_beacon.dm | 2 +- code/modules/antagonists/creep/creep.dm | 2 +- code/modules/antagonists/cult/cult.dm | 2 +- code/modules/antagonists/cult/runes.dm | 6 +- code/modules/antagonists/devil/devil.dm | 2 +- code/modules/antagonists/fugitive/fugitive.dm | 2 +- code/modules/antagonists/fugitive/hunter.dm | 2 +- code/modules/antagonists/gang/gang.dm | 2 +- .../antagonists/heretic/heretic_antag.dm | 2 +- .../antagonists/heretic/heretic_knowledge.dm | 2 +- .../antagonists/heretic/heretic_monsters.dm | 2 +- .../heretic/knowledge/flesh_lore.dm | 2 +- code/modules/antagonists/hivemind/hivemind.dm | 5 +- code/modules/antagonists/hivemind/vessel.dm | 5 +- .../antagonists/hypnotization/hypnotized.dm | 2 +- .../antagonists/incursion/incursion.dm | 9 +- code/modules/antagonists/morph/morph.dm | 8 +- code/modules/antagonists/morph/morph_antag.dm | 6 +- .../antagonists/nightmare/nightmare.dm | 6 +- code/modules/antagonists/ninja/ninja.dm | 6 +- code/modules/antagonists/nukeop/nukeop.dm | 4 +- .../antagonists/overthrow/overthrow.dm | 4 +- code/modules/antagonists/pirate/pirate.dm | 2 +- code/modules/antagonists/revenant/revenant.dm | 6 +- .../revenant/revenant_spawn_event.dm | 2 +- .../antagonists/revolution/revolution.dm | 7 +- .../role_preference/_role_preference.dm | 4 - .../role_preference/role_changeling.dm | 1 - .../role_preference/role_operative.dm | 1 - .../role_preference/role_traitor.dm | 2 - .../role_preference/role_wizard.dm | 1 - .../roundstart_special/special_antagonist.dm | 9 +- .../antagonists/slaughter/slaughter_antag.dm | 2 +- .../antagonists/slaughter/slaughterevent.dm | 2 +- .../antagonists/space_dragon/carp_rift.dm | 2 +- .../antagonists/space_dragon/space_dragon.dm | 2 +- code/modules/antagonists/spider/spider.dm | 2 +- code/modules/antagonists/swarmer/swarmer.dm | 10 +- .../antagonists/traitor/datum_traitor.dm | 4 +- .../traitor/equipment/contractor.dm | 2 +- .../antagonists/traitor/traitor_spawner.dm | 6 +- .../antagonists/wizard/equipment/soulstone.dm | 2 +- code/modules/antagonists/wizard/wizard.dm | 6 +- code/modules/antagonists/xeno/xeno.dm | 2 +- code/modules/awaymissions/corpse.dm | 102 +------ .../awaymissions/mission_code/Academy.dm | 7 +- .../awaymissions/mission_code/snowdin.dm | 2 +- code/modules/client/client_procs.dm | 14 - code/modules/client/preferences.dm | 4 - code/modules/client/preferences_toggles.dm | 9 - code/modules/clothing/outfits/vr.dm | 2 +- code/modules/events/abductor.dm | 2 +- code/modules/events/alien_infestation.dm | 2 +- code/modules/events/blob.dm | 2 +- code/modules/events/creep_awakening.dm | 2 +- code/modules/events/devil.dm | 6 +- code/modules/events/fugitive_spawning.dm | 12 +- code/modules/events/ghost_role.dm | 4 +- code/modules/events/nightmare.dm | 6 +- code/modules/events/operative.dm | 2 +- code/modules/events/pirates.dm | 2 +- code/modules/events/sentience.dm | 2 +- code/modules/events/space_dragon.dm | 2 +- code/modules/events/special_antag_event.dm | 15 +- code/modules/events/spider_infestation.dm | 4 +- code/modules/events/wizard/imposter.dm | 2 +- .../food_and_drinks/food/snacks_meat.dm | 2 +- code/modules/guardian/guardian.dm | 4 +- code/modules/guardian/guardianbuilder.dm | 2 +- code/modules/guardian/standarrow.dm | 2 +- code/modules/jobs/jobs.dm | 4 +- code/modules/mob/living/brain/posibrain.dm | 2 +- code/modules/mob/living/carbon/alien/alien.dm | 2 +- .../modules/mob/living/carbon/alien/organs.dm | 4 +- .../carbon/alien/special/alien_embryo.dm | 2 +- code/modules/mob/living/living_defines.dm | 1 - code/modules/mob/living/living_sentience.dm | 13 +- .../modules/mob/living/silicon/robot/robot.dm | 2 +- .../living/simple_animal/bot/SuperBeepsky.dm | 2 +- .../friendly/drone/drones_as_items.dm | 2 +- .../friendly/drone/extra_drone_types.dm | 2 +- .../mob/living/simple_animal/hostile/alien.dm | 2 +- .../mob/living/simple_animal/hostile/carp.dm | 2 +- .../simple_animal/hostile/giant_spider.dm | 1 - .../hostile/mining_mobs/elites/elite.dm | 2 +- .../living/simple_animal/hostile/syndicate.dm | 4 +- .../living/simple_animal/hostile/wizard.dm | 2 +- .../mob/living/simple_animal/slime/life.dm | 1 - .../mob/living/simple_animal/slime/slime.dm | 2 +- code/modules/mob/mob_helpers.dm | 6 +- code/modules/ninja/ninja_event.dm | 6 +- code/modules/projectiles/projectile/magic.dm | 6 +- code/modules/religion/rites.dm | 6 +- .../xenobiology/crossbreeding/warping.dm | 2 +- .../research/xenobiology/xenobiology.dm | 4 +- code/modules/ruins/lavaland_ruin_code.dm | 3 +- .../ruins/spaceruin_code/hilbertshotel.dm | 1 + .../objective_types/assassination.dm | 3 +- .../objective_types/vip_extraction.dm | 2 +- code/modules/shuttle/syndicate.dm | 2 +- code/modules/unit_tests/_unit_tests.dm | 2 + code/modules/unit_tests/antag_datums.dm | 13 + .../unit_tests/dynamic_ruleset_sanity.dm | 11 + code/modules/unit_tests/gamemode_sanity.dm | 18 ++ .../traits/xenoartifact_minors.dm | 17 +- 171 files changed, 744 insertions(+), 778 deletions(-) create mode 100644 code/modules/unit_tests/antag_datums.dm create mode 100644 code/modules/unit_tests/gamemode_sanity.dm diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index 10062b2c95652..87bbbcdce5d94 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -81,6 +81,11 @@ /// Define for the heretic faction applied to heretics and heretic mobs. #define FACTION_HERETIC "heretics" +#define FACTION_SYNDICATE "Syndicate" +#define FACTION_BLOB "Blob" +#define FACTION_ALIEN "Xenomorph" +#define FACTION_WIZARD "Wizard" + // Heretic path defines. #define HERETIC_PATH_START "Heretic Start Path" #define HERETIC_PATH_SIDE "Heretic Side Path" diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm index 3ca6ef3e9f2f1..24b610852b3cc 100644 --- a/code/__DEFINES/preferences.dm +++ b/code/__DEFINES/preferences.dm @@ -6,7 +6,7 @@ #define PREFTOGGLE_SOUND_LOBBY (1<<3) #define PREFTOGGLE_MEMBER_PUBLIC (1<<4) #define PREFTOGGLE_INTENT_STYLE (1<<5) -#define PREFTOGGLE_MIDROUND_ANTAG (1<<6) +//#define PREFTOGGLE_MIDROUND_ANTAG (1<<6) #define PREFTOGGLE_SOUND_INSTRUMENTS (1<<7) #define PREFTOGGLE_SOUND_SHIP_AMBIENCE (1<<8) #define PREFTOGGLE_SOUND_PRAYERS (1<<9) @@ -27,7 +27,7 @@ #define PREFTOGGLE_RUNECHAT_NONMOBS (1<<22) #define PREFTOGGLE_RUNECHAT_EMOTES (1<<23) -#define TOGGLES_DEFAULT (PREFTOGGLE_SOUND_ADMINHELP|PREFTOGGLE_SOUND_MIDI|PREFTOGGLE_SOUND_AMBIENCE|PREFTOGGLE_SOUND_LOBBY|PREFTOGGLE_MEMBER_PUBLIC|PREFTOGGLE_INTENT_STYLE|PREFTOGGLE_MIDROUND_ANTAG|PREFTOGGLE_SOUND_INSTRUMENTS|PREFTOGGLE_SOUND_SHIP_AMBIENCE|PREFTOGGLE_SOUND_PRAYERS|PREFTOGGLE_SOUND_ANNOUNCEMENTS|PREFTOGGLE_OUTLINE_ENABLED|PREFTOGGLE_RUNECHAT_GLOBAL|PREFTOGGLE_RUNECHAT_NONMOBS|PREFTOGGLE_RUNECHAT_EMOTES) +#define TOGGLES_DEFAULT (PREFTOGGLE_SOUND_ADMINHELP|PREFTOGGLE_SOUND_MIDI|PREFTOGGLE_SOUND_AMBIENCE|PREFTOGGLE_SOUND_LOBBY|PREFTOGGLE_MEMBER_PUBLIC|PREFTOGGLE_INTENT_STYLE|PREFTOGGLE_SOUND_INSTRUMENTS|PREFTOGGLE_SOUND_SHIP_AMBIENCE|PREFTOGGLE_SOUND_PRAYERS|PREFTOGGLE_SOUND_ANNOUNCEMENTS|PREFTOGGLE_OUTLINE_ENABLED|PREFTOGGLE_RUNECHAT_GLOBAL|PREFTOGGLE_RUNECHAT_NONMOBS|PREFTOGGLE_RUNECHAT_EMOTES) // You CANNOT go above 1<<23 in BYOND due to integer limits // Please add subsequent ones as PREFTOGGLE_2_[name] diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index d2073bdd7a0b2..1090f05f25492 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -10,103 +10,192 @@ #define MINUTES_REQUIRED_COMMAND 1200 //For command positions, to be weighed against the relevant department -// Banning snowflake - global antag ban. Badly named. -#define ROLE_SYNDICATE "Syndicate" +// Banning snowflake - global antag ban. Does not include ghost roles that aren't antagonists +#define BAN_ROLE_ALL_ANTAGONISTS "All Antagonists" +#define BAN_ROLE_ALL_ANTAGONISTS_AND_FORCED "All Antagonists and Forced Antagonists" //These are synced with the Database, if you change the values of the defines //then you MUST update the database! -#define ROLE_TRAITOR "Traitor" - -#define ROLE_OPERATIVE "Operative" -#define ROLE_CHANGELING "Changeling" -#define ROLE_WIZARD "Wizard" -#define ROLE_MALF "Malf AI" -#define ROLE_INCURSION "Incursion Team" -#define ROLE_EXCOMM "Excommunicated Syndicate Agent" -#define ROLE_REV "Revolutionary" -#define ROLE_REV_HEAD "Head Revolutionary" -#define ROLE_REV_SUCCESSFUL "Victorious Revolutionary" -#define ROLE_ALIEN "Xenomorph" -#define ROLE_PAI "pAI" -#define ROLE_CULTIST "Cultist" -#define ROLE_SERVANT_OF_RATVAR "Servant of Ratvar" -#define ROLE_HERETIC "Heretic" -#define ROLE_BLOB "Blob" -#define ROLE_NINJA "Space Ninja" -#define ROLE_ABDUCTOR "Abductor" -#define ROLE_REVENANT "Revenant" -#define ROLE_DEVIL "Devil" -#define ROLE_BROTHER "Blood Brother" -#define ROLE_BRAINWASHED "Brainwashed Victim" -#define ROLE_HYPNOTIZED "Hypnotized Victim" -#define ROLE_OVERTHROW "Syndicate Mutineer" -#define ROLE_HIVE "Hivemind Host" -#define ROLE_HIVE_VESSEL "Awakened Vessel" -#define ROLE_OBSESSED "Obsessed" -#define ROLE_SPACE_DRAGON "Space Dragon" -#define ROLE_SENTIENCE "Sentience Potion Spawn" -#define ROLE_MIND_TRANSFER "Mind Transfer Potion" -#define ROLE_POSIBRAIN "Posibrain" -#define ROLE_DRONE "Drone" -#define ROLE_DEATHSQUAD "Deathsquad" -#define ROLE_LAVALAND "Lavaland" -#define ROLE_INTERNAL_AFFAIRS "Internal Affairs Agent" -#define ROLE_GANG "Gangster" -#define ROLE_HOLOPARASITE "Holoparasite" -#define ROLE_TERATOMA "Teratoma" -#define ROLE_EXPERIMENTAL_CLONE "Experimental Clone" - - - -#define ROLE_SPIDER "Spider" -#define ROLE_SWARMER "Swarmer" -#define ROLE_MORPH "Morph" -#define ROLE_NIGHTMARE "Nightmare" -#define ROLE_SPACE_PIRATE "Space Pirate" -#define ROLE_FUGITIVE "Fugitive" -#define ROLE_FUGITIVE_HUNTER "Fugitive Hunter" - -//Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR. -//The gamemode specific ones are just so the gamemodes can query whether a player is old enough -//(in game days played) to play that role -GLOBAL_LIST_INIT(special_roles, list( - ROLE_TRAITOR = /datum/game_mode/traitor, - ROLE_BROTHER = /datum/game_mode/traitor/bros, - ROLE_INCURSION = /datum/game_mode/incursion, - ROLE_EXCOMM = /datum/game_mode/incursion, - ROLE_OPERATIVE = /datum/game_mode/nuclear, - ROLE_CHANGELING = /datum/game_mode/changeling, - ROLE_WIZARD = /datum/game_mode/wizard, - ROLE_MALF, - ROLE_REV = /datum/game_mode/revolution, - ROLE_ALIEN, - ROLE_SPIDER, - ROLE_PAI, - ROLE_CULTIST = /datum/game_mode/cult, - ROLE_SERVANT_OF_RATVAR = /datum/game_mode/clockcult, - ROLE_BLOB, - ROLE_NINJA, - ROLE_OBSESSED, - ROLE_SPACE_DRAGON, - ROLE_REVENANT, - ROLE_ABDUCTOR, - ROLE_DEVIL = /datum/game_mode/devil, - ROLE_OVERTHROW = /datum/game_mode/overthrow, - ROLE_HIVE = /datum/game_mode/hivemind, - ROLE_INTERNAL_AFFAIRS = /datum/game_mode/traitor/internal_affairs, - ROLE_SENTIENCE, - ROLE_GANG = /datum/game_mode/gang, - ROLE_HOLOPARASITE, - ROLE_HERETIC = /datum/game_mode/heretics, - ROLE_TERATOMA, - ROLE_MORPH, - ROLE_NIGHTMARE, - ROLE_SWARMER, - ROLE_SPACE_PIRATE, - ROLE_FUGITIVE, - ROLE_FUGITIVE_HUNTER, +#define BAN_ROLE_TRAITOR "Traitor" +#define BAN_ROLE_OPERATIVE "Nuclear Operative" +#define BAN_ROLE_CHANGELING "Changeling" +#define BAN_ROLE_WIZARD "Wizard" +//#define BAN_ROLE_MALF "Malf AI" // Currently under traitor datum, so we can't have this separate. +#define BAN_ROLE_INCURSION "Incursion Team" +#define BAN_ROLE_EXCOMM "Excommunicated Syndicate Agent" +#define BAN_ROLE_REV "Revolutionary" +#define BAN_ROLE_REV_HEAD "Head Revolutionary" +#define BAN_ROLE_ALIEN "Xenomorph" +#define BAN_ROLE_CULTIST "Cultist" +#define BAN_ROLE_SERVANT_OF_RATVAR "Servant of Ratvar" +#define BAN_ROLE_HERETIC "Heretic" +#define BAN_ROLE_BLOB "Blob" +#define BAN_ROLE_NINJA "Space Ninja" +#define BAN_ROLE_ABDUCTOR "Abductor" +#define BAN_ROLE_REVENANT "Revenant" +#define BAN_ROLE_DEVIL "Devil" +#define BAN_ROLE_BROTHER "Blood Brother" +#define BAN_ROLE_OVERTHROW "Syndicate Mutineer" +#define BAN_ROLE_HIVE "Hivemind Host" +#define BAN_ROLE_OBSESSED "Obsessed" +#define BAN_ROLE_SPACE_DRAGON "Space Dragon" +#define BAN_ROLE_INTERNAL_AFFAIRS "Internal Affairs Agent" +#define BAN_ROLE_GANG "Gangster" +#define BAN_ROLE_HOLOPARASITE "Holoparasite" +#define BAN_ROLE_TERATOMA "Teratoma" +#define BAN_ROLE_SPIDER "Spider" +#define BAN_ROLE_SWARMER "Swarmer" +#define BAN_ROLE_MORPH "Morph" +#define BAN_ROLE_NIGHTMARE "Nightmare" +#define BAN_ROLE_SPACE_PIRATE "Space Pirate" +#define BAN_ROLE_FUGITIVE "Fugitive" +#define BAN_ROLE_FUGITIVE_HUNTER "Fugitive Hunter" +#define BAN_ROLE_SLAUGHTER_DEMON "Slaughter Demon" + +/// Roles that are antagonists, roundstart or not, and have passes to do.. antagonistry +GLOBAL_LIST_INIT(antagonist_bannable_roles, list( + BAN_ROLE_TRAITOR, + BAN_ROLE_OPERATIVE, + BAN_ROLE_CHANGELING, + BAN_ROLE_WIZARD, +// BAN_ROLE_MALF, + BAN_ROLE_INCURSION, + BAN_ROLE_EXCOMM, + BAN_ROLE_REV, + BAN_ROLE_REV_HEAD, + BAN_ROLE_ALIEN, + BAN_ROLE_CULTIST, + BAN_ROLE_SERVANT_OF_RATVAR, + BAN_ROLE_HERETIC, + BAN_ROLE_BLOB, + BAN_ROLE_NINJA, + BAN_ROLE_ABDUCTOR, + BAN_ROLE_REVENANT, + BAN_ROLE_DEVIL, + BAN_ROLE_BROTHER, + BAN_ROLE_OVERTHROW, + BAN_ROLE_HIVE, + BAN_ROLE_OBSESSED, + BAN_ROLE_SPACE_DRAGON, + BAN_ROLE_INTERNAL_AFFAIRS, + BAN_ROLE_GANG, + BAN_ROLE_HOLOPARASITE, + BAN_ROLE_TERATOMA, + BAN_ROLE_SPIDER, + BAN_ROLE_SWARMER, + BAN_ROLE_MORPH, + BAN_ROLE_NIGHTMARE, + BAN_ROLE_SPACE_PIRATE, + BAN_ROLE_FUGITIVE, + BAN_ROLE_FUGITIVE_HUNTER, + BAN_ROLE_SLAUGHTER_DEMON, +)) + +#define BAN_ROLE_BRAINWASHED "Brainwashed Victim" +#define BAN_ROLE_HYPNOTIZED "Hypnotized Victim" +#define BAN_ROLE_HIVE_VESSEL "Awakened Vessel" + +/// Forced antagonist roles +GLOBAL_LIST_INIT(forced_bannable_roles, list( + BAN_ROLE_BRAINWASHED, + BAN_ROLE_HYPNOTIZED, + BAN_ROLE_HIVE_VESSEL, +)) + +#define BAN_ROLE_ALL_GHOST "Non-Antagonist Ghost Roles" + +#define BAN_ROLE_PAI "pAI" +#define BAN_ROLE_POSIBRAIN "Posibrain" +#define BAN_ROLE_DRONE "Drone" +#define BAN_ROLE_SENTIENCE "Sentience Potion Spawn" +#define BAN_ROLE_EXPERIMENTAL_CLONE "Experimental Clone" +#define BAN_ROLE_LAVALAND_ELITE "Lavaland Elite" +#define BAN_ROLE_SPECTRAL_BLADE "Spectral Blade" +#define BAN_ROLE_ASHKWALKER "Ashwalker" + +/// Any ghost role that is not really an antagonist or doesn't antagonize (lavaland, sentience potion, etc) +GLOBAL_LIST_INIT(ghost_role_bannable_roles, list( + BAN_ROLE_PAI, + BAN_ROLE_POSIBRAIN, + BAN_ROLE_DRONE, + BAN_ROLE_SENTIENCE, + BAN_ROLE_EXPERIMENTAL_CLONE, + BAN_ROLE_LAVALAND_ELITE, + BAN_ROLE_SPECTRAL_BLADE, + BAN_ROLE_ASHKWALKER, )) +#define BAN_ROLE_IMAGINARY_FRIEND "Imaginary Friend" +#define BAN_ROLE_SPLIT_PERSONALITY "Split Personality" +#define BAN_ROLE_MIND_TRANSFER "Mind Transfer Potion" + +/// Other roles that don't really fit any of the above, and probably shouldn't be banned with the others as a group +/// Little to no impact on anything +GLOBAL_LIST_INIT(other_bannable_roles, list( + BAN_ROLE_IMAGINARY_FRIEND, + BAN_ROLE_SPLIT_PERSONALITY, + BAN_ROLE_MIND_TRANSFER, +)) + +/proc/role_preference_enabled(client/player, role_preference_key) + if(!ispath(role_preference_key, /datum/role_preference)) + CRASH("Invalid role_preference_key [role_preference_key] passed to role_preference_enabled!") + if(!istype(player) || !player.prefs) + return FALSE + var/role_preference_value = player.prefs.be_special[role_preference_key] + if(isnum(role_preference_value) && role_preference_value == 0) // explicitly disabled and not null + return FALSE + return TRUE + +/// If the client given is fit for a given role based on the arguments passed +/// banning_key: BAN_ROLE_X used for this role - to check if the player is banned. +/// role_preference_key: The /datum/role_preference typepath to check if the player has the role enabled and would like to receive the poll. +/// poll_ignore_key: The POLL_IGNORE_X define for this role, used for temporarily disabling ghost polls for high volume roles. +/// gamemode_for_age: The gamemode that this role is typically belonging to, since gamemodes store experience requirements, it checks the player's account age. +/// req_hours: The amount of living hours required to receive this role. +/// feedback: if we should send a to_chat +/proc/should_include_for_role(client/player, banning_key = BAN_ROLE_ALL_ANTAGONISTS, role_preference_key = null, poll_ignore_key = null, datum/game_mode/gamemode_for_age = null, req_hours = 0, feedback = FALSE) + if(QDELETED(player) || (poll_ignore_key && GLOB.poll_ignore[poll_ignore_key] && (player.ckey in GLOB.poll_ignore[poll_ignore_key]))) + return FALSE + if(role_preference_key) + if(!ispath(role_preference_key, /datum/role_preference)) + CRASH("Invalid role_preference_key [role_preference_key] passed to should_include_for_role!") + if(!role_preference_enabled(player, role_preference_key)) + return FALSE + if(banning_key) + if(is_banned_from(player.ckey, banning_key)) + if(feedback) + to_chat(player, "You are banned from this role!") + return FALSE + if(gamemode_for_age) + if(!gamemode_for_age.age_check(player)) + if(feedback) + to_chat(player, "Your account is not old enough to take this role!") + return FALSE + if(req_hours) //minimum living hour count + if((player.get_exp_living(TRUE)/60) < req_hours) + if(feedback) + to_chat(player, "You do not have enough living hours to take this role ([req_hours]hrs required)!") + return FALSE + return TRUE + +/proc/can_take_ghost_spawner(client/player, banning_key = BAN_ROLE_ALL_ANTAGONISTS, use_cooldown = TRUE, is_admin_spawned = FALSE) + if(!SSticker.HasRoundStarted() || !istype(player)) + return FALSE + if(!(GLOB.ghost_role_flags & GHOSTROLE_SPAWNER) && !is_admin_spawned) + to_chat(src, "An admin has temporarily disabled non-admin ghost roles!") + return FALSE + if(!should_include_for_role( + player, + banning_key = banning_key, + feedback = TRUE + )) + return FALSE + if(use_cooldown && player.next_ghost_role_tick > world.time) + to_chat(src, "You have died recently, you must wait [(player.next_ghost_role_tick - world.time)/10] seconds until you can use a ghost spawner.") + return FALSE + return TRUE + //Job defines for what happens when you fail to qualify for any job during job selection #define BEOVERFLOW 1 #define BERANDOMJOB 2 @@ -115,3 +204,13 @@ GLOBAL_LIST_INIT(special_roles, list( #define ROLE_PREFERENCE_CATEGORY_ANAGONIST "Antagonists" #define ROLE_PREFERENCE_CATEGORY_MIDROUND_LIVING "Midrounds (Living)" #define ROLE_PREFERENCE_CATEGORY_MIDROUND_GHOST "Midrounds (Ghost Poll)" + +GLOBAL_LIST_INIT(role_preference_entries, init_role_preference_entries()) + +/proc/init_role_preference_entries() + var/list/output = list() + for (var/datum/role_preference/preference_type as anything in subtypesof(/datum/role_preference)) + if (initial(preference_type.abstract_type) == preference_type) + continue + output[preference_type] = new preference_type + return output diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index fce73f883e822..51f363f4deb80 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -438,29 +438,25 @@ return pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category, flashwindow, candidates, req_hours) -/proc/pollCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE, list/group = null, req_hours = 0) +/proc/pollCandidates(Question, banning_key, datum/game_mode/gamemode_for_age, role_preference_key = null, poll_time = 300, poll_ignore_key = null, flashwindow = TRUE, list/group = null, req_hours = 0) var/time_passed = world.time if (!Question) Question = "Would you like to be a special role?" var/list/result = list() for(var/m in group) var/mob/M = m - if(!M.key || !M.client || (ignore_category && GLOB.poll_ignore[ignore_category] && (M.ckey in GLOB.poll_ignore[ignore_category]))) + if(QDELETED(M) || !M.key || !M.client) continue - if(be_special_flag) - if(!(M.client.prefs) || !(be_special_flag in M.client.prefs.be_special)) - continue - if(gametypeCheck) - if(!gametypeCheck.age_check(M.client)) - continue - if(jobbanType) - if(QDELETED(M) || is_banned_from(M.ckey, list(jobbanType, ROLE_SYNDICATE))) - continue - if(req_hours) //minimum living hour count - if((M.client.get_exp_living(TRUE)/60) < req_hours) - continue - - showCandidatePollWindow(M, poll_time, Question, result, ignore_category, time_passed, flashwindow) + if(!should_include_for_role( + M.client, + banning_key = banning_key, + role_preference_key = role_preference_key, + poll_ignore_key = poll_ignore_key, + gamemode_for_age = gamemode_for_age, + req_hours = req_hours + )) + continue + showCandidatePollWindow(M, poll_time, Question, result, poll_ignore_key, time_passed, flashwindow) sleep(poll_time) //Check all our candidates, to make sure they didn't log off or get deleted during the wait period. diff --git a/code/_globalvars/lists/poll_ignore.dm b/code/_globalvars/lists/poll_ignore.dm index 99727c503b6e0..559a667531971 100644 --- a/code/_globalvars/lists/poll_ignore.dm +++ b/code/_globalvars/lists/poll_ignore.dm @@ -13,6 +13,7 @@ #define POLL_IGNORE_HOLYCARP "holy_carp" #define POLL_IGNORE_HOLYUNDEAD "holy_undead" #define POLL_IGNORE_IMAGINARYFRIEND "imaginary_friend" +#define POLL_IGNORE_MRAT "mrat" #define POLL_IGNORE_PAI "pai" #define POLL_IGNORE_POSIBRAIN "posibrain" #define POLL_IGNORE_POSSESSED_BLADE "possessed_blade" diff --git a/code/controllers/subsystem/pai.dm b/code/controllers/subsystem/pai.dm index 898033a89f61d..2292f94e060a9 100644 --- a/code/controllers/subsystem/pai.dm +++ b/code/controllers/subsystem/pai.dm @@ -85,7 +85,7 @@ SUBSYSTEM_DEF(pai) return /// The matching candidate from search var/datum/pai_candidate/candidate = candidates[usr.ckey] - if(is_banned_from(usr.ckey, ROLE_PAI)) + if(is_banned_from(usr.ckey, BAN_ROLE_PAI)) to_chat(usr, "You are banned from playing pAI!") ui.close() return FALSE diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index 350831fdbffc1..23d747a0c4fb7 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -46,7 +46,7 @@ if(owner.stat == DEAD || !owner.mind) qdel(src) return - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s imaginary friend?", ROLE_PAI, null, null, 75, friend, POLL_IGNORE_IMAGINARYFRIEND) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s imaginary friend?", BAN_ROLE_IMAGINARY_FRIEND, null, null, 75, friend, POLL_IGNORE_IMAGINARYFRIEND) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) friend.key = C.key diff --git a/code/datums/brain_damage/mrat.dm b/code/datums/brain_damage/mrat.dm index 7629937699cbf..102b903b28bf2 100644 --- a/code/datums/brain_damage/mrat.dm +++ b/code/datums/brain_damage/mrat.dm @@ -12,7 +12,7 @@ /datum/brain_trauma/special/imaginary_friend/mrat/get_ghost() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollMentorCandidatesForMob("Do you want to play as [owner]'s mentor rat?", ROLE_PAI, null, null, 75, friend, POLL_IGNORE_IMAGINARYFRIEND) + var/list/mob/dead/observer/candidates = pollMentorCandidatesForMob("Do you want to play as [owner]'s mentor rat?", BAN_ROLE_IMAGINARY_FRIEND, null, null, 75, friend, POLL_IGNORE_MRAT) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) friend.key = C.key diff --git a/code/datums/brain_damage/split_personality.dm b/code/datums/brain_damage/split_personality.dm index b2ebc6c189971..9eef8d0ba3af8 100644 --- a/code/datums/brain_damage/split_personality.dm +++ b/code/datums/brain_damage/split_personality.dm @@ -31,7 +31,7 @@ if(owner.stat == DEAD || !owner.mind) qdel(src) return - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s split personality?", ROLE_PAI, null, null, 75, stranger_backseat, POLL_IGNORE_SPLITPERSONALITY) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s split personality?", BAN_ROLE_SPLIT_PERSONALITY, null, null, 75, stranger_backseat, POLL_IGNORE_SPLITPERSONALITY) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) stranger_backseat.key = C.key diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm index 4abb88bf3b7f7..9e26286a56601 100644 --- a/code/datums/diseases/transformation.dm +++ b/code/datums/diseases/transformation.dm @@ -80,10 +80,11 @@ /datum/disease/transformation/proc/replace_banned_player(var/mob/living/new_mob) // This can run well after the mob has been transferred, so need a handle on the new mob to kill it if needed. set waitfor = FALSE + affected_mob.playable_bantype = bantype affected_mob.ghostize(TRUE,SENTIENCE_FORCE) to_chat(affected_mob, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!") - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [affected_mob.name]?", bantype, null, bantype, 50, affected_mob) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [affected_mob.name]?", bantype, null, null, 50, affected_mob) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(affected_mob)]) to replace a jobbanned player.") @@ -108,7 +109,7 @@ stage5 = list("Your skin feels as if it's about to burst off!") new_form = /mob/living/silicon/robot infectable_biotypes = list(MOB_ORGANIC, MOB_UNDEAD, MOB_ROBOTIC) - bantype = "Cyborg" + bantype = JOB_NAME_CYBORG /datum/disease/transformation/robot/stage_act() ..() @@ -140,7 +141,7 @@ stage4 = list("Your skin feels very tight.", "Your blood boils!", "You can feel... something...inside you.") stage5 = list("Your skin feels as if it's about to burst off!") new_form = /mob/living/carbon/alien/humanoid/hunter - bantype = ROLE_ALIEN + bantype = BAN_ROLE_ALIEN /datum/disease/transformation/xeno/stage_act() ..() diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 2706e57c3d862..f3ebf8c47b262 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -661,20 +661,20 @@ var/datum/antagonist/changeling/C = has_antag_datum(/datum/antagonist/changeling) if(!C) C = add_antag_datum(/datum/antagonist/changeling) - special_role = ROLE_CHANGELING + special_role = BAN_ROLE_CHANGELING return C /datum/mind/proc/make_Wizard() if(!has_antag_datum(/datum/antagonist/wizard)) - special_role = ROLE_WIZARD - assigned_role = ROLE_WIZARD + special_role = BAN_ROLE_WIZARD + assigned_role = BAN_ROLE_WIZARD add_antag_datum(/datum/antagonist/wizard) /datum/mind/proc/make_Cultist() if(!has_antag_datum(/datum/antagonist/cult,TRUE)) SSticker.mode.add_cultist(src,FALSE,equip=TRUE) - special_role = ROLE_CULTIST + special_role = BAN_ROLE_CULTIST to_chat(current, "You catch a glimpse of the Realm of Nar'Sie, The Geometer of Blood. You now see how flimsy your world is, you see that it should be open to the knowledge of Nar'Sie.") to_chat(current, "Assist your new brethren in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back.") @@ -683,7 +683,7 @@ head.give_flash = TRUE head.give_hud = TRUE add_antag_datum(head) - special_role = ROLE_REV_HEAD + special_role = BAN_ROLE_REV_HEAD /datum/mind/proc/AddSpell(obj/effect/proc_holder/spell/S) spell_list += S @@ -811,7 +811,7 @@ //PAI /mob/living/silicon/pai/mind_initialize() ..() - mind.assigned_role = ROLE_PAI + mind.assigned_role = BAN_ROLE_PAI mind.special_role = "" // Quirk Procs // diff --git a/code/game/gamemodes/brother/traitor_bro.dm b/code/game/gamemodes/brother/traitor_bro.dm index 6059dba2a1414..32b80e5dd6c95 100644 --- a/code/game/gamemodes/brother/traitor_bro.dm +++ b/code/game/gamemodes/brother/traitor_bro.dm @@ -27,7 +27,7 @@ if(CONFIG_GET(flag/protect_heads_from_antagonist)) restricted_jobs += GLOB.command_positions - var/list/datum/mind/possible_brothers = get_players_for_role(ROLE_BROTHER) + var/list/datum/mind/possible_brothers = get_players_for_role(BAN_ROLE_BROTHER, /datum/role_preference/antagonist/blood_brother) var/num_teams = team_amount var/bsc = CONFIG_GET(number/brother_scaling_coeff) @@ -40,7 +40,7 @@ var/datum/team/brother_team/team = new var/team_size = prob(10) ? min(3, possible_brothers.len) : 2 for(var/k = 1 to team_size) - var/datum/mind/bro = antag_pick(possible_brothers, ROLE_BROTHER) + var/datum/mind/bro = antag_pick(possible_brothers) possible_brothers -= bro antag_candidates -= bro team.add_member(bro) diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm index 7489d6458255d..c3a16eaa185ac 100644 --- a/code/game/gamemodes/changeling/changeling.dm +++ b/code/game/gamemodes/changeling/changeling.dm @@ -9,7 +9,8 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w name = "changeling" config_tag = "changeling" report_type = "changeling" - antag_flag = ROLE_CHANGELING + banning_key = BAN_ROLE_CHANGELING + role_preference = /datum/role_preference/antagonist/changeling false_report_weight = 10 restricted_jobs = list(JOB_NAME_AI, JOB_NAME_CYBORG) protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) @@ -51,10 +52,10 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w for(var/i = 0, i < num_changelings, i++) if(!antag_candidates.len) break - var/datum/mind/changeling = antag_pick(antag_candidates, ROLE_CHANGELING) + var/datum/mind/changeling = antag_pick(antag_candidates) antag_candidates -= changeling changelings += changeling - changeling.special_role = ROLE_CHANGELING + changeling.special_role = BAN_ROLE_CHANGELING changeling.restricted_roles = restricted_jobs return 1 else @@ -74,12 +75,15 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w if(changelings.len >= changelingcap) //Caps number of latejoin antagonists return if(changelings.len <= (changelingcap - 2) || prob(100 - (csc * 2))) - if(ROLE_CHANGELING in character.client.prefs.be_special) - if(!is_banned_from(character.ckey, list(ROLE_CHANGELING, ROLE_SYNDICATE)) && !QDELETED(character)) - if(age_check(character.client)) - if(!(character.job in restricted_jobs)) - character.mind.make_Changeling() - changelings += character.mind + if(!QDELETED(character) && character.client && should_include_for_role( + character.client, + banning_key = banning_key, + role_preference_key = role_preference, + gamemode_for_age = src + )) + if(!(character.job in restricted_jobs)) + character.mind.make_Changeling() + changelings += character.mind /datum/game_mode/changeling/generate_report() return "The Gorlex Marauders have announced the successful raid and destruction of Central Command containment ship #S-[rand(1111, 9999)]. This ship housed only a single prisoner - \ diff --git a/code/game/gamemodes/changeling/traitor_chan.dm b/code/game/gamemodes/changeling/traitor_chan.dm index 95057675495c6..6c769b3202bf1 100644 --- a/code/game/gamemodes/changeling/traitor_chan.dm +++ b/code/game/gamemodes/changeling/traitor_chan.dm @@ -22,7 +22,7 @@ /datum/game_mode/traitor/changeling/can_start() if(!..()) return 0 - possible_changelings = get_players_for_role(ROLE_CHANGELING) + possible_changelings = get_players_for_role(BAN_ROLE_CHANGELING, /datum/role_preference/antagonist/changeling) if(possible_changelings.len < required_enemies) return 0 return 1 @@ -37,7 +37,7 @@ if(CONFIG_GET(flag/protect_heads_from_antagonist)) restricted_jobs += GLOB.command_positions - var/list/datum/mind/possible_changelings = get_players_for_role(ROLE_CHANGELING) + var/list/datum/mind/possible_changelings = get_players_for_role(BAN_ROLE_CHANGELING, /datum/role_preference/antagonist/changeling) var/num_changelings = 1 @@ -51,10 +51,10 @@ for(var/j = 0, j < num_changelings, j++) if(!possible_changelings.len) break - var/datum/mind/changeling = antag_pick(possible_changelings, ROLE_CHANGELING) + var/datum/mind/changeling = antag_pick(possible_changelings) antag_candidates -= changeling possible_changelings -= changeling - changeling.special_role = ROLE_CHANGELING + changeling.special_role = BAN_ROLE_CHANGELING changelings += changeling changeling.restricted_roles = restricted_jobs return ..() @@ -73,12 +73,15 @@ ..() return if(changelings.len <= (changelingcap - 2) || prob(100 / (csc * 4))) - if(ROLE_CHANGELING in character.client.prefs.be_special) - if(!is_banned_from(character.ckey, list(ROLE_CHANGELING, ROLE_SYNDICATE)) && !QDELETED(character)) - if(age_check(character.client)) - if(!(character.job in restricted_jobs)) - character.mind.make_Changeling() - changelings += character.mind + if(!QDELETED(character) && character.client && should_include_for_role( + character.client, + banning_key = BAN_ROLE_CHANGELING, + role_preference_key = /datum/role_preference/antagonist/changeling, + gamemode_for_age = src + )) + if(!(character.job in restricted_jobs)) + character.mind.make_Changeling() + changelings += character.mind if(QDELETED(character)) return ..() diff --git a/code/game/gamemodes/clock_cult/clockcult.dm b/code/game/gamemodes/clock_cult/clockcult.dm index a3c1f0864e96b..f30527ad8c777 100644 --- a/code/game/gamemodes/clock_cult/clockcult.dm +++ b/code/game/gamemodes/clock_cult/clockcult.dm @@ -32,7 +32,8 @@ GLOBAL_VAR(clockcult_eminence) required_players = 24 required_enemies = 4 recommended_enemies = 4 - antag_flag = ROLE_SERVANT_OF_RATVAR + banning_key = BAN_ROLE_SERVANT_OF_RATVAR + role_preference = /datum/role_preference/antagonist/clock_cultist enemy_minimum_age = 14 title_icon = "clockcult" @@ -58,14 +59,14 @@ GLOBAL_VAR(clockcult_eminence) for(var/i in 1 to clock_cultists) if(!antag_candidates.len) break - var/datum/mind/clockie = antag_pick(antag_candidates, ROLE_SERVANT_OF_RATVAR) + var/datum/mind/clockie = antag_pick(antag_candidates) //In case antag_pick breaks if(!clockie) continue antag_candidates -= clockie selected_servants += clockie - clockie.assigned_role = ROLE_SERVANT_OF_RATVAR - clockie.special_role = ROLE_SERVANT_OF_RATVAR + clockie.assigned_role = BAN_ROLE_SERVANT_OF_RATVAR + clockie.special_role = BAN_ROLE_SERVANT_OF_RATVAR generate_clockcult_scriptures() return TRUE diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm index 7b2498f926389..e249ef351cefc 100644 --- a/code/game/gamemodes/cult/cult.dm +++ b/code/game/gamemodes/cult/cult.dm @@ -36,7 +36,8 @@ name = "cult" config_tag = "cult" report_type = "cult" - antag_flag = ROLE_CULTIST + banning_key = BAN_ROLE_CULTIST + role_preference = /datum/role_preference/antagonist/blood_cultist false_report_weight = 1 restricted_jobs = list(JOB_NAME_CHAPLAIN,JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL) protected_jobs = list() @@ -84,13 +85,13 @@ for(var/cultists_number = 1 to recommended_enemies) if(!antag_candidates.len) break - var/datum/mind/cultist = antag_pick(antag_candidates, ROLE_CULTIST) + var/datum/mind/cultist = antag_pick(antag_candidates) antag_candidates -= cultist if(!cultist) cultists_number-- continue cultists_to_cult += cultist - cultist.special_role = ROLE_CULTIST + cultist.special_role = BAN_ROLE_CULTIST cultist.restricted_roles = restricted_jobs log_game("[key_name(cultist)] has been selected as a cultist") diff --git a/code/game/gamemodes/devil/devil_game_mode.dm b/code/game/gamemodes/devil/devil_game_mode.dm index b7d0bbe776607..1f6c83c7c5bca 100644 --- a/code/game/gamemodes/devil/devil_game_mode.dm +++ b/code/game/gamemodes/devil/devil_game_mode.dm @@ -2,7 +2,8 @@ name = "devil" config_tag = "devil" report_type = "devil" - antag_flag = ROLE_DEVIL + banning_key = BAN_ROLE_DEVIL + role_preference = /datum/role_preference/antagonist/devil false_report_weight = 1 protected_jobs = list(JOB_NAME_LAWYER, JOB_NAME_CURATOR, JOB_NAME_CHAPLAIN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE) required_players = 0 @@ -42,7 +43,7 @@ for(var/j = 0, j < num_devils, j++) if (!antag_candidates.len) break - var/datum/mind/devil = antag_pick(antag_candidates, ROLE_DEVIL) + var/datum/mind/devil = antag_pick(antag_candidates) devils += devil devil.special_role = traitor_name devil.restricted_roles = restricted_jobs diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets.dm b/code/game/gamemodes/dynamic/dynamic_rulesets.dm index c439bd569d97b..e94f34e86c0f4 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets.dm @@ -18,8 +18,8 @@ var/list/mob/candidates = list() /// List of players that were selected for this rule var/list/datum/mind/assigned = list() - /// Preferences flag such as ROLE_WIZARD that need to be turned on for players to be antag - var/antag_flag = null + /// The /datum/role_preference typepath used for this ruleset. + var/role_preference = null /// The antagonist datum that is assigned to the mobs mind on ruleset execution. var/datum/antagonist/antag_datum = null /// The required minimum account age for this ruleset. @@ -282,7 +282,7 @@ /// Picks a candidate from a list, while potentially taking antag rep into consideration. /datum/dynamic_ruleset/proc/antag_pick_n_take(list/candidates) - . = (mode && consider_antag_rep) ? mode.antag_pick(candidates, antag_flag) : pick(candidates) + . = (mode && consider_antag_rep) ? mode.antag_pick(candidates, role_preference) : pick(candidates) if(.) candidates -= . @@ -298,19 +298,21 @@ var/client/client = GET_CLIENT(P) if (!client || !P.mind) // Are they connected? candidates.Remove(P) - else if(!mode.check_age(client, minimum_required_age)) + continue + + if(!should_include_for_role( + client, + banning_key = initial(antag_datum.banning_key), + role_preference_key = role_preference + )) + continue + + if(!mode.check_age(client, minimum_required_age)) candidates.Remove(P) continue if(P.mind.special_role) // We really don't want to give antag to an antag. candidates.Remove(P) - else if(antag_flag_override) - if(!(antag_flag_override in client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag_override, ROLE_SYNDICATE))) - candidates.Remove(P) - continue - else - if(!(antag_flag in client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag, ROLE_SYNDICATE))) - candidates.Remove(P) - continue + continue /// Do your checks if the ruleset is ready to be executed here. /// Should ignore certain checks if forced is TRUE diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm index 530b8cf4e03b5..a52f982c57fac 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm @@ -22,8 +22,13 @@ continue if (P.mind.assigned_role in restricted_roles) // Does their job allow for it? candidates.Remove(P) - continue - if ((exclusive_roles.len > 0) && !(P.mind.assigned_role in exclusive_roles)) // Is the rule exclusive to their job? + else if(length(exclusive_roles) && !(P.mind.assigned_role in exclusive_roles)) // Is the rule exclusive to their job? + candidates.Remove(P) + else if(!should_include_for_role( + P.client, + banning_key = initial(antag_datum.banning_key), + role_preference_key = role_preference + )) candidates.Remove(P) continue @@ -53,7 +58,7 @@ /datum/dynamic_ruleset/latejoin/execute() var/mob/M = pick(candidates) assigned += M.mind - M.mind.special_role = antag_flag + M.mind.special_role = initial(antag_datum.banning_key) M.mind.add_antag_datum(antag_datum) return TRUE @@ -66,7 +71,7 @@ /datum/dynamic_ruleset/latejoin/infiltrator name = "Syndicate Infiltrator" antag_datum = /datum/antagonist/traitor - antag_flag = ROLE_TRAITOR + role_preference = /datum/role_preference/antagonist/traitor protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL) restricted_roles = list(JOB_NAME_AI,JOB_NAME_CYBORG) required_candidates = 1 @@ -94,8 +99,7 @@ name = "Provocateur" persistent = TRUE antag_datum = /datum/antagonist/rev/head - antag_flag = ROLE_REV_HEAD - antag_flag_override = ROLE_REV + role_preference = /datum/role_preference/antagonist/revolutionary restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_CHIEFENGINEER, JOB_NAME_CHIEFMEDICALOFFICER, JOB_NAME_RESEARCHDIRECTOR) enemy_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER,JOB_NAME_DETECTIVE,JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_WARDEN) required_enemies = list(2,2,1,1,1,1,1,0,0,0) @@ -127,7 +131,7 @@ var/mob/M = pick(candidates) // This should contain a single player, but in case. if(check_eligible(M.mind)) // Didnt die/run off z-level/get implanted since leaving shuttle. assigned += M.mind - M.mind.special_role = antag_flag + M.mind.special_role = BAN_ROLE_REV_HEAD revolution = new() var/datum/antagonist/rev/head/new_head = new() new_head.give_flash = TRUE @@ -170,7 +174,7 @@ /datum/dynamic_ruleset/latejoin/heretic_smuggler name = "Heretic Smuggler" antag_datum = /datum/antagonist/heretic - antag_flag = ROLE_HERETIC + role_preference = /datum/role_preference/antagonist/heretic protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_roles = list(JOB_NAME_AI,JOB_NAME_CYBORG) required_candidates = 1 @@ -190,7 +194,7 @@ /datum/dynamic_ruleset/latejoin/heretic_smuggler/execute() var/mob/picked_mob = pick(candidates) assigned += picked_mob.mind - picked_mob.mind.special_role = antag_flag + picked_mob.mind.special_role = BAN_ROLE_HERETIC var/datum/antagonist/heretic/new_heretic = picked_mob.mind.add_antag_datum(antag_datum) // Heretics passively gain influence over time. diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index 97668b63bdfd0..c4f5d27382f60 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -54,6 +54,7 @@ if(!mode.check_age(M.client, minimum_required_age)) trimmed_list.Remove(M) continue +<<<<<<< HEAD if(antag_flag_override) if(!(antag_flag_override in M.client.prefs.be_special) || is_banned_from(M.ckey, list(antag_flag_override, ROLE_SYNDICATE))) trimmed_list.Remove(M) @@ -62,6 +63,15 @@ if(!(antag_flag in M.client.prefs.be_special) || is_banned_from(M.ckey, list(antag_flag, ROLE_SYNDICATE))) trimmed_list.Remove(M) continue +======= + if(!should_include_for_role( + M.client, + banning_key = initial(antag_datum.banning_key), + role_preference_key = role_preference, + poll_ignore_key = role_preference + )) + continue +>>>>>>> b4bb6c2e7d5 (Today is the day I nuked antag prefs) if (M.mind) if (restrict_ghost_roles && (M.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL])) // Are they playing a ghost role? trimmed_list.Remove(M) @@ -117,7 +127,11 @@ message_admins("Polling [possible_volunteers.len] players to apply for the [name] ruleset.") log_game("DYNAMIC: Polling [possible_volunteers.len] players to apply for the [name] ruleset.") +<<<<<<< HEAD candidates = pollGhostCandidates("The mode is looking for volunteers to become [antag_flag] for [name]", antag_flag, SSticker.mode, antag_flag_override ? antag_flag_override : antag_flag, poll_time = 300) +======= + candidates = pollGhostCandidates("The mode is looking for volunteers to become [initial(antag_datum.name)] for [name]", initial(antag_datum.banning_key), SSticker.mode, role_preference, poll_time = 300) +>>>>>>> b4bb6c2e7d5 (Today is the day I nuked antag prefs) if(!length(candidates)) message_admins("The ruleset [name] received no applications.") @@ -173,7 +187,7 @@ var/datum/antagonist/new_role = new antag_datum() setup_role(new_role) new_character.mind.add_antag_datum(new_role) - new_character.mind.special_role = antag_flag + new_character.mind.special_role = new_role.banning_key /datum/dynamic_ruleset/midround/from_ghosts/proc/setup_role(datum/antagonist/new_role) return @@ -188,7 +202,7 @@ name = "Syndicate Sleeper Agent" midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT antag_datum = /datum/antagonist/traitor - antag_flag = ROLE_TRAITOR + role_preference = /datum/role_preference/midround_living/traitor protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_roles = list(JOB_NAME_CYBORG, JOB_NAME_AI, "Positronic Brain") required_candidates = 1 @@ -243,7 +257,7 @@ name = "Malfunctioning AI" midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY antag_datum = /datum/antagonist/traitor - antag_flag = ROLE_MALF + role_preference = /datum/role_preference/midround_living/malfunctioning_ai enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_SCIENTIST, JOB_NAME_CHEMIST, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CHIEFENGINEER) exclusive_roles = list(JOB_NAME_AI) required_enemies = list(3,3,2,2,2,1,1,1,1,0) @@ -284,7 +298,7 @@ var/mob/living/silicon/ai/M = antag_pick_n_take(candidates) assigned += M.mind var/datum/antagonist/traitor/AI = new - M.mind.special_role = antag_flag + M.mind.special_role = "Malf AI" M.mind.add_antag_datum(AI) if(prob(ion_announce)) priority_announce("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert", ANNOUNCER_IONSTORM) @@ -304,7 +318,7 @@ name = "Wizard" midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY antag_datum = /datum/antagonist/wizard - antag_flag = ROLE_WIZARD + role_preference = /datum/role_preference/midround_ghost/wizard enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_RESEARCHDIRECTOR) //RD doesn't believe in magic required_enemies = list(2,2,1,1,1,1,1,0,0,0) required_candidates = 1 @@ -335,7 +349,7 @@ /datum/dynamic_ruleset/midround/from_ghosts/nuclear name = "Nuclear Assault" midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY - antag_flag = ROLE_OPERATIVE + role_preference = /datum/role_preference/midround_ghost/nuclear_operative antag_datum = /datum/antagonist/nukeop enemy_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(3,3,2,2,2,2,1,1,0,0) @@ -361,8 +375,8 @@ return ..() /datum/dynamic_ruleset/midround/from_ghosts/nuclear/finish_setup(mob/new_character, index) - new_character.mind.special_role = "Nuclear Operative" - new_character.mind.assigned_role = "Nuclear Operative" + new_character.mind.special_role = BAN_ROLE_OPERATIVE + new_character.mind.assigned_role = BAN_ROLE_OPERATIVE if (index == 1) // Our first guy is the leader var/datum/antagonist/nukeop/leader/new_role = new nuke_team = new_role.nuke_team @@ -380,7 +394,7 @@ name = "Blob" midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY antag_datum = /datum/antagonist/blob - antag_flag = ROLE_BLOB + role_preference = /datum/role_preference/midround_ghost/blob enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(2,2,1,1,1,1,1,0,0,0) required_candidates = 1 @@ -404,7 +418,7 @@ name = "Alien Infestation" midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY antag_datum = /datum/antagonist/xeno - antag_flag = ROLE_ALIEN + role_preference = /datum/role_preference/midround_ghost/xenomorph enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(2,2,2,1,1,1,1,0,0,0) required_candidates = 1 @@ -452,7 +466,7 @@ name = "Nightmare" midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT antag_datum = /datum/antagonist/nightmare - antag_flag = ROLE_NIGHTMARE + role_preference = /datum/role_preference/midround_ghost/nightmare enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(1,1,1,1,0,0,0,0,0,0) required_candidates = 1 @@ -496,7 +510,7 @@ name = "Space Dragon" midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY antag_datum = /datum/antagonist/space_dragon - antag_flag = ROLE_SPACE_DRAGON + role_preference = /datum/role_preference/midround_ghost/space_dragon enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(1,1,1,1,0,0,0,0,0,0) required_candidates = 1 @@ -538,7 +552,7 @@ /datum/dynamic_ruleset/midround/from_ghosts/abductors name = "Abductors" midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT - antag_flag = ROLE_ABDUCTOR + role_preference = /datum/role_preference/midround_ghost/abductor enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(2,2,1,1,1,1,0,0,0,0) required_candidates = 2 @@ -576,7 +590,7 @@ name = "Revenant" midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT antag_datum = /datum/antagonist/revenant - antag_flag = ROLE_REVENANT + role_preference = /datum/role_preference/midround_ghost/revenant enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(1,1,1,1,0,0,0,0,0,0) required_candidates = 1 @@ -627,7 +641,7 @@ /datum/dynamic_ruleset/midround/pirates name = "Space Pirates" midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY - antag_flag = ROLE_SPACE_PIRATE + role_preference = /datum/role_preference/midround_ghost/space_pirate required_type = /mob/dead/observer enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(2,2,2,1,1,1,1,0,0,0) @@ -654,7 +668,7 @@ name = "Obsessed" midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT antag_datum = /datum/antagonist/obsessed - antag_flag = ROLE_OBSESSED + role_preference = /datum/role_preference/midround_living/obsessed restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, "Positronic Brain") enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(1,1,1,1,0,0,0,0,0,0) @@ -672,7 +686,6 @@ !candidate.getorgan(/obj/item/organ/brain) \ || candidate.mind.has_antag_datum(/datum/antagonist/obsessed) \ || candidate.stat == DEAD \ - || !(ROLE_OBSESSED in candidate.client?.prefs?.be_special) \ || !SSjob.GetJob(candidate.mind.assigned_role) \ || (candidate.mind.assigned_role in GLOB.nonhuman_positions) \ ) @@ -698,7 +711,7 @@ /datum/dynamic_ruleset/midround/from_ghosts/spiders name = "Spider Infestation" - antag_flag = ROLE_SPIDER + role_preference = /datum/role_preference/midround_ghost/spider midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY required_type = /mob/dead/observer enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) @@ -746,7 +759,7 @@ spider_team.directive ="Ensure the survival of your brood and overtake whatever structure you find yourself in." var/datum/antagonist/spider/spider_antag = new_character.mind.has_antag_datum(/datum/antagonist/spider) spider_antag.set_spider_team(spider_team) - new_character.mind.special_role = antag_flag + new_character.mind.special_role = BAN_ROLE_SPIDER ////////////////////////////////////////////// // // @@ -758,7 +771,7 @@ name = "Swarmer" midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY antag_datum = /datum/antagonist/swarmer - antag_flag = ROLE_SWARMER + role_preference = /datum/role_preference/midround_ghost/swarmer enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(1,1,1,1,0,0,0,0,0,0) required_candidates = 1 @@ -798,7 +811,7 @@ name = "Morph" midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY antag_datum = /datum/antagonist/morph - antag_flag = ROLE_MORPH + role_preference = /datum/role_preference/midround_ghost/morph enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(2,2,1,1,1,1,1,1,0,0) required_candidates = 1 @@ -834,7 +847,7 @@ /datum/dynamic_ruleset/midround/from_ghosts/fugitives name = "Fugitives" midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT - antag_flag = ROLE_FUGITIVE + role_preference = /datum/role_preference/midround_ghost/fugitive required_type = /mob/dead/observer required_candidates = 1 weight = 3 diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index 4dc137aaadfb3..76bd6f9c06e17 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -7,7 +7,7 @@ /datum/dynamic_ruleset/roundstart/traitor name = "Traitors" - antag_flag = ROLE_TRAITOR + role_preference = /datum/role_preference/antagonist/traitor antag_datum = /datum/antagonist/traitor minimum_required_age = 0 protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) @@ -30,7 +30,7 @@ break var/mob/M = antag_pick_n_take(candidates) assigned += M.mind - M.mind.special_role = ROLE_TRAITOR + M.mind.special_role = BAN_ROLE_TRAITOR M.mind.restricted_roles = restricted_roles return TRUE @@ -43,8 +43,8 @@ /datum/dynamic_ruleset/roundstart/traitorbro name = "Blood Brothers" - antag_flag = ROLE_BROTHER - antag_datum = /datum/antagonist/brother/ + role_preference = /datum/role_preference/antagonist/blood_brother + antag_datum = /datum/antagonist/brother protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG) required_candidates = 2 @@ -68,7 +68,7 @@ var/mob/bro = antag_pick_n_take(candidates) assigned += bro.mind team.add_member(bro.mind) - bro.mind.special_role = "brother" + bro.mind.special_role = BAN_ROLE_BROTHER bro.mind.restricted_roles = restricted_roles pre_brother_teams += team return TRUE @@ -91,7 +91,7 @@ /datum/dynamic_ruleset/roundstart/changeling name = "Changelings" - antag_flag = ROLE_CHANGELING + role_preference = /datum/role_preference/antagonist/changeling antag_datum = /datum/antagonist/changeling protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG) @@ -111,7 +111,7 @@ var/mob/M = antag_pick_n_take(candidates) assigned += M.mind M.mind.restricted_roles = restricted_roles - M.mind.special_role = ROLE_CHANGELING + M.mind.special_role = BAN_ROLE_CHANGELING return TRUE /datum/dynamic_ruleset/roundstart/changeling/execute() @@ -128,7 +128,7 @@ /datum/dynamic_ruleset/roundstart/heretics name = "Heretics" - antag_flag = ROLE_HERETIC + role_preference = /datum/role_preference/antagonist/heretic antag_datum = /datum/antagonist/heretic protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG) @@ -150,7 +150,7 @@ var/mob/picked_candidate = antag_pick_n_take(candidates) assigned += picked_candidate.mind picked_candidate.mind.restricted_roles = restricted_roles - picked_candidate.mind.special_role = ROLE_HERETIC + picked_candidate.mind.special_role = BAN_ROLE_HERETIC return TRUE /datum/dynamic_ruleset/roundstart/heretics/execute() @@ -172,7 +172,7 @@ // Dynamic is a wonderful thing that adds wizards to every round and then adds even more wizards during the round. /datum/dynamic_ruleset/roundstart/wizard name = "Wizard" - antag_flag = ROLE_WIZARD + role_preference = /datum/role_preference/antagonist/wizard antag_datum = /datum/antagonist/wizard flags = HIGH_IMPACT_RULESET | NO_OTHER_ROUNDSTARTS_RULESET | PERSISTENT_RULESET minimum_required_age = 14 @@ -197,8 +197,8 @@ var/mob/M = antag_pick_n_take(candidates) if (M) assigned += M.mind - M.mind.assigned_role = ROLE_WIZARD - M.mind.special_role = ROLE_WIZARD + M.mind.assigned_role = BAN_ROLE_WIZARD + M.mind.special_role = BAN_ROLE_WIZARD return TRUE @@ -216,7 +216,7 @@ /datum/dynamic_ruleset/roundstart/bloodcult name = "Blood Cult" - antag_flag = ROLE_CULTIST + role_preference = /datum/role_preference/antagonist/blood_cult antag_datum = /datum/antagonist/cult minimum_required_age = 14 restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_CHAPLAIN, JOB_NAME_HEADOFPERSONNEL) @@ -241,7 +241,7 @@ break var/mob/M = antag_pick_n_take(candidates) assigned += M.mind - M.mind.special_role = ROLE_CULTIST + M.mind.special_role = BAN_ROLE_CULTIST M.mind.restricted_roles = restricted_roles return TRUE @@ -272,7 +272,7 @@ /datum/dynamic_ruleset/roundstart/nuclear name = "Nuclear Emergency" - antag_flag = ROLE_OPERATIVE + role_preference = /datum/role_preference/antagonist/nuclear_operative antag_datum = /datum/antagonist/nukeop var/datum/antagonist/antag_leader_datum = /datum/antagonist/nukeop/leader minimum_required_age = 14 @@ -298,8 +298,9 @@ break var/mob/M = antag_pick_n_take(candidates) assigned += M.mind - M.mind.assigned_role = "Nuclear Operative" - M.mind.special_role = "Nuclear Operative" + M.mind.assigned_role = BAN_ROLE_OPERATIVE + M.mind.special_role = BAN_ROLE_OPERATIVE + GLOB.pre_setup_antags += M.mind return TRUE /datum/dynamic_ruleset/roundstart/nuclear/execute() @@ -312,6 +313,7 @@ else var/datum/antagonist/nukeop/new_op = new antag_datum() M.add_antag_datum(new_op) + GLOB.pre_setup_antags -= M return TRUE /datum/dynamic_ruleset/roundstart/nuclear/round_result() @@ -357,8 +359,7 @@ /datum/dynamic_ruleset/roundstart/revs name = "Revolution" persistent = TRUE - antag_flag = ROLE_REV_HEAD - antag_flag_override = ROLE_REV + role_preference = /datum/role_preference/antagonist/revolutionary antag_datum = /datum/antagonist/rev/head minimum_required_age = 14 restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_CHIEFENGINEER, JOB_NAME_CHIEFMEDICALOFFICER, JOB_NAME_RESEARCHDIRECTOR) @@ -386,7 +387,7 @@ var/mob/M = antag_pick_n_take(candidates) assigned += M.mind M.mind.restricted_roles = restricted_roles - M.mind.special_role = antag_flag + M.mind.special_role = BAN_ROLE_REV_HEAD return TRUE /datum/dynamic_ruleset/roundstart/revs/execute() @@ -440,7 +441,6 @@ /datum/dynamic_ruleset/roundstart/extended name = "Extended" - antag_flag = null antag_datum = null restricted_roles = list() required_candidates = 0 @@ -489,7 +489,7 @@ /datum/dynamic_ruleset/roundstart/devil name = "Devil" - antag_flag = ROLE_DEVIL + role_preference = /datum/role_preference/antagonist/devil antag_datum = /datum/antagonist/devil restricted_roles = list(JOB_NAME_LAWYER, JOB_NAME_CURATOR, JOB_NAME_CHAPLAIN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE) required_candidates = 1 @@ -508,7 +508,7 @@ break var/mob/devil = antag_pick_n_take(candidates) assigned += devil.mind - devil.mind.special_role = ROLE_DEVIL + devil.mind.special_role = BAN_ROLE_DEVIL devil.mind.restricted_roles = restricted_roles log_game("[key_name(devil)] has been selected as a devil") @@ -577,7 +577,7 @@ /datum/dynamic_ruleset/roundstart/clockcult name = "Clockwork Cult" - antag_flag = ROLE_SERVANT_OF_RATVAR + role_preference = /datum/role_preference/antagonist/clock_cultist antag_datum = /datum/antagonist/servant_of_ratvar restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE,JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_CHAPLAIN, JOB_NAME_HEADOFPERSONNEL) required_candidates = 4 @@ -602,8 +602,8 @@ for (var/i in 1 to starter_servants) var/mob/servant = antag_pick_n_take(candidates) assigned += servant.mind - servant.mind.assigned_role = ROLE_SERVANT_OF_RATVAR - servant.mind.special_role = ROLE_SERVANT_OF_RATVAR + servant.mind.assigned_role = BAN_ROLE_SERVANT_OF_RATVAR + servant.mind.special_role = BAN_ROLE_SERVANT_OF_RATVAR //Generate scriptures generate_clockcult_scriptures() return TRUE @@ -640,7 +640,7 @@ /datum/dynamic_ruleset/roundstart/incursion name = "Incursion" - antag_flag = ROLE_INCURSION + role_preference = /datum/role_preference/antagonist/incursionist antag_datum = /datum/antagonist/incursion protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE,JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_HEADOFSECURITY, JOB_NAME_CHIEFENGINEER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CHIEFMEDICALOFFICER) restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG) @@ -663,7 +663,7 @@ break var/mob/M = antag_pick_n_take(candidates) assigned += M.mind - M.mind.special_role = ROLE_INCURSION + M.mind.special_role = BAN_ROLE_INCURSION M.mind.restricted_roles = restricted_roles return TRUE @@ -692,7 +692,7 @@ /datum/dynamic_ruleset/roundstart/hivemind name = "Assimilation" - antag_flag = ROLE_HIVE + role_preference = /datum/role_preference/antagonist/hivemind_host antag_datum = /datum/antagonist/hivemind protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE,JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG) @@ -709,7 +709,7 @@ var/mob/M = antag_pick_n_take(candidates) assigned += M.mind M.mind.restricted_roles = restricted_roles - M.mind.special_role = ROLE_HIVE + M.mind.special_role = BAN_ROLE_HIVE return TRUE /datum/dynamic_ruleset/roundstart/hivemind/execute() diff --git a/code/game/gamemodes/eldritch_cult/eldritch_cult.dm b/code/game/gamemodes/eldritch_cult/eldritch_cult.dm index 307b1be089318..0d364b4025134 100644 --- a/code/game/gamemodes/eldritch_cult/eldritch_cult.dm +++ b/code/game/gamemodes/eldritch_cult/eldritch_cult.dm @@ -2,7 +2,8 @@ name = "heresy" config_tag = "heresy" report_type = "heresy" - antag_flag = ROLE_HERETIC + banning_key = BAN_ROLE_HERETIC + role_preference = /datum/role_preference/antagonist/heretic false_report_weight = 5 protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_jobs = list(JOB_NAME_AI, JOB_NAME_CYBORG) @@ -44,9 +45,9 @@ for(var/i in 1 to num_ecult) if(!antag_candidates.len) break - var/datum/mind/cultie = antag_pick(antag_candidates, ROLE_HERETIC) + var/datum/mind/cultie = antag_pick(antag_candidates) antag_candidates -= cultie - cultie.special_role = ROLE_HERETIC + cultie.special_role = BAN_ROLE_HERETIC cultie.restricted_roles = restricted_jobs culties += cultie diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 0eac49e4e809e..861ad50e2b412 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -28,7 +28,10 @@ var/maximum_players = -1 // -1 is no maximum, positive numbers limit the selection of a mode on overstaffed stations var/required_enemies = 0 var/recommended_enemies = 0 - var/antag_flag = null //preferences flag such as BE_WIZARD that need to be turned on for players to be antag + /// BAN_ROLE_X key / special role key. + var/banning_key + /// The role preference used to poll players. + var/role_preference var/mob/living/living_antag_player = null var/datum/game_mode/replacementmode = null var/round_converted = 0 //0: round not converted, 1: round going to convert, 2: round converted @@ -87,7 +90,7 @@ return 1 /datum/game_mode/proc/setup_antag_candidates() - antag_candidates = get_players_for_role(antag_flag) + antag_candidates = get_players_for_role(banning_key, role_preference) ///Attempts to select players for special roles the mode might have. /datum/game_mode/proc/pre_setup() @@ -121,8 +124,8 @@ if(candidates.len == 0) return //No more candidates, end the selection process, and active specials at this time will be handled by latejoins or not included var/mob/person - if(special.special_role_flag) - person = antag_pick(candidates, special.special_role_flag) + if(special.use_antag_rep) + person = antag_pick(candidates, special.special_role_preference) else person = pick_n_take(candidates) if(is_banned_from(person.ckey, special.preference_type)) @@ -256,7 +259,7 @@ var/list/antag_candidates = list() for(var/mob/living/carbon/human/H in living_crew) - if(H.client && (H.client.prefs.toggles & PREFTOGGLE_MIDROUND_ANTAG) && !is_centcom_level(H.z)) + if(H.client && !is_centcom_level(H.z)) antag_candidates += H if(!antag_candidates) @@ -427,7 +430,8 @@ // The odds become: // Player A: 150 / 250 = 0.6 = 60% // Player B: 100 / 250 = 0.4 = 40% -/datum/game_mode/proc/antag_pick(list/datum/candidates, role) +/// The role_preference argument is optional, but candidates will not be considered if the preference is disabled. Passing null will consider all candidates. +/datum/game_mode/proc/antag_pick(list/datum/candidates, role_preference = null) if(!CONFIG_GET(flag/use_antag_rep)) // || candidates.len <= 1) return pick(candidates) @@ -458,7 +462,10 @@ if(!player) candidates -= candidate continue - total_tickets += min(((role in player.client.prefs.be_special) ? SSpersistence.antag_rep[p_ckey] : 0) + DEFAULT_ANTAG_TICKETS, MAX_TICKETS_PER_ROLL) + var/role_enabled = TRUE + if(role_preference && player.client) + role_enabled = role_preference_enabled(player.client, role_preference) + total_tickets += min((role_enabled ? SSpersistence.antag_rep[p_ckey] : 0) + DEFAULT_ANTAG_TICKETS, MAX_TICKETS_PER_ROLL) var/antag_select = rand(1,total_tickets) var/current = 1 @@ -475,9 +482,11 @@ else continue p_rep = SSpersistence.antag_rep[p_ckey] - + var/role_enabled = TRUE + if(role_preference && player.client) + role_enabled = role_preference_enabled(player.client, role_preference) var/previous = current - var/spend = min(((role in player.client.prefs.be_special) ? p_rep : 0) + DEFAULT_ANTAG_TICKETS, MAX_TICKETS_PER_ROLL) + var/spend = min((role_enabled ? p_rep : 0) + DEFAULT_ANTAG_TICKETS, MAX_TICKETS_PER_ROLL) current += spend if(antag_select >= previous && antag_select <= (current-1)) @@ -488,7 +497,7 @@ WARNING("Something has gone terribly wrong. /datum/game_mode/proc/antag_pick failed to select a candidate. Falling back to pick()") return pick(candidates) -/datum/game_mode/proc/get_players_for_role(role) +/datum/game_mode/proc/get_players_for_role(banning_key = null, role_preference = null) var/list/players = list() var/list/candidates = list() var/list/drafted = list() @@ -505,8 +514,8 @@ for(var/mob/dead/new_player/player in players) if(player.client && player.ready == PLAYER_READY_TO_PLAY) - if(role in player.client.prefs.be_special) - if(!is_banned_from(player.ckey, list(role, ROLE_SYNDICATE)) && !QDELETED(player)) + if(role_preference_enabled(player.client, role_preference)) + if(!is_banned_from(player.ckey, banning_key) && !QDELETED(player)) if(age_check(player.client)) //Must be older than the minimum age candidates += player.mind // Get a list of all the people who want to be the antagonist for this round @@ -519,8 +528,8 @@ if(candidates.len < recommended_enemies) for(var/mob/dead/new_player/player in players) if(player.client && player.ready == PLAYER_READY_TO_PLAY) - if(!(role in player.client.prefs.be_special)) // We don't have enough people who want to be antagonist, make a separate list of people who don't want to be one - if(!is_banned_from(player.ckey, list(role, ROLE_SYNDICATE)) && !QDELETED(player)) + if(!role_preference_enabled(player.client, role_preference)) // We don't have enough people who want to be antagonist, make a separate list of people who don't want to be one + if(!is_banned_from(player.ckey, banning_key) && !QDELETED(player)) drafted += player.mind if(restricted_jobs) @@ -564,15 +573,13 @@ // Less if there are not enough valid players in the game entirely to make recommended_enemies. -/datum/game_mode/proc/get_alive_non_antagonsist_players_for_role(role, list/restricted_roles) +/datum/game_mode/proc/get_alive_non_antagonsist_players_for_role(banning_key, role_preference, list/restricted_roles) var/list/candidates = list() for(var/mob/living/carbon/human/player in GLOB.player_list) - if(player.client && is_station_level(player.z)) - if(role in player.client.prefs.be_special) - if(!is_banned_from(player.ckey, list(role, ROLE_SYNDICATE)) && !QDELETED(player)) - if(age_check(player.client) && !player.mind.special_role) //Must be older than the minimum age - candidates += player.mind // Get a list of all the people who want to be the antagonist for this round + if(!QDELETED(player) && player.client && is_station_level(player.z) && !player.mind.special_role) + if(should_include_for_role(player.client, banning_key = banning_key, role_preference_key = role_preference, gamemode_for_age = src)) + candidates += player.mind // Get a list of all the people who want to be the antagonist for this round var/restricted_list = length(restricted_roles) ? restricted_roles : restricted_jobs if(restricted_list) diff --git a/code/game/gamemodes/gangs/gangs.dm b/code/game/gamemodes/gangs/gangs.dm index 139f04f473402..10aabcfcd9071 100644 --- a/code/game/gamemodes/gangs/gangs.dm +++ b/code/game/gamemodes/gangs/gangs.dm @@ -5,7 +5,8 @@ GLOBAL_LIST_EMPTY(gangs) /datum/game_mode/gang name = "gang war" config_tag = "gang" - antag_flag = ROLE_GANG + banning_key = BAN_ROLE_GANG + role_preference = /datum/role_preference/antagonist/gang restricted_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_AI, JOB_NAME_CYBORG,JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_HEADOFSECURITY) required_players = 30 required_enemies = 2 diff --git a/code/game/gamemodes/hivemind/hivemind.dm b/code/game/gamemodes/hivemind/hivemind.dm index 4fa0b6095c0d8..b71b4c76573bc 100644 --- a/code/game/gamemodes/hivemind/hivemind.dm +++ b/code/game/gamemodes/hivemind/hivemind.dm @@ -4,7 +4,8 @@ GLOBAL_LIST_EMPTY(hivehosts) name = "assimilation" config_tag = "hivemind" report_type = "hivemind" - antag_flag = ROLE_HIVE + banning_key = BAN_ROLE_HIVE + role_preference = /datum/role_preference/antagonist/hivemind false_report_weight = 5 protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_jobs = list(JOB_NAME_AI, JOB_NAME_CYBORG) @@ -62,9 +63,9 @@ GLOBAL_LIST_EMPTY(hivehosts) for(var/j = 0, j < num_hosts, j++) if (!antag_candidates.len) break - var/datum/mind/host = antag_pick(antag_candidates, ROLE_HIVE) + var/datum/mind/host = antag_pick(antag_candidates) hosts += host - host.special_role = ROLE_HIVE + host.special_role = BAN_ROLE_HIVE host.restricted_roles = restricted_jobs log_game("[key_name(host)] has been selected as a hivemind host") antag_candidates.Remove(host) diff --git a/code/game/gamemodes/incursion/incursion.dm b/code/game/gamemodes/incursion/incursion.dm index 837c38193860f..b334d764a1a8d 100644 --- a/code/game/gamemodes/incursion/incursion.dm +++ b/code/game/gamemodes/incursion/incursion.dm @@ -7,7 +7,8 @@ config_tag = "incursion" restricted_jobs = list(JOB_NAME_AI, JOB_NAME_CYBORG) protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE,JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_HEADOFSECURITY, JOB_NAME_CHIEFENGINEER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CHIEFMEDICALOFFICER) - antag_flag = ROLE_INCURSION + banning_key = BAN_ROLE_INCURSION + role_preference = /datum/role_preference/antagonist/incursion false_report_weight = 10 enemy_minimum_age = 0 @@ -30,8 +31,6 @@ if(CONFIG_GET(flag/protect_assistant_from_antagonist)) restricted_jobs += JOB_NAME_ASSISTANT - var/list/datum/mind/possible_traitors = get_players_for_role(ROLE_INCURSION) - var/datum/team/incursion/team = new var/cost_base = CONFIG_GET(number/incursion_cost_base) var/cost_increment = CONFIG_GET(number/incursion_cost_increment) @@ -41,14 +40,13 @@ team_size = CLAMP(team_size, CONFIG_GET(number/incursion_count_min), CONFIG_GET(number/incursion_count_max)) for(var/k = 1 to team_size) - var/datum/mind/incursion = antag_pick(possible_traitors, ROLE_INCURSION) + var/datum/mind/incursion = antag_pick(antag_candidates) if(!incursion) message_admins("Ran out of people to put in an incursion team, wanted [team_size] but only got [k-1]") break - possible_traitors -= incursion antag_candidates -= incursion team.add_member(incursion) - incursion.special_role = ROLE_INCURSION + incursion.special_role = BAN_ROLE_INCURSION incursion.restricted_roles = restricted_jobs log_game("[key_name(incursion)] has been selected as a member of the incursion") pre_incursionist_team = team diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index 281f3507fe38a..39b077c470132 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -8,7 +8,8 @@ required_players = 30 // 30 players - 3 players to be the nuke ops = 27 players remaining required_enemies = 2 recommended_enemies = 5 - antag_flag = ROLE_OPERATIVE + banning_key = BAN_ROLE_OPERATIVE + role_preference = /datum/role_preference/antagonist/nuclear_operative enemy_minimum_age = 14 announce_span = "danger" @@ -31,7 +32,7 @@ var/n_agents = min(round(num_players() / 10), antag_candidates.len, agents_possible) if(n_agents >= required_enemies) for(var/i = 0, i < n_agents, ++i) - var/datum/mind/new_op = antag_pick(antag_candidates, ROLE_OPERATIVE) + var/datum/mind/new_op = antag_pick(antag_candidates) pre_nukeops += new_op new_op.assigned_role = "Nuclear Operative" new_op.special_role = "Nuclear Operative" @@ -160,7 +161,7 @@ E.implant(H) var/obj/item/implant/weapons_auth/W = new/obj/item/implant/weapons_auth(H) W.implant(H) - H.faction |= ROLE_SYNDICATE + H.faction |= FACTION_SYNDICATE H.update_icons() /datum/outfit/syndicate/full diff --git a/code/game/gamemodes/overthrow/overthrow.dm b/code/game/gamemodes/overthrow/overthrow.dm index 7c5e94f9c76a4..c6f69e98a2722 100644 --- a/code/game/gamemodes/overthrow/overthrow.dm +++ b/code/game/gamemodes/overthrow/overthrow.dm @@ -3,7 +3,8 @@ name = "overthrow" config_tag = "overthrow" report_type = "overthrow" - antag_flag = ROLE_OVERTHROW + banning_key = BAN_ROLE_OVERTHROW + role_preference = /datum/role_preference/antagonist/traitor // use traitor role pref restricted_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_AI, JOB_NAME_CYBORG,JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_HEADOFSECURITY, JOB_NAME_CHIEFENGINEER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CHIEFMEDICALOFFICER) required_players = 20 // the core idea is of a swift, bloodless coup, so it shouldn't be as chaotic as revs. required_enemies = 2 // minimum two teams, otherwise it's just nerfed revs. @@ -28,11 +29,11 @@ for (var/i in 1 to sleeping_agents) if (!antag_candidates.len) break - var/datum/mind/sleeping_agent = antag_pick(antag_candidates, ROLE_OVERTHROW) + var/datum/mind/sleeping_agent = antag_pick(antag_candidates) antag_candidates -= sleeping_agent initial_agents += sleeping_agent sleeping_agent.restricted_roles = restricted_jobs - sleeping_agent.special_role = ROLE_OVERTHROW + sleeping_agent.special_role = BAN_ROLE_OVERTHROW if(initial_agents.len < required_enemies) setup_error = "Not enough initial sleeping agents candidates" diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm index 01a13d0aa18ce..97352dca8020b 100644 --- a/code/game/gamemodes/revolution/revolution.dm +++ b/code/game/gamemodes/revolution/revolution.dm @@ -11,7 +11,8 @@ name = "revolution" config_tag = "revolution" report_type = "revolution" - antag_flag = ROLE_REV + banning_key = BAN_ROLE_REV_HEAD + role_preference = /datum/role_preference/antagonist/revolutionary false_report_weight = 10 restricted_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_AI, JOB_NAME_CYBORG,JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_HEADOFSECURITY, JOB_NAME_CHIEFENGINEER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CHIEFMEDICALOFFICER) required_jobs = list(list(JOB_NAME_CAPTAIN=1),list(JOB_NAME_HEADOFPERSONNEL=1),list(JOB_NAME_HEADOFSECURITY=1),list(JOB_NAME_CHIEFENGINEER=1),list(JOB_NAME_RESEARCHDIRECTOR=1),list(JOB_NAME_CHIEFMEDICALOFFICER=1)) //Any head present @@ -55,7 +56,7 @@ for (var/i=1 to max_headrevs) if (antag_candidates.len==0) break - var/datum/mind/lenin = antag_pick(antag_candidates, ROLE_REV_HEAD) + var/datum/mind/lenin = antag_pick(antag_candidates) antag_candidates -= lenin headrev_candidates += lenin lenin.restricted_roles = restricted_jobs diff --git a/code/game/gamemodes/traitor/double_agents.dm b/code/game/gamemodes/traitor/double_agents.dm index 7e3db6e86c4d4..9e8862aee2b0b 100644 --- a/code/game/gamemodes/traitor/double_agents.dm +++ b/code/game/gamemodes/traitor/double_agents.dm @@ -12,7 +12,8 @@ recommended_enemies = 8 reroll_friendly = 0 traitor_name = "Nanotrasen Internal Affairs Agent" - antag_flag = ROLE_INTERNAL_AFFAIRS + banning_key = BAN_ROLE_INTERNAL_AFFAIRS + role_preference = /datum/role_preference/antagonist/internal_affairs traitors_possible = 10 //hard limit on traitors if scaling is turned off num_modifier = 4 // Four additional traitors diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm index 1b29f900425bd..e25c5c113cb8f 100644 --- a/code/game/gamemodes/traitor/traitor.dm +++ b/code/game/gamemodes/traitor/traitor.dm @@ -9,7 +9,8 @@ name = "traitor" config_tag = "traitor" report_type = "traitor" - antag_flag = ROLE_TRAITOR + role_preference = /datum/role_preference/antagonist/traitor + banning_key = BAN_ROLE_TRAITOR false_report_weight = 20 //Reports of traitors are pretty common. restricted_jobs = list(JOB_NAME_CYBORG)//They are part of the AI if he is traitor so are they, they use to get double chances protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) @@ -58,7 +59,7 @@ for(var/j = 0, j < num_traitors, j++) if (!antag_candidates.len) break - var/datum/mind/traitor = antag_pick(antag_candidates, ROLE_TRAITOR) + var/datum/mind/traitor = antag_pick(antag_candidates) pre_traitors += traitor traitor.special_role = traitor_name traitor.restricted_roles = restricted_jobs @@ -93,11 +94,14 @@ if((SSticker.mode.traitors.len + pre_traitors.len) >= traitorcap) //Upper cap for number of latejoin antagonists return if((SSticker.mode.traitors.len + pre_traitors.len) <= (traitorcap - 2) || prob(100 / (tsc * 2))) - if(antag_flag in character.client.prefs.be_special) - if(!is_banned_from(character.ckey, list(ROLE_TRAITOR, ROLE_SYNDICATE)) && !QDELETED(character)) - if(age_check(character.client)) - if(!(character.job in restricted_jobs)) - add_latejoin_traitor(character.mind) + if(!QDELETED(character) && character.client && should_include_for_role( + character.client, + banning_key = banning_key, + role_preference_key = role_preference, + gamemode_for_age = src + )) + if(!(character.job in restricted_jobs)) + add_latejoin_traitor(character.mind) /datum/game_mode/traitor/proc/add_latejoin_traitor(datum/mind/character) var/datum/antagonist/traitor/new_antag = new antag_datum() diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm index 03cb95fc04b36..e8a6eff1718b3 100644 --- a/code/game/gamemodes/wizard/wizard.dm +++ b/code/game/gamemodes/wizard/wizard.dm @@ -6,7 +6,8 @@ name = "wizard" config_tag = "wizard" report_type = "wizard" - antag_flag = ROLE_WIZARD + banning_key = BAN_ROLE_WIZARD + role_preference = /datum/role_preference/antagonist/wizard false_report_weight = 10 required_players = 20 required_enemies = 1 @@ -22,10 +23,10 @@ title_icon = "wizard" /datum/game_mode/wizard/pre_setup() - var/datum/mind/wizard = antag_pick(antag_candidates, ROLE_WIZARD) + var/datum/mind/wizard = antag_pick(antag_candidates) wizards += wizard - wizard.assigned_role = ROLE_WIZARD - wizard.special_role = ROLE_WIZARD + wizard.assigned_role = BAN_ROLE_WIZARD + wizard.special_role = BAN_ROLE_WIZARD log_game("[key_name(wizard)] has been selected as a Wizard") //TODO: Move these to base antag datum if(GLOB.wizardstart.len == 0) setup_error = "No wizard starting location found" diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index bf04200c4da7d..c9a7326bb7494 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -296,7 +296,7 @@ /obj/machinery/clonepod/proc/offer_to_ghost(mob/living/carbon/H) set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [H.real_name]'s experimental clone?", ROLE_EXPERIMENTAL_CLONE, null, null, 300, H, POLL_IGNORE_EXPERIMENTAL_CLONE) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [H.real_name]'s experimental clone?", BAN_ROLE_EXPERIMENTAL_CLONE, null, null, 300, H, POLL_IGNORE_EXPERIMENTAL_CLONE) if(length(candidates)) var/mob/dead/observer/C = pick(candidates) H.key = C.key diff --git a/code/game/machinery/ecto_sniffer.dm b/code/game/machinery/ecto_sniffer.dm index 1554acbbdd373..361cce5694c83 100644 --- a/code/game/machinery/ecto_sniffer.dm +++ b/code/game/machinery/ecto_sniffer.dm @@ -35,7 +35,7 @@ to_chat(user, "You must wait for your ectoplasmic residue to decay off of [src]'s sensors!") return - if(is_banned_from(user.ckey, ROLE_POSIBRAIN)) + if(is_banned_from(user.ckey, BAN_ROLE_POSIBRAIN)) to_chat(user, "Central Command outlawed your soul from interacting with the living...") return diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm index 926026868f94a..b59a1b6c88ad8 100644 --- a/code/game/machinery/porta_turret/portable_turret.dm +++ b/code/game/machinery/porta_turret/portable_turret.dm @@ -427,7 +427,7 @@ if(iscyborg(sillycone)) var/mob/living/silicon/robot/sillyconerobot = A - if((ROLE_SYNDICATE in faction) && sillyconerobot.emagged == TRUE) + if((FACTION_SYNDICATE in faction) && sillyconerobot.emagged == TRUE) continue else if(iscarbon(A)) @@ -681,7 +681,7 @@ stun_projectile_sound = 'sound/weapons/gunshot.ogg' icon_state = "syndie_off" base_icon_state = "syndie" - faction = list(ROLE_SYNDICATE) + faction = list(FACTION_SYNDICATE) desc = "A ballistic machine gun auto-turret." /obj/machinery/porta_turret/syndicate/ComponentInitialize() diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm index 0074c82094f41..a004d2ad33ddc 100644 --- a/code/game/objects/effects/anomalies.dm +++ b/code/game/objects/effects/anomalies.dm @@ -352,7 +352,7 @@ S.amount_grown = SLIME_EVOLUTION_THRESHOLD S.Evolve() S.flavor_text = FLAVOR_TEXT_EVIL - S.set_playable() + S.set_playable(BAN_ROLE_PYRO_SLIME) ///////////////////// diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index 9f89a4911d5e5..241a59cc723a0 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -120,7 +120,7 @@ /obj/structure/spider/eggcluster/attack_ghost(mob/user) . = ..() - if(!user?.client.canGhostRole(ROLE_SPIDER, TRUE, flags_1)) + if(!can_take_ghost_spawner(user?.client, BAN_ROLE_SPIDER, TRUE, flags_1 & ADMIN_SPAWNED_1)) return if(ghost_ready) make_spider(user) @@ -198,7 +198,7 @@ random_spider = new random_spider(get_turf(src)) random_spider.faction = faction.Copy() random_spider.spider_team = spider_team - random_spider.set_playable() + random_spider.set_playable(BAN_ROLE_SPIDER) spawns_remaining-- if(!spawns_remaining) qdel(src) diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm index d93a1555a5b61..377133f11f578 100644 --- a/code/game/objects/items/holy_weapons.dm +++ b/code/game/objects/items/holy_weapons.dm @@ -580,7 +580,7 @@ possessed = TRUE - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_POSSESSED_BLADE) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", BAN_ROLE_SPECTRAL_BLADE, null, FALSE, 100, POLL_IGNORE_POSSESSED_BLADE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) diff --git a/code/game/objects/structures/fugitive_role_spawners.dm b/code/game/objects/structures/fugitive_role_spawners.dm index 15c513fc7b325..4381b58fdb68c 100644 --- a/code/game/objects/structures/fugitive_role_spawners.dm +++ b/code/game/objects/structures/fugitive_role_spawners.dm @@ -2,7 +2,7 @@ name = "Fugitive Hunter pod" desc = "A small sleeper typically used to make long distance travel a bit more bearable." mob_name = "a fugitive hunter" - assignedrole = ROLE_FUGITIVE_HUNTER + assignedrole = BAN_ROLE_FUGITIVE_HUNTER roundstart = FALSE death = FALSE random = TRUE @@ -10,7 +10,7 @@ density = TRUE icon = 'icons/obj/machines/sleeper.dmi' icon_state = "sleeper" - banType = ROLE_FUGITIVE_HUNTER + banType = BAN_ROLE_FUGITIVE_HUNTER /// This is set by the shuttle template var/datum/fugitive_type/hunter/backstory var/static/leader_spawned = FALSE @@ -30,7 +30,6 @@ var/datum/antagonist/fugitive_hunter/fughunter = new fughunter.backstory = backstory new_spawn.mind.add_antag_datum(fughunter) - new_spawn.mind.assigned_role = ROLE_FUGITIVE_HUNTER var/outfit = leader ? backstory.leader_outfit : backstory.outfit if(islist(outfit)) var/static/index = 1 // incredibly jank, but no two fugitive teams should exist or I will explode reality diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm index 875f484b843ec..df2b6913075c7 100644 --- a/code/game/objects/structures/ghost_role_spawners.dm +++ b/code/game/objects/structures/ghost_role_spawners.dm @@ -18,6 +18,7 @@ Estimated time of last contact: Deployment, 5000 millennia ago." assignedrole = "Lifebringer" use_cooldown = TRUE + banType = BAN_ROLE_LIFEBRINGER /obj/effect/mob_spawn/human/seed_vault/special(mob/living/new_spawn) var/plant_name = pick("Tomato", "Potato", "Broccoli", "Carrot", "Ambrosia", "Pumpkin", "Ivy", "Kudzu", "Banana", "Moss", "Flower", "Bloom", "Root", "Bark", "Glowshroom", "Petal", "Leaf", \ @@ -54,6 +55,7 @@ assignedrole = "Ash Walker" var/datum/team/ashwalkers/team use_cooldown = TRUE + banType = BAN_ROLE_ASHWALKER /obj/effect/mob_spawn/human/ash_walker/special(mob/living/new_spawn) to_chat(new_spawn, "Drag the corpses of men and beasts to your nest. It will absorb them to create more of your kind. Don't leave your nest undefended, protect it with your life. Glory to the Necropolis!") @@ -78,40 +80,6 @@ head = /obj/item/clothing/head/helmet/gladiator uniform = /obj/item/clothing/under/costume/gladiator/ash_walker - -//Timeless prisons: Spawns in Wish Granter prisons in lavaland. Ghosts become age-old users of the Wish Granter and are advised to seek repentance for their past. -/obj/effect/mob_spawn/human/exile - name = "timeless prison" - desc = "Although this stasis pod looks medicinal, it seems as though it's meant to preserve something for a very long time." - mob_name = "a penitent exile" - icon = 'icons/obj/machines/sleeper.dmi' - icon_state = "sleeper" - roundstart = FALSE - death = FALSE - mob_species = /datum/species/shadow - short_desc = "You are cursed." - flavour_text = "Years ago, you sacrificed the lives of your trusted friends and the humanity of yourself to reach the Wish Granter. Though you \ - did so, it has come at a cost: your very body rejects the light, dooming you to wander endlessly in this horrible wasteland." - assignedrole = "Exile" - use_cooldown = TRUE - -/obj/effect/mob_spawn/human/exile/Destroy() - new/obj/structure/fluff/empty_sleeper(get_turf(src)) - return ..() - -/obj/effect/mob_spawn/human/exile/special(mob/living/new_spawn) - new_spawn.fully_replace_character_name(null,"Wish Granter's Victim ([rand(1,999)])") - var/wish = rand(1,4) - switch(wish) - if(1) - to_chat(new_spawn, "You wished to kill, and kill you did. You've lost track of how many, but the spark of excitement that murder once held has winked out. You feel only regret.") - if(2) - to_chat(new_spawn, "You wished for unending wealth, but no amount of money was worth this existence. Maybe charity might redeem your soul?") - if(3) - to_chat(new_spawn, "You wished for power. Little good it did you, cast out of the light. You are the [gender == MALE ? "king" : "queen"] of a hell that holds no subjects. You feel only remorse.") - if(4) - to_chat(new_spawn, "You wished for immortality, even as your friends lay dying behind you. No matter how many times you cast yourself into the lava, you awaken in this room again within a few days. There is no escape.") - //Golem shells: Spawns in Free Golem ships in lavaland. Ghosts become mineral golems and are advised to spread personal freedom. /obj/effect/mob_spawn/human/golem name = "inert free golem shell" @@ -132,6 +100,7 @@ flavour_text = "In his infinite and divine wisdom, he set your clan free to \ travel the stars with a single declaration: \"Yeah go do whatever.\" Though you are bound to the one who created you, it is customary in your society to repeat those same words to newborn \ golems, so that no golem may ever be forced to serve again." + banType = BAN_ROLE_FREE_GOLEM /obj/effect/mob_spawn/human/golem/Initialize(mapload, datum/species/golem/species = null, mob/creator = null) if(species) //spawners list uses object name to register so this goes before ..() @@ -210,6 +179,7 @@ can_transfer = FALSE mob_species = /datum/species/golem/adamantine use_cooldown = TRUE //Only the roundstart free golems are + banType = BAN_ROLE_FREE_GOLEM id = /obj/item/card/id/golem/spawner //Malfunctioning cryostasis sleepers: Spawns in makeshift shelters in lavaland. Ghosts become hermits with knowledge of how they got to where they are now. @@ -229,6 +199,7 @@ the fresh air of Earth. These thoughts are dispelled by yet another recollection of how you got here... " assignedrole = "Hermit" use_cooldown = TRUE + banType = BAN_ROLE_HERMIT /obj/effect/mob_spawn/human/hermit/Initialize(mapload) . = ..() @@ -271,6 +242,7 @@ all you did was apply bruise packs. Why is this place full of advanced medical equipment? And what are those screams you hear? The world outside is desolate - tormented with fire and brimstone. But you took an oath. \ You have to save these people! You might not have a fancy cloning machine like a real hospital, but surely there must be some way to save these people with the tools you have. Right?" assignedrole = "Translocated Vet" + banType = BAN_ROLE_TRANSLOCATED_VET /obj/effect/mob_spawn/human/doctor/alive/lavaland/Destroy() var/obj/structure/fluff/empty_sleeper/S = new(drop_location()) @@ -292,6 +264,7 @@ flavour_text = "Good. It seems as though your ship crashed. You remember that you were convicted of " assignedrole = "Escaped Prisoner" use_cooldown = TRUE + banType = BAN_ROLE_LAVALAND_ESCAPED_PRISONER /obj/effect/mob_spawn/human/prisoner_transport/special(mob/living/L) L.fully_replace_character_name(null,"NTP #LL-0[rand(111,999)]") //Nanotrasen Prisoner #Lavaland-(numbers) @@ -332,6 +305,7 @@ important_info = "DON'T leave the hotel" assignedrole = "Hotel Staff" use_cooldown = TRUE + banType = BAN_ROLE_HOTEL_STAFF /datum/outfit/hotelstaff name = "Hotel Staff" @@ -379,6 +353,7 @@ var/obj/effect/proc_holder/spell/targeted/summon_friend/spell var/datum/mind/owner assignedrole = "SuperFriend" + banType = BAN_ROLE_DEMONIC_FRIEND /obj/effect/mob_spawn/human/demonic_friend/Initialize(mapload, datum/mind/owner_mind, obj/effect/proc_holder/spell/targeted/summon_friend/summoning_spell) . = ..() @@ -419,77 +394,6 @@ implants = list(/obj/item/implant/mindshield) //No revolutionaries, he's MY friend. id = /obj/item/card/id -/obj/effect/mob_spawn/human/syndicate - name = "Syndicate Operative" - roundstart = FALSE - death = FALSE - icon = 'icons/obj/machines/sleeper.dmi' - icon_state = "sleeper_s" - outfit = /datum/outfit/syndicate_empty - assignedrole = "Space Syndicate" //I know this is really dumb, but Syndicate operative is nuke ops - -/datum/outfit/syndicate_empty - name = "Syndicate Operative Empty" - uniform = /obj/item/clothing/under/syndicate - shoes = /obj/item/clothing/shoes/combat - gloves = /obj/item/clothing/gloves/combat - ears = /obj/item/radio/headset/syndicate/alt - back = /obj/item/storage/backpack - implants = list(/obj/item/implant/weapons_auth) - id = /obj/item/card/id/syndicate - -/datum/outfit/syndicate_empty/post_equip(mob/living/carbon/human/H) - H.faction |= ROLE_SYNDICATE - -/obj/effect/mob_spawn/human/syndicate/battlecruiser - name = "Syndicate Battlecruiser Ship Operative" - short_desc = "You are a crewmember aboard the syndicate flagship: the SBC Starfury." - flavour_text = "Your job is to follow your captain's orders, maintain the ship, and keep the engine running. If you are not familiar with how the supermatter engine functions: do not attempt to start it." - important_info = "The armory is not a candy store, and your role is not to assault the station directly, leave that work to the assault operatives." - outfit = /datum/outfit/syndicate_empty/SBC - -/datum/outfit/syndicate_empty/SBC - name = "Syndicate Battlecruiser Ship Operative" - l_pocket = /obj/item/gun/ballistic/automatic/pistol - r_pocket = /obj/item/kitchen/knife/combat/survival - belt = /obj/item/storage/belt/military/assault - -/obj/effect/mob_spawn/human/syndicate/battlecruiser/assault - name = "Syndicate Battlecruiser Assault Operative" - short_desc = "You are an assault operative aboard the syndicate flagship: the SBC Starfury." - flavour_text = "Your job is to follow your captain's orders, keep intruders out of the ship, and assault Space Station 13. There is an armory, multiple assault ships, and beam cannons to attack the station with." - important_info = "Work as a team with your fellow operatives and work out a plan of attack. If you are overwhelmed, escape back to your ship!" - outfit = /datum/outfit/syndicate_empty/SBC/assault - -/datum/outfit/syndicate_empty/SBC/assault - name = "Syndicate Battlecruiser Assault Operative" - uniform = /obj/item/clothing/under/syndicate/combat - l_pocket = /obj/item/ammo_box/magazine/m10mm - r_pocket = /obj/item/kitchen/knife/combat/survival - belt = /obj/item/storage/belt/military - suit = /obj/item/clothing/suit/armor/vest - suit_store = /obj/item/gun/ballistic/automatic/pistol - back = /obj/item/storage/backpack/security - mask = /obj/item/clothing/mask/gas/syndicate - -/obj/effect/mob_spawn/human/syndicate/battlecruiser/captain - name = "Syndicate Battlecruiser Captain" - short_desc = "You are the captain aboard the syndicate flagship: the SBC Starfury." - flavour_text = "Your job is to oversee your crew, defend the ship, and destroy Space Station 13. The ship has an armory, multiple ships, beam cannons, and multiple crewmembers to accomplish this goal." - important_info = "As the captain, this whole operation falls on your shoulders. You do not need to nuke the station, causing sufficient damage and preventing your ship from being destroyed will be enough." - outfit = /datum/outfit/syndicate_empty/SBC/assault/captain - id_access_list = list(150,151) - -/datum/outfit/syndicate_empty/SBC/assault/captain - name = "Syndicate Battlecruiser Captain" - l_pocket = /obj/item/melee/transforming/energy/sword/saber/red - r_pocket = /obj/item/melee/classic_baton/police/telescopic - suit = /obj/item/clothing/suit/armor/vest/capcarapace/syndicate - suit_store = /obj/item/gun/ballistic/revolver/mateba - back = /obj/item/storage/backpack/satchel/leather - head = /obj/item/clothing/head/HoS/syndicate - mask = /obj/item/clothing/mask/cigarette/cigar/havana - glasses = /obj/item/clothing/glasses/thermal/eyepatch //Ancient cryogenic sleepers. Players become NT crewmen from a hundred year old space station, now on the verge of collapse. /obj/effect/mob_spawn/human/oldsec @@ -514,6 +418,7 @@ l_pocket = /obj/item/assembly/flash/handheld assignedrole = "Ancient Crew" use_cooldown = TRUE + banType = BAN_ROLE_ANCIENT_CREW /obj/effect/mob_spawn/human/oldsec/Destroy() new/obj/structure/showcase/machinery/oldpod/used(drop_location()) @@ -541,6 +446,7 @@ l_pocket = /obj/item/tank/internals/emergency_oxygen assignedrole = "Ancient Crew" use_cooldown = TRUE + banType = BAN_ROLE_ANCIENT_CREW /obj/effect/mob_spawn/human/oldeng/Destroy() new/obj/structure/showcase/machinery/oldpod/used(drop_location()) @@ -567,6 +473,7 @@ l_pocket = /obj/item/stack/medical/bruise_pack assignedrole = "Ancient Crew" use_cooldown = TRUE + banType = BAN_ROLE_ANCIENT_CREW /obj/effect/mob_spawn/human/oldsci/Destroy() new/obj/structure/showcase/machinery/oldpod/used(drop_location()) @@ -588,8 +495,8 @@ show_flavour = FALSE //Flavour only exists for spawners menu short_desc = "You are a space pirate." flavour_text = "The station refused to pay for your protection, protect the ship, siphon the credits from the station and raid it for even more loot." - assignedrole = ROLE_SPACE_PIRATE - banType = ROLE_SPACE_PIRATE + assignedrole = BAN_ROLE_SPACE_PIRATE + banType = BAN_ROLE_SPACE_PIRATE var/rank = "Mate" /obj/effect/mob_spawn/human/pirate/special(mob/living/new_spawn) diff --git a/code/game/objects/structures/spawner.dm b/code/game/objects/structures/spawner.dm index 0b41fcf8d498d..8f7870e7b30fa 100644 --- a/code/game/objects/structures/spawner.dm +++ b/code/game/objects/structures/spawner.dm @@ -31,7 +31,7 @@ icon_state = "syndbeacon" spawn_text = "warps in from" mob_types = list(/mob/living/simple_animal/hostile/syndicate/ranged) - faction = list(ROLE_SYNDICATE) + faction = list(FACTION_SYNDICATE) /obj/structure/spawner/skeleton name = "bone pit" diff --git a/code/modules/admin/antag_panel.dm b/code/modules/admin/antag_panel.dm index 3dc29f2d1cfa4..7b7eae1822bb9 100644 --- a/code/modules/admin/antag_panel.dm +++ b/code/modules/admin/antag_panel.dm @@ -165,10 +165,8 @@ GLOBAL_VAR(antag_prototypes) continue pref_source = prototype break - if(pref_source.job_rank) - if(!is_banned_from(src.key, pref_source.job_rank)) - antag_header_parts += pref_source.enabled_in_preferences(src) ? "Enabled in Prefs" : "Disabled in Prefs" - else + if(pref_source.banning_key) + if(is_banned_from(src.key, pref_source.banning_key)) antag_header_parts += "\[BANNED\]" //Traitor : None | Traitor | IAA diff --git a/code/modules/admin/fun_balloon.dm b/code/modules/admin/fun_balloon.dm index ca395efabda3c..4a6e91e14af54 100644 --- a/code/modules/admin/fun_balloon.dm +++ b/code/modules/admin/fun_balloon.dm @@ -53,7 +53,7 @@ bodies += M var/question = "Would you like to be [group_name]?" - var/list/candidates = pollCandidatesForMobs(question, ROLE_PAI, null, FALSE, 100, bodies) + var/list/candidates = pollCandidatesForMobs(question, BAN_ROLE_SENTIENCE, null, FALSE, 100, bodies) while(LAZYLEN(candidates) && LAZYLEN(bodies)) var/mob/dead/observer/C = pick_n_take(candidates) var/mob/living/body = pick_n_take(bodies) diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm index 613234edb6673..89201c6970e00 100644 --- a/code/modules/admin/secrets.dm +++ b/code/modules/admin/secrets.dm @@ -690,7 +690,7 @@ GLOBAL_DATUM_INIT(admin_secrets, /datum/admin_secrets, new) continue if((L in GLOB.player_list) || L.mind || (L.flags_1 & HOLOGRAM_1)) continue - L.set_playable() + L.set_playable(BAN_ROLE_SENTIENT_ANIMAL) if("flipmovement") if(!check_rights(R_FUN)) @@ -789,7 +789,7 @@ GLOBAL_DATUM_INIT(admin_secrets, /datum/admin_secrets, new) var/list/candidates = list() if (prefs["offerghosts"]["value"] == "Yes") - candidates = pollGhostCandidates(replacetext(prefs["ghostpoll"]["value"], "%TYPE%", initial(pathToSpawn.name)), ROLE_TRAITOR) + candidates = pollGhostCandidates(replacetext(prefs["ghostpoll"]["value"], "%TYPE%", initial(pathToSpawn.name)), BAN_ROLE_ALL_ANTAGONISTS) if (prefs["playersonly"]["value"] == "Yes" && length(candidates) < prefs["minplayers"]["value"]) message_admins("Not enough players signed up to create a portal storm, the minimum was [prefs["minplayers"]["value"]] and the number of signups [length(candidates)]") diff --git a/code/modules/admin/sql_ban_system.dm b/code/modules/admin/sql_ban_system.dm index d1c09200491a2..566e122dfc5f3 100644 --- a/code/modules/admin/sql_ban_system.dm +++ b/code/modules/admin/sql_ban_system.dm @@ -325,16 +325,15 @@ "} break_counter++ output += "" - var/list/long_job_lists = list(("Civilian" = GLOB.civilian_positions | JOB_NAME_GIMMICK), - "Ghost and Other Roles" = list(ROLE_BRAINWASHED, ROLE_HYPNOTIZED, ROLE_DEATHSQUAD, ROLE_DRONE, ROLE_LAVALAND, ROLE_MIND_TRANSFER, ROLE_POSIBRAIN, ROLE_SENTIENCE), - "Antagonist Positions" = list(ROLE_ABDUCTOR, ROLE_ALIEN, ROLE_BLOB, ROLE_SPACE_DRAGON, - ROLE_BROTHER, ROLE_CHANGELING, ROLE_CULTIST, ROLE_HERETIC, - ROLE_DEVIL, ROLE_INTERNAL_AFFAIRS, ROLE_MALF, - ROLE_NINJA, ROLE_OPERATIVE, - ROLE_SERVANT_OF_RATVAR, - ROLE_OVERTHROW, ROLE_REV, ROLE_REVENANT, - ROLE_REV_HEAD, ROLE_SYNDICATE, - ROLE_TRAITOR, ROLE_WIZARD, ROLE_HIVE, ROLE_GANG, ROLE_TERATOMA, ROLE_NIGHTMARE, ROLE_SPIDER, ROLE_MORPH, ROLE_SWARMER, ROLE_SPACE_PIRATE, ROLE_FUGITIVE, ROLE_FUGITIVE_HUNTER)) //ROLE_REV_HEAD is excluded from this because rev jobbans are handled by ROLE_REV + var/list/long_job_lists = list( + "Civilian" = GLOB.civilian_positions | JOB_NAME_GIMMICK, + "Special Ban Types" = list(BAN_ROLE_ALL_ANTAGONISTS, BAN_ROLE_ALL_ANTAGONISTS_AND_FORCED, BAN_ROLE_ALL_GHOST_ROLES), + "Antagonist Positions" = GLOB.antagonist_bannable_roles, + "Forced Antagonist Positions" = GLOB.forced_bannable_roles, + "Ghost Roles" = GLOB.ghost_role_bannable_roles, + "Other" = GLOB.other_bannable_roles, + ) + for(var/department in long_job_lists) output += "
" break_counter = 0 diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm index 9416dd0924079..469b049da44ed 100644 --- a/code/modules/admin/verbs/one_click_antag.dm +++ b/code/modules/admin/verbs/one_click_antag.dm @@ -27,10 +27,10 @@ popup.set_content(dat) popup.open() -/datum/admins/proc/isReadytoRumble(mob/living/carbon/human/applicant, targetrole, onstation = TRUE, conscious = TRUE) +/datum/admins/proc/isReadytoRumble(mob/living/carbon/human/applicant, targetrole, preference, onstation = TRUE, conscious = TRUE) if(applicant.mind.special_role) return FALSE - if(!(targetrole in applicant.client.prefs.be_special)) + if(!role_preference_enabled(applicant.client, preference)) return FALSE if(onstation) var/turf/T = get_turf(applicant) @@ -40,7 +40,7 @@ return FALSE if(!considered_alive(applicant.mind) || considered_afk(applicant.mind)) //makes sure the player isn't a zombie, brain, or just afk all together return FALSE - return !is_banned_from(applicant.ckey, list(targetrole, ROLE_SYNDICATE)) + return !is_banned_from(applicant.ckey, targetrole) /datum/admins/proc/makeTraitors(maxCount = 3) @@ -59,7 +59,7 @@ var/mob/living/carbon/human/H = null for(var/mob/living/carbon/human/applicant in GLOB.player_list) - if(isReadytoRumble(applicant, ROLE_TRAITOR)) + if(isReadytoRumble(applicant, BAN_ROLE_TRAITOR, /datum/role_preference/midround_living/traitor)) if(temp.age_check(applicant.client)) if(!(applicant.job in temp.restricted_jobs)) candidates += applicant @@ -94,7 +94,7 @@ var/mob/living/carbon/human/H = null for(var/mob/living/carbon/human/applicant in GLOB.player_list) - if(isReadytoRumble(applicant, ROLE_CHANGELING)) + if(isReadytoRumble(applicant, BAN_ROLE_CHANGELING, /datum/role_preference/antagonist/changeling)) if(temp.age_check(applicant.client)) if(!(applicant.job in temp.restricted_jobs)) candidates += applicant @@ -124,7 +124,7 @@ var/mob/living/carbon/human/H = null for(var/mob/living/carbon/human/applicant in GLOB.player_list) - if(isReadytoRumble(applicant, ROLE_REV)) + if(isReadytoRumble(applicant, BAN_ROLE_REV_HEAD, /datum/role_preference/antagonist/revolutionary)) if(temp.age_check(applicant.client)) if(!(applicant.job in temp.restricted_jobs)) candidates += applicant @@ -142,7 +142,7 @@ /datum/admins/proc/makeWizard() - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for the position of a Wizard Federation 'diplomat'?", ROLE_WIZARD, null) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for the position of a Wizard Federation 'diplomat'?", BAN_ROLE_WIZARD, null, /datum/role_preference/midround_ghost/wizard) var/mob/dead/observer/selected = pick_n_take(candidates) @@ -166,7 +166,7 @@ var/mob/living/carbon/human/H = null for(var/mob/living/carbon/human/applicant in GLOB.player_list) - if(isReadytoRumble(applicant, ROLE_CULTIST)) + if(isReadytoRumble(applicant, BAN_ROLE_CULTIST, /datum/role_preference/antagonist/blood_cultist)) if(temp.age_check(applicant.client)) if(!(applicant.job in temp.restricted_jobs)) candidates += applicant @@ -186,8 +186,7 @@ /datum/admins/proc/makeNukeTeam(maxCount = 5) - var/datum/game_mode/nuclear/temp = new - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for a nuke team being sent in?", ROLE_OPERATIVE, temp) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for a nuke team being sent in?", BAN_ROLE_OPERATIVE, null, /datum/role_preference/midround_ghost/nuclear_operative) var/list/mob/dead/observer/chosen = list() var/mob/dead/observer/theghost = null diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index d0a38b9964d5c..dc3b449451663 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -297,7 +297,7 @@ for(var/mob/M in GLOB.player_list) if(M.stat != DEAD) continue //we are not dead! - if(!(ROLE_ALIEN in M.client.prefs.be_special)) + if(!role_preference_enabled(M.client, /datum/role_preference/midround_ghost/xenomorph)) continue //we don't want to be an alium if(M.client.is_afk()) continue //we are afk @@ -366,7 +366,7 @@ Traitors and the like can also be revived with the previous role mostly intact. if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something //Check if they were an alien - if(G_found.mind.assigned_role == ROLE_ALIEN) + if(G_found.mind.assigned_role == BAN_ROLE_ALIEN) if(alert("This character appears to have been an alien. Would you like to respawn them as such?",,"Yes","No")=="Yes") var/turf/T if(GLOB.xeno_spawn.len) @@ -467,15 +467,15 @@ Traitors and the like can also be revived with the previous role mostly intact. switch(new_character.mind.special_role) - if(ROLE_WIZARD) + if(BAN_ROLE_WIZARD) new_character.forceMove(pick(GLOB.wizardstart)) var/datum/antagonist/wizard/A = new_character.mind.has_antag_datum(/datum/antagonist/wizard,TRUE) A.equip_wizard() - if(ROLE_SYNDICATE) + if(BAN_ROLE_OPERATIVE) new_character.forceMove(pick(GLOB.nukeop_start)) var/datum/antagonist/nukeop/N = new_character.mind.has_antag_datum(/datum/antagonist/nukeop,TRUE) N.equip_op() - if(ROLE_NINJA) + if(BAN_ROLE_NINJA) var/list/ninja_spawn = list() for(var/obj/effect/landmark/carpspawn/L in GLOB.landmarks_list) ninja_spawn += L diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index 7396b459b1b7d..3cb92de24aaf9 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -11,7 +11,8 @@ GLOBAL_LIST(admin_antag_list) var/silent = FALSE //Silent will prevent the gain/lose texts to show var/can_coexist_with_others = TRUE //Whether or not the person will be able to have more than one datum var/list/typecache_datum_blacklist = list() //List of datums this type can't coexist with - var/job_rank + /// The BAN_ROLE_X key used for this antagonist. + var/banning_key var/give_objectives = TRUE //Should the default objectives be generated? var/replace_banned = TRUE //Should replace jobbanned player with ghosts if granted. var/list/objectives = list() @@ -114,12 +115,12 @@ GLOBAL_LIST(admin_antag_list) /datum/antagonist/proc/is_banned(mob/M) if(!M) return FALSE - . = (is_banned_from(M.ckey, list(ROLE_SYNDICATE, job_rank)) || QDELETED(M)) + . = (is_banned_from(M.ckey, banning_key) || QDELETED(M)) /datum/antagonist/proc/replace_banned_player() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [name]?", job_rank, null, job_rank, 50, owner.current) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [name]?", banning_key, null, null, 50, owner.current) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!") @@ -127,6 +128,7 @@ GLOBAL_LIST(admin_antag_list) owner.current.ghostize(FALSE) owner.current.key = C.key else + owenr.current.playable_bantype = banning_key owner.current.ghostize(FALSE,SENTIENCE_FORCE) ///Called by the remove_antag_datum() and remove_all_antag_datums() mind procs for the antag datum to handle its own removal and deletion. @@ -256,14 +258,6 @@ GLOBAL_LIST(admin_antag_list) /datum/antagonist/proc/antag_panel_data() return "" -/datum/antagonist/proc/enabled_in_preferences(datum/mind/M) - if(job_rank) - if(M.current && M.current.client && (job_rank in M.current.client.prefs.be_special)) - return TRUE - else - return FALSE - return TRUE - // List if ["Command"] = CALLBACK(), user will be appeneded to callback arguments on execution /datum/antagonist/proc/get_admin_commands() . = list() diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm index a626b743f3ad4..6de246bf77e2f 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -58,7 +58,7 @@ if(used) to_chat(H, "You already used this contract!") return - var/list/candidates = pollCandidatesForMob("Do you want to play as a wizard's [href_list["school"]] apprentice?", ROLE_WIZARD, null, ROLE_WIZARD, 150, src) + var/list/candidates = pollGhostCandidates("Do you want to play as a wizard's [href_list["school"]] apprentice?", BAN_ROLE_WIZARD, null, /datum/role_preference/midround_ghost/wizard, 150) if(LAZYLEN(candidates)) if(QDELETED(src)) return @@ -120,7 +120,7 @@ return to_chat(user, "You activate [src] and wait for confirmation.") - var/list/nuke_candidates = pollGhostCandidates("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", ROLE_OPERATIVE, null, ROLE_OPERATIVE, 150, POLL_IGNORE_SYNDICATE) + var/list/nuke_candidates = pollGhostCandidates("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", BAN_ROLE_OPERATIVE, null, /datum/role_preference/midround_ghost/nuclear_operative, 150) if(LAZYLEN(nuke_candidates)) if(QDELETED(src) || !check_usability(user)) return @@ -240,7 +240,7 @@ return if(used) return - var/list/candidates = pollCandidatesForMob("Do you want to play as a [initial(demon_type.name)]?", ROLE_ALIEN, null, ROLE_ALIEN, 50, src) + var/list/candidates = pollCandidatesForMob("Do you want to play as a [initial(demon_type.name)]?", BAN_ROLE_SLAUGHTER_DEMON, null, null, 50, src) if(LAZYLEN(candidates)) if(used || QDELETED(src)) return @@ -292,7 +292,7 @@ return to_chat(user, "You activate [src] and wait for confirmation.") - var/list/candidates = pollGhostCandidates("Do you want to play as a gangster reinforcements?", ROLE_GANG, null, ROLE_GANG, 150) + var/list/candidates = pollGhostCandidates("Do you want to play as a gangster reinforcements?", BAN_ROLE_GANG, null, /datum/role_preference/antagonist/gangster, 150) if(LAZYLEN(candidates)) if(QDELETED(src) || !check_usability(user)) return diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm index cc68f75c40ec7..0353fac388ad6 100644 --- a/code/modules/antagonists/abductor/abductor.dm +++ b/code/modules/antagonists/abductor/abductor.dm @@ -4,7 +4,7 @@ name = "Abductor" roundend_category = "abductors" antagpanel_category = "Abductor" - job_rank = ROLE_ABDUCTOR + banning_key = BAN_ROLE_ABDUCTOR show_in_antagpanel = FALSE //should only show subtypes show_to_ghosts = TRUE var/datum/team/abductor_team/team diff --git a/code/modules/antagonists/ashwalker/ashwalker.dm b/code/modules/antagonists/ashwalker/ashwalker.dm index b7b187cf1b885..1e0e54b8e9037 100644 --- a/code/modules/antagonists/ashwalker/ashwalker.dm +++ b/code/modules/antagonists/ashwalker/ashwalker.dm @@ -4,7 +4,7 @@ /datum/antagonist/ashwalker name = "Ash Walker" - job_rank = ROLE_LAVALAND + banning_key = BAN_ROLE_ASHKWALKER show_in_antagpanel = FALSE show_to_ghosts = TRUE prevent_roundtype_conversion = FALSE diff --git a/code/modules/antagonists/blob/blob.dm b/code/modules/antagonists/blob/blob.dm index 9c58e95630bb8..f8cc423d127f5 100644 --- a/code/modules/antagonists/blob/blob.dm +++ b/code/modules/antagonists/blob/blob.dm @@ -3,7 +3,7 @@ roundend_category = "blobs" antagpanel_category = "Blob" show_to_ghosts = TRUE - job_rank = ROLE_BLOB + banning_key = BAN_ROLE_BLOB var/datum/action/innate/blobpop/pop_action var/starting_points_human_blob = 60 diff --git a/code/modules/antagonists/blob/blob_mobs.dm b/code/modules/antagonists/blob/blob_mobs.dm index 13dff9cfa8400..4cd1d903fb30b 100644 --- a/code/modules/antagonists/blob/blob_mobs.dm +++ b/code/modules/antagonists/blob/blob_mobs.dm @@ -7,7 +7,7 @@ /mob/living/simple_animal/hostile/blob icon = 'icons/mob/blob.dmi' pass_flags = PASSBLOB - faction = list(ROLE_BLOB) + faction = list(FACTION_BLOB) bubble_icon = "blob" speak_emote = null //so we use verb_yell/verb_say/etc atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) @@ -163,11 +163,10 @@ H.update_hair() H.forceMove(src) oldguy = H - role = ROLE_BLOB update_icons() visible_message("The corpse of [H.name] suddenly rises!") if(!key) - set_playable() + set_playable(BAN_ROLE_BLOB) /mob/living/simple_animal/hostile/blob/blobspore/death(gibbed) // On death, create a small smoke of harmful gas (s-Acid) diff --git a/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm b/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm index f0e47095fbd00..b4b95e46dfd26 100644 --- a/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm +++ b/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm @@ -31,7 +31,7 @@ var/obj/effect/temp_visual/explosion/fast/E = new /obj/effect/temp_visual/explosion/fast(get_turf(M)) E.alpha = 150 for(var/mob/living/L in ohearers(1, get_turf(M))) - if(ROLE_BLOB in L.faction) //no friendly fire + if(FACTION_BLOB in L.faction) //no friendly fire continue var/aoe_volume = ..(L, TOUCH, initial_volume, 0, L.get_permeability_protection(), O) L.apply_damage(0.4*aoe_volume, BRUTE) diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm index c03a54acbc117..46885b1175a46 100644 --- a/code/modules/antagonists/blob/overmind.dm +++ b/code/modules/antagonists/blob/overmind.dm @@ -18,7 +18,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) layer = FLY_LAYER pass_flags = PASSBLOB - faction = list(ROLE_BLOB) + faction = list(FACTION_BLOB) lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE hud_type = /datum/hud/blob_overmind var/obj/structure/blob/core/blob_core = null // The blob overmind's core @@ -146,7 +146,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) if(!(Ablob.area_flags & BLOBS_ALLOWED)) continue - if(!(ROLE_BLOB in L.faction)) + if(!(FACTION_BLOB in L.faction)) playsound(L, 'sound/effects/splat.ogg', 50, 1) L.death() new/mob/living/simple_animal/hostile/blob/blobspore(T) diff --git a/code/modules/antagonists/blob/powers.dm b/code/modules/antagonists/blob/powers.dm index bf9a28e4ea228..6be3fa4c4d30b 100644 --- a/code/modules/antagonists/blob/powers.dm +++ b/code/modules/antagonists/blob/powers.dm @@ -16,13 +16,13 @@ if(!placement_override) if(!pop_override) for(var/mob/living/M in range(7, src)) - if(ROLE_BLOB in M.faction) + if(FACTION_BLOB in M.faction) continue if(M.client) to_chat(src, "There is someone too close to place your blob core!") return FALSE for(var/mob/living/M in hearers(13, src)) - if(ROLE_BLOB in M.faction) + if(FACTION_BLOB in M.faction) continue if(M.client) to_chat(src, "Someone could see your blob core from here!") @@ -175,7 +175,7 @@ B.naut = TRUE //temporary placeholder to prevent creation of more than one per factory. to_chat(src, "You attempt to produce a blobbernaut.") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a [blobstrain.name] blobbernaut?", ROLE_BLOB, null, ROLE_BLOB, 50) //players must answer rapidly + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a [blobstrain.name] blobbernaut?", BAN_ROLE_BLOB, null, /datum/role_preference/midround_ghost/blob, 50) //players must answer rapidly if(LAZYLEN(candidates)) //if we got at least one candidate, they're a blobbernaut now. B.max_integrity = initial(B.max_integrity) * 0.25 //factories that produced a blobbernaut have much lower health B.obj_integrity = min(B.obj_integrity, B.max_integrity) @@ -270,7 +270,7 @@ if(can_buy(BLOB_SPREAD_COST)) var/attacksuccess = FALSE for(var/mob/living/L in T) - if(ROLE_BLOB in L.faction) //no friendly/dead fire + if(FACTION_BLOB in L.faction) //no friendly/dead fire continue if(L.stat != DEAD) attacksuccess = TRUE diff --git a/code/modules/antagonists/blob/structures/_blob.dm b/code/modules/antagonists/blob/structures/_blob.dm index 30538152c2679..21bfe98511b8d 100644 --- a/code/modules/antagonists/blob/structures/_blob.dm +++ b/code/modules/antagonists/blob/structures/_blob.dm @@ -253,7 +253,7 @@ /obj/structure/blob/attack_animal(mob/living/simple_animal/M) - if(ROLE_BLOB in M.faction) //sorry, but you can't kill the blob as a blobbernaut + if(FACTION_BLOB in M.faction) //sorry, but you can't kill the blob as a blobbernaut return ..() diff --git a/code/modules/antagonists/brainwashing/brainwashing.dm b/code/modules/antagonists/brainwashing/brainwashing.dm index 5dacc79c7a775..5106595d00f44 100644 --- a/code/modules/antagonists/brainwashing/brainwashing.dm +++ b/code/modules/antagonists/brainwashing/brainwashing.dm @@ -69,7 +69,7 @@ /datum/antagonist/brainwashed name = "Brainwashed Victim" - job_rank = ROLE_BRAINWASHED + banning_key = BAN_ROLE_BRAINWASHED roundend_category = "brainwashed victims" show_in_antagpanel = TRUE antagpanel_category = "Other" diff --git a/code/modules/antagonists/brother/brother.dm b/code/modules/antagonists/brother/brother.dm index 52c091066986c..78513285dd071 100644 --- a/code/modules/antagonists/brother/brother.dm +++ b/code/modules/antagonists/brother/brother.dm @@ -1,8 +1,7 @@ /datum/antagonist/brother name = "Brother" antagpanel_category = "Brother" - job_rank = ROLE_BROTHER - var/special_role = ROLE_BROTHER + banning_key = BAN_ROLE_BROTHER hijack_speed = 0.5 var/datum/team/brother_team/team antag_moodlet = /datum/mood_event/focused @@ -22,14 +21,14 @@ objectives += team.objectives for(var/datum/objective/O in team.objectives) log_objective(owner, O.explanation_text) - owner.special_role = special_role + owner.special_role = BAN_ROLE_BROTHER finalize_brother() return ..() /datum/antagonist/brother/on_removal() SSticker.mode.brothers -= owner if(owner.current) - to_chat(owner.current,"You are no longer the [special_role]!") + to_chat(owner.current,"You are no longer the Blood Brother!") owner.special_role = null return ..() @@ -56,7 +55,7 @@ /datum/antagonist/brother/greet() var/brother_text = get_brother_names() - to_chat(owner.current, "You are the [owner.special_role] of [brother_text].") + to_chat(owner.current, "You are the Blood Brother of [brother_text].") to_chat(owner.current, "The Syndicate only accepts those that have proven themselves. Prove yourself and prove your [team.member_name]s by completing your objectives together! You and your team are outfitted with communication implants allowing for direct, encrypted communication.") owner.announce_objectives() give_meeting_area() diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm index 46f667d408f7c..c8b5db362d87a 100644 --- a/code/modules/antagonists/changeling/changeling.dm +++ b/code/modules/antagonists/changeling/changeling.dm @@ -6,7 +6,7 @@ name = "Changeling" roundend_category = "changelings" antagpanel_category = "Changeling" - job_rank = ROLE_CHANGELING + banning_key = BAN_ROLE_CHANGELING antag_moodlet = /datum/mood_event/focused hijack_speed = 0.5 var/you_are_greet = TRUE @@ -448,7 +448,7 @@ var/datum/objective/assassinate/kill_objective = new kill_objective.owner = owner if(team_mode) //No backstabbing while in a team - kill_objective.find_target_by_role(role = ROLE_CHANGELING, role_type = TRUE, invert = TRUE) + kill_objective.find_target_by_role(role = BAN_ROLE_CHANGELING, role_type = TRUE, invert = TRUE) else kill_objective.find_target() objectives += kill_objective @@ -457,7 +457,7 @@ var/datum/objective/maroon/maroon_objective = new maroon_objective.owner = owner if(team_mode) - maroon_objective.find_target_by_role(role = ROLE_CHANGELING, role_type = TRUE, invert = TRUE) + maroon_objective.find_target_by_role(role = BAN_ROLE_CHANGELING, role_type = TRUE, invert = TRUE) else maroon_objective.find_target() objectives += maroon_objective @@ -485,7 +485,7 @@ var/datum/objective/escape/escape_with_identity/identity_theft = new identity_theft.owner = owner if(team_mode) - identity_theft.find_target_by_role(role = ROLE_CHANGELING, role_type = TRUE, invert = TRUE) + identity_theft.find_target_by_role(role = BAN_ROLE_CHANGELING, role_type = TRUE, invert = TRUE) else identity_theft.find_target() objectives += identity_theft diff --git a/code/modules/antagonists/changeling/powers/teratoma.dm b/code/modules/antagonists/changeling/powers/teratoma.dm index 482ac09834b0c..56b0fd907ebff 100644 --- a/code/modules/antagonists/changeling/powers/teratoma.dm +++ b/code/modules/antagonists/changeling/powers/teratoma.dm @@ -23,7 +23,7 @@ var/datum/antagonist/changeling/c = user.mind.has_antag_datum(/datum/antagonist/changeling) c.chem_charges -= chemical_cost //I'm taking your chemicals hostage! var/turf/A = get_turf(user) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a living teratoma?", ROLE_TERATOMA, null, ROLE_TERATOMA, 5 SECONDS) //players must answer rapidly + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a living teratoma?", BAN_ROLE_TERATOMA, null, null, 5 SECONDS, POLL_IGNORE_TERATOMA) //players must answer rapidly if(!LAZYLEN(candidates)) //if we got at least one candidate, they're teratoma now to_chat(usr, "You fail at creating a tumor. Perhaps you should try again later?") c.chem_charges += chemical_cost //If it fails we want to refund the chemicals diff --git a/code/modules/antagonists/changeling/teratoma.dm b/code/modules/antagonists/changeling/teratoma.dm index e304ae4383c15..74418878bda9a 100644 --- a/code/modules/antagonists/changeling/teratoma.dm +++ b/code/modules/antagonists/changeling/teratoma.dm @@ -2,7 +2,7 @@ name = "Teratoma" roundend_category = "other" antagpanel_category = "Changeling" - job_rank = ROLE_TERATOMA + banning_key = BAN_ROLE_TERATOMA /datum/antagonist/teratoma/on_gain() owner.special_role = "Teratoma" @@ -45,7 +45,6 @@ name = "Maintenance Teratoma" roundend_category = "other" antagpanel_category = "Changeling" - job_rank = ROLE_TERATOMA /datum/antagonist/teratoma/hugbox/greet() ..() diff --git a/code/modules/antagonists/clock_cult/mobs/cogscarab.dm b/code/modules/antagonists/clock_cult/mobs/cogscarab.dm index 108aad10c9d04..f92bca1a3a922 100644 --- a/code/modules/antagonists/clock_cult/mobs/cogscarab.dm +++ b/code/modules/antagonists/clock_cult/mobs/cogscarab.dm @@ -63,7 +63,7 @@ GLOBAL_LIST_INIT(cogscarabs, list()) to construct and maintain defenses at the City of Cogs." /obj/effect/mob_spawn/drone/cogscarab/attack_ghost(mob/user) - if(is_banned_from(user.ckey, ROLE_SERVANT_OF_RATVAR) || QDELETED(src) || QDELETED(user)) + if(is_banned_from(user.ckey, BAN_ROLE_SERVANT_OF_RATVAR) || QDELETED(src) || QDELETED(user)) return if(CONFIG_GET(flag/use_age_restriction_for_jobs)) if(!isnum(user.client.player_age)) //apparently what happens when there's no DB connected. just don't let anybody be a drone without admin intervention diff --git a/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm b/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm index e4ce19d5f044f..039e96d3e7d30 100644 --- a/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm +++ b/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm @@ -53,7 +53,7 @@ if(M.mind) M.mind.grab_ghost(TRUE) else - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [M.name], an inactive clock cultist?", ROLE_SERVANT_OF_RATVAR, null, ROLE_SERVANT_OF_RATVAR, 50, M) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [M.name], an inactive clock cultist?", BAN_ROLE_SERVANT_OF_RATVAR, null, /datum/role_preference/antagonist/clock_cultist, 50, M) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(M)]) to replace an AFK player.") diff --git a/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm b/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm index ef41ed2bb6136..f0168dadbda6d 100644 --- a/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm +++ b/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm @@ -17,7 +17,7 @@ var/mob/dead/observer/selected /datum/clockcult/scripture/marauder/invoke() - candidates = pollGhostCandidates("Would you like to play as a clockwork marauder?", ROLE_SERVANT_OF_RATVAR, null, null, 100, POLL_IGNORE_CLOCKWORK) + candidates = pollGhostCandidates("Would you like to play as a clockwork marauder?", BAN_ROLE_SERVANT_OF_RATVAR, null, /datum/role_preference/antagonist/clock_cultist, 100, POLL_IGNORE_CLOCKWORK) if(LAZYLEN(candidates)) selected = pick(candidates) if(!selected) diff --git a/code/modules/antagonists/clock_cult/servant_of_ratvar.dm b/code/modules/antagonists/clock_cult/servant_of_ratvar.dm index 470215b269673..5adf47c70291a 100644 --- a/code/modules/antagonists/clock_cult/servant_of_ratvar.dm +++ b/code/modules/antagonists/clock_cult/servant_of_ratvar.dm @@ -7,7 +7,7 @@ roundend_category = "clock cultists" antagpanel_category = "Clockcult" antag_moodlet = /datum/mood_event/cult - job_rank = ROLE_SERVANT_OF_RATVAR + banning_key = BAN_ROLE_SERVANT_OF_RATVAR //The class of the servant var/datum/action/innate/clockcult/transmit/transmit_spell diff --git a/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm b/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm index 3572ef99ce840..ecda97d075145 100644 --- a/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm +++ b/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm @@ -35,7 +35,7 @@ vote_active = FALSE used = TRUE if(!eminence) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the eminence?", ROLE_SERVANT_OF_RATVAR, null, null, 100, POLL_IGNORE_PYROSLIME) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the eminence?", BAN_ROLE_SERVANT_OF_RATVAR, null, /datum/role_preference/antagonist/clock_cultist, 100) if(LAZYLEN(candidates)) eminence = pick(candidates) else diff --git a/code/modules/antagonists/creep/creep.dm b/code/modules/antagonists/creep/creep.dm index 7cb73e81f7611..d295623d42f55 100644 --- a/code/modules/antagonists/creep/creep.dm +++ b/code/modules/antagonists/creep/creep.dm @@ -2,7 +2,7 @@ name = "Obsessed" show_in_antagpanel = TRUE antagpanel_category = "Other" - job_rank = ROLE_OBSESSED + banning_key = BAN_ROLE_OBSESSED show_name_in_check_antagonists = TRUE roundend_category = "obsessed" count_against_dynamic_roll_chance = FALSE diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm index b0c38e2735522..f8cab78fdf3ac 100644 --- a/code/modules/antagonists/cult/cult.dm +++ b/code/modules/antagonists/cult/cult.dm @@ -8,7 +8,7 @@ var/datum/action/innate/cult/comm/communion = new var/datum/action/innate/cult/mastervote/vote = new var/datum/action/innate/cult/blood_magic/magic = new - job_rank = ROLE_CULTIST + banning_key = BAN_ROLE_CULTIST var/ignore_implant = FALSE var/give_equipment = FALSE var/datum/team/cult/cult_team diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm index 75a75cfc18b01..70714d2cdb3e7 100644 --- a/code/modules/antagonists/cult/runes.dm +++ b/code/modules/antagonists/cult/runes.dm @@ -279,7 +279,7 @@ structure_check() searches for nearby cultist structures required for the invoca "AAAAAAAAAAAAAA-") SSticker.mode.add_cultist(convertee.mind, 1) new /obj/item/melee/cultblade/dagger(get_turf(src)) - convertee.mind.special_role = ROLE_CULTIST + convertee.mind.special_role = BAN_ROLE_CULTIST to_chat(convertee, "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible, truth. The veil of reality has been ripped away \ and something evil takes root.") to_chat(convertee, "Assist your new compatriots in their dark dealings. Your goal is theirs, and theirs is yours. You serve the Geometer above all else. Bring it back.\ @@ -608,7 +608,7 @@ structure_check() searches for nearby cultist structures required for the invoca mob_to_revive.grab_ghost() if(!mob_to_revive.client || mob_to_revive.client.is_afk()) set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [mob_to_revive.name], an inactive blood cultist?", ROLE_CULTIST, null, ROLE_CULTIST, 50, mob_to_revive) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [mob_to_revive.name], an inactive blood cultist?", BAN_ROLE_CULTIST, null, /datum/role_preference/antagonist/blood_cultist, 50, mob_to_revive) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) to_chat(mob_to_revive.mind, "Your physical form has been taken over by another soul due to your inactivity! Ahelp if you wish to regain your form.") @@ -892,7 +892,7 @@ structure_check() searches for nearby cultist structures required for the invoca notify_ghosts("Manifest rune invoked in [get_area(src)].", 'sound/effects/ghost2.ogg', source = src, header = "Manifest rune") var/list/ghosts_on_rune = list() for(var/mob/dead/observer/O in T) - if(O.client && !is_banned_from(O.ckey, ROLE_CULTIST) && !QDELETED(src) && !QDELETED(O)) + if(O.client && !is_banned_from(O.ckey, BAN_ROLE_CULTIST) && !QDELETED(src) && !QDELETED(O)) ghosts_on_rune += O if(!ghosts_on_rune.len) to_chat(user, "There are no spirits near [src]!") diff --git a/code/modules/antagonists/devil/devil.dm b/code/modules/antagonists/devil/devil.dm index d1e0e183082bf..d5303f344a299 100644 --- a/code/modules/antagonists/devil/devil.dm +++ b/code/modules/antagonists/devil/devil.dm @@ -88,7 +88,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master", name = "Devil" roundend_category = "devils" antagpanel_category = "Devil" - job_rank = ROLE_DEVIL + banning_key = BAN_ROLE_DEVIL show_to_ghosts = TRUE var/obligation var/ban diff --git a/code/modules/antagonists/fugitive/fugitive.dm b/code/modules/antagonists/fugitive/fugitive.dm index ac291ece94aff..1b48074dbf972 100644 --- a/code/modules/antagonists/fugitive/fugitive.dm +++ b/code/modules/antagonists/fugitive/fugitive.dm @@ -1,7 +1,7 @@ /datum/antagonist/fugitive name = "Fugitive" roundend_category = "Fugitive" - job_rank = ROLE_FUGITIVE + banning_key = BAN_ROLE_FUGITIVE show_in_antagpanel = TRUE antagpanel_category = "Fugitives" show_to_ghosts = TRUE diff --git a/code/modules/antagonists/fugitive/hunter.dm b/code/modules/antagonists/fugitive/hunter.dm index 2a82b496de3ac..bb3f2805c069a 100644 --- a/code/modules/antagonists/fugitive/hunter.dm +++ b/code/modules/antagonists/fugitive/hunter.dm @@ -1,7 +1,7 @@ /datum/antagonist/fugitive_hunter name = "Fugitive Hunter" roundend_category = "Fugitive" - job_rank = ROLE_FUGITIVE_HUNTER + banning_key = BAN_ROLE_FUGITIVE_HUNTER show_in_antagpanel = TRUE antagpanel_category = "Fugitive Hunters" show_to_ghosts = TRUE diff --git a/code/modules/antagonists/gang/gang.dm b/code/modules/antagonists/gang/gang.dm index 37377021b71d9..a36a0b98f9b9e 100644 --- a/code/modules/antagonists/gang/gang.dm +++ b/code/modules/antagonists/gang/gang.dm @@ -2,7 +2,7 @@ name = "Gangster" roundend_category = "gangsters" can_coexist_with_others = FALSE - job_rank = ROLE_GANG + banning_key = BAN_ROLE_GANG antagpanel_category = "Gang" var/hud_type = "gangster" var/message_name = "Gangster" diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm index 674ecca457248..fe3cb9d6fd80c 100644 --- a/code/modules/antagonists/heretic/heretic_antag.dm +++ b/code/modules/antagonists/heretic/heretic_antag.dm @@ -17,7 +17,7 @@ roundend_category = "Heretics" antagpanel_category = "Heretic" antag_moodlet = /datum/mood_event/heretics - job_rank = ROLE_HERETIC + banning_key = BAN_ROLE_HERETIC /// Whether we've ascended! (Completed one of the final rituals) var/ascended = FALSE /// The path our heretic has chosen. Mostly used for flavor. diff --git a/code/modules/antagonists/heretic/heretic_knowledge.dm b/code/modules/antagonists/heretic/heretic_knowledge.dm index 83166f720c633..79a1f1dbf40f6 100644 --- a/code/modules/antagonists/heretic/heretic_knowledge.dm +++ b/code/modules/antagonists/heretic/heretic_knowledge.dm @@ -285,7 +285,7 @@ animate(summoned, 10 SECONDS, alpha = 155) message_admins("A [summoned.name] is being summoned by [ADMIN_LOOKUPFLW(user)] in [ADMIN_COORDJMP(summoned)].") - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [summoned.real_name]?", ROLE_HERETIC, null, FALSE, 10 SECONDS, summoned) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [summoned.real_name]?", BAN_ROLE_HERETIC, null, null, 10 SECONDS, summoned) if(!LAZYLEN(candidates)) loc.balloon_alert(user, "Ritual failed, no ghosts") animate(summoned, 0.5 SECONDS, alpha = 0) diff --git a/code/modules/antagonists/heretic/heretic_monsters.dm b/code/modules/antagonists/heretic/heretic_monsters.dm index 5dafc1b06c630..ef4c010a5d3c5 100644 --- a/code/modules/antagonists/heretic/heretic_monsters.dm +++ b/code/modules/antagonists/heretic/heretic_monsters.dm @@ -4,7 +4,7 @@ roundend_category = "Heretics" antagpanel_category = "Heretic Beast" antag_moodlet = /datum/mood_event/heretics - job_rank = ROLE_HERETIC + banning_key = BAN_ROLE_HERETIC show_in_antagpanel = FALSE var/antag_hud_type = ANTAG_HUD_HERETIC var/antag_hud_name = "heretic_beast" diff --git a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm index b22051ee5a3e8..5a2b9b5a4cde2 100644 --- a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm @@ -172,7 +172,7 @@ if(!soon_to_be_ghoul.mind || !soon_to_be_ghoul.client) message_admins("[ADMIN_LOOKUPFLW(user)] is creating a voiceless dead of a body with no player.") - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [soon_to_be_ghoul.real_name], a voiceless dead?", ROLE_HERETIC, null, ROLE_HERETIC, 5 SECONDS, soon_to_be_ghoul) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [soon_to_be_ghoul.real_name], a voiceless dead?", BAN_ROLE_HERETIC, null, null, 5 SECONDS, soon_to_be_ghoul) if(!LAZYLEN(candidates)) loc.balloon_alert(user, "Ritual failed, no ghosts") return FALSE diff --git a/code/modules/antagonists/hivemind/hivemind.dm b/code/modules/antagonists/hivemind/hivemind.dm index f7679c4d57abb..403cb93e136a6 100644 --- a/code/modules/antagonists/hivemind/hivemind.dm +++ b/code/modules/antagonists/hivemind/hivemind.dm @@ -2,9 +2,8 @@ name = "Hivemind Host" roundend_category = "hiveminds" antagpanel_category = "Hivemind Host" - job_rank = ROLE_HIVE + banning_key = BAN_ROLE_HIVE antag_moodlet = /datum/mood_event/hivehost - var/special_role = ROLE_HIVE var/list/hivemembers = list() var/list/avessels = list() var/hive_size = 0 @@ -156,7 +155,7 @@ /datum/antagonist/hivemind/on_gain() - owner.special_role = special_role + owner.special_role = BAN_ROLE_HIVE GLOB.hivehosts += src generate_flavour() create_actions() diff --git a/code/modules/antagonists/hivemind/vessel.dm b/code/modules/antagonists/hivemind/vessel.dm index 1a3c550527db3..78a261cd7a2a0 100644 --- a/code/modules/antagonists/hivemind/vessel.dm +++ b/code/modules/antagonists/hivemind/vessel.dm @@ -2,13 +2,12 @@ /datum/antagonist/hivevessel name = "Awoken Vessel" - job_rank = ROLE_BRAINWASHED + banning_key = BAN_ROLE_HIVE_VESSEL roundend_category = "awoken vessels" antagpanel_category = "Other" show_name_in_check_antagonists = TRUE var/hiveID = "Hivemind" var/datum/antagonist/hivemind/master - var/special_role = ROLE_HIVE_VESSEL var/mutable_appearance/glow var/obj/effect/proc_holder/spell/targeted/touch/hive_fist/fist = new show_in_roundend = FALSE @@ -36,7 +35,7 @@ unbrainwash(user, objective) /datum/antagonist/hivevessel/on_gain() - owner.special_role = special_role + owner.special_role = BAN_ROLE_HIVE_VESSEL owner.AddSpell(fist) ..() diff --git a/code/modules/antagonists/hypnotization/hypnotized.dm b/code/modules/antagonists/hypnotization/hypnotized.dm index 0e86265e9baa1..5d30f64b8a7d7 100644 --- a/code/modules/antagonists/hypnotization/hypnotized.dm +++ b/code/modules/antagonists/hypnotization/hypnotized.dm @@ -26,7 +26,7 @@ /datum/antagonist/hypnotized name = "Hypnotized Victim" - job_rank = ROLE_HYPNOTIZED + banning_key = BAN_ROLE_HYPNOTIZED roundend_category = "hypnotized victims" show_in_antagpanel = TRUE antagpanel_category = "Other" diff --git a/code/modules/antagonists/incursion/incursion.dm b/code/modules/antagonists/incursion/incursion.dm index d9ccbb95d0e11..adc696710871a 100644 --- a/code/modules/antagonists/incursion/incursion.dm +++ b/code/modules/antagonists/incursion/incursion.dm @@ -1,8 +1,7 @@ /datum/antagonist/incursion name = "Syndicate Incursion Member" antagpanel_category = "Incursion" - job_rank = ROLE_INCURSION - var/special_role = ROLE_INCURSION + banning_key = BAN_ROLE_INCURSION var/datum/team/incursion/team antag_moodlet = /datum/mood_event/focused hijack_speed = 0.5 @@ -22,7 +21,7 @@ for(var/datum/objective/O in team.objectives) objectives += O log_objective(owner, O.explanation_text) - owner.special_role = special_role + owner.special_role = BAN_ROLE_INCURSION finalize_incursion() return ..() @@ -198,11 +197,11 @@ /datum/team/incursion/proc/generate_traitor_kill_objective(list/restricted_jobs) //Spawn someone as a traitor - var/list/datum/mind/people = SSticker.mode.get_alive_non_antagonsist_players_for_role(ROLE_EXCOMM, restricted_jobs) + var/list/datum/mind/people = SSticker.mode.get_alive_non_antagonsist_players_for_role(BAN_ROLE_EXCOMM, /datum/role_preference/antagonist/excommunicate, restricted_jobs) if(!LAZYLEN(people)) log_game("Not enough players for incursion role. [LAZYLEN(people)]") return - var/datum/mind/target = SSticker.mode.antag_pick(people, ROLE_EXCOMM) + var/datum/mind/target = SSticker.mode.antag_pick(people) if(!target) log_game("No mind selected.") return diff --git a/code/modules/antagonists/morph/morph.dm b/code/modules/antagonists/morph/morph.dm index 6a1981f4d2917..4f28a0313e4fa 100644 --- a/code/modules/antagonists/morph/morph.dm +++ b/code/modules/antagonists/morph/morph.dm @@ -283,10 +283,10 @@ /datum/round_event/ghost_role/morph minimum_required = 1 - role_name = ROLE_MORPH + role_name = BAN_ROLE_MORPH /datum/round_event/ghost_role/morph/spawn_role() - var/list/candidates = get_candidates(ROLE_MORPH, null, ROLE_MORPH) + var/list/candidates = get_candidates(BAN_ROLE_MORPH, null, /datum/role_preference/midround_ghost/morph) if(!candidates.len) return NOT_ENOUGH_PLAYERS @@ -298,8 +298,8 @@ return MAP_ERROR var/mob/living/simple_animal/hostile/morph/S = new /mob/living/simple_animal/hostile/morph(pick(GLOB.xeno_spawn)) player_mind.transfer_to(S) - player_mind.assigned_role = ROLE_MORPH - player_mind.special_role = ROLE_MORPH + player_mind.assigned_role = BAN_ROLE_MORPH + player_mind.special_role = BAN_ROLE_MORPH player_mind.add_antag_datum(/datum/antagonist/morph) to_chat(S, S.playstyle_string) SEND_SOUND(S, sound('sound/magic/mutate.ogg')) diff --git a/code/modules/antagonists/morph/morph_antag.dm b/code/modules/antagonists/morph/morph_antag.dm index 7a2f3f7b58f86..cc9fb8fe6b210 100644 --- a/code/modules/antagonists/morph/morph_antag.dm +++ b/code/modules/antagonists/morph/morph_antag.dm @@ -2,7 +2,7 @@ name = "Morph" show_name_in_check_antagonists = TRUE show_to_ghosts = TRUE - job_rank = ROLE_MORPH + banning_key = BAN_ROLE_MORPH antagpanel_category = "Morph" show_name_in_check_antagonists = TRUE @@ -51,8 +51,8 @@ if(istype(new_mob)) new_mob.a_intent = INTENT_HARM M.mind.transfer_to(new_mob) - new_owner.assigned_role = ROLE_MORPH - new_owner.special_role = ROLE_MORPH + new_owner.assigned_role = BAN_ROLE_MORPH + new_owner.special_role = BAN_ROLE_MORPH new_mob.name = "morph" new_mob.real_name = "morph" qdel(M) diff --git a/code/modules/antagonists/nightmare/nightmare.dm b/code/modules/antagonists/nightmare/nightmare.dm index 3347125ad17ff..a408f23d67641 100644 --- a/code/modules/antagonists/nightmare/nightmare.dm +++ b/code/modules/antagonists/nightmare/nightmare.dm @@ -2,7 +2,7 @@ name = "Nightmare" show_in_antagpanel = TRUE show_to_ghosts = TRUE - job_rank = ROLE_NIGHTMARE + banning_key = BAN_ROLE_NIGHTMARE antagpanel_category = "Nightmare" show_name_in_check_antagonists = TRUE @@ -45,6 +45,6 @@ var/mob/living/carbon/C = new_owner.current if(alert(admin,"Transform the player into a nightmare?","Species Change","Yes","No") == "Yes") C.set_species(/datum/species/shadow/nightmare) - new_owner.assigned_role = ROLE_NIGHTMARE - new_owner.special_role = ROLE_NIGHTMARE + new_owner.assigned_role = BAN_ROLE_NIGHTMARE + new_owner.special_role = BAN_ROLE_NIGHTMARE return ..() diff --git a/code/modules/antagonists/ninja/ninja.dm b/code/modules/antagonists/ninja/ninja.dm index 03b0f9042c81c..f1c245de69db4 100644 --- a/code/modules/antagonists/ninja/ninja.dm +++ b/code/modules/antagonists/ninja/ninja.dm @@ -1,7 +1,7 @@ /datum/antagonist/ninja name = "Ninja" antagpanel_category = "Ninja" - job_rank = ROLE_NINJA + banning_key = BAN_ROLE_NINJA show_name_in_check_antagonists = TRUE show_to_ghosts = TRUE antag_moodlet = /datum/mood_event/focused @@ -109,8 +109,8 @@ . = ..() /datum/antagonist/ninja/admin_add(datum/mind/new_owner,mob/admin) - new_owner.assigned_role = ROLE_NINJA - new_owner.special_role = ROLE_NINJA + new_owner.assigned_role = BAN_ROLE_NINJA + new_owner.special_role = BAN_ROLE_NINJA new_owner.add_antag_datum(src) message_admins("[key_name_admin(admin)] has ninja'd [key_name_admin(new_owner)].") log_admin("[key_name(admin)] has ninja'd [key_name(new_owner)].") diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm index 6dc05be9964ec..d7d4bb08e7ce8 100644 --- a/code/modules/antagonists/nukeop/nukeop.dm +++ b/code/modules/antagonists/nukeop/nukeop.dm @@ -2,7 +2,7 @@ name = "Nuclear Operative" roundend_category = "syndicate operatives" //just in case antagpanel_category = "NukeOp" - job_rank = ROLE_OPERATIVE + banning_key = BAN_ROLE_OPERATIVE antag_moodlet = /datum/mood_event/focused show_to_ghosts = TRUE hijack_speed = 2 //If you can't take out the station, take the shuttle instead. @@ -144,7 +144,7 @@ nuke_team = new_team /datum/antagonist/nukeop/admin_add(datum/mind/new_owner,mob/admin) - new_owner.assigned_role = ROLE_SYNDICATE + new_owner.assigned_role = BAN_ROLE_OPERATIVE new_owner.add_antag_datum(src) message_admins("[key_name_admin(admin)] has nuke op'ed [key_name_admin(new_owner)].") log_admin("[key_name(admin)] has nuke op'ed [key_name(new_owner)].") diff --git a/code/modules/antagonists/overthrow/overthrow.dm b/code/modules/antagonists/overthrow/overthrow.dm index eeb259298913e..725d243f92d9a 100644 --- a/code/modules/antagonists/overthrow/overthrow.dm +++ b/code/modules/antagonists/overthrow/overthrow.dm @@ -9,7 +9,7 @@ name = "Syndicate mutineer" roundend_category = "syndicate mutineers" antagpanel_category = "Syndicate Mutineers" - job_rank = ROLE_TRAITOR // simply use the traitor preference & jobban settings + banning_key = BAN_ROLE_OVERTHROW var/datum/team/overthrow/team var/static/list/possible_useful_items @@ -29,7 +29,7 @@ ..() owner.announce_objectives() equip_overthrow() - owner.special_role = ROLE_OVERTHROW + owner.special_role = BAN_ROLE_OVERTHROW for(var/datum/objective/O in team.objectives) log_objective(owner, O.explanation_text) diff --git a/code/modules/antagonists/pirate/pirate.dm b/code/modules/antagonists/pirate/pirate.dm index 87a862377abe9..27dcfdfe5cde2 100644 --- a/code/modules/antagonists/pirate/pirate.dm +++ b/code/modules/antagonists/pirate/pirate.dm @@ -1,6 +1,6 @@ /datum/antagonist/pirate name = "Space Pirate" - job_rank = ROLE_TRAITOR + banning_key = BAN_ROLE_SPACE_PIRATE roundend_category = "space pirates" antagpanel_category = "Pirate" show_to_ghosts = TRUE diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm index a64ad3051ccc7..cd7eaba634740 100644 --- a/code/modules/antagonists/revenant/revenant.dm +++ b/code/modules/antagonists/revenant/revenant.dm @@ -123,8 +123,8 @@ to_chat(src, "Be sure to read the wiki page to learn more.") if(!generated_objectives_and_spells) generated_objectives_and_spells = TRUE - mind.assigned_role = ROLE_REVENANT - mind.special_role = ROLE_REVENANT + mind.assigned_role = BAN_ROLE_REVENANT + mind.special_role = BAN_ROLE_REVENANT SEND_SOUND(src, sound('sound/effects/ghost.ogg')) mind.add_antag_datum(/datum/antagonist/revenant) @@ -494,7 +494,7 @@ break if(!key_of_revenant) message_admins("The new revenant's old client either could not be found or is in a new, living mob - grabbing a random candidate instead...") - var/list/candidates = pollCandidatesForMob("Do you want to be [revenant.name] (reforming)?", ROLE_REVENANT, null, ROLE_REVENANT, 50, revenant) + var/list/candidates = pollCandidatesForMob("Do you want to be [revenant.name] (reforming)?", BAN_ROLE_REVENANT, null, /datum/role_preference/midround_ghost/revenant, 50, revenant) if(!LAZYLEN(candidates)) qdel(revenant) message_admins("No candidates were found for the new revenant. Oh well!") diff --git a/code/modules/antagonists/revenant/revenant_spawn_event.dm b/code/modules/antagonists/revenant/revenant_spawn_event.dm index 10ee621a9b87f..78fe247a779d5 100644 --- a/code/modules/antagonists/revenant/revenant_spawn_event.dm +++ b/code/modules/antagonists/revenant/revenant_spawn_event.dm @@ -26,7 +26,7 @@ message_admins("Event attempted to spawn a revenant, but there were only [deadMobs]/[REVENANT_SPAWN_THRESHOLD] dead mobs.") return WAITING_FOR_SOMETHING - var/list/candidates = get_candidates(ROLE_REVENANT, null, ROLE_REVENANT) + var/list/candidates = get_candidates(BAN_ROLE_REVENANT, null, /datum/role_preference/midround_ghost/revenant) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm index d5dce9e21efef..fa09bd74f1522 100644 --- a/code/modules/antagonists/revolution/revolution.dm +++ b/code/modules/antagonists/revolution/revolution.dm @@ -7,7 +7,7 @@ name = "Revolutionary" roundend_category = "revolutionaries" // if by some miracle revolutionaries without revolution happen antagpanel_category = "Revolution" - job_rank = ROLE_REV + banning_key = BAN_ROLE_REV antag_moodlet = /datum/mood_event/revolution var/hud_type = "rev" var/datum/team/revolution/rev_team @@ -159,6 +159,7 @@ /datum/antagonist/rev/head name = "Head Revolutionary" hud_type = "rev_head" + banning_key = BAN_ROLE_REV_HEAD var/remove_clumsy = FALSE var/give_flash = FALSE var/give_hud = TRUE @@ -196,7 +197,7 @@ carbon_mob.flash_act(1, 1) rev_mind.current.Stun(100) rev_mind.add_antag_datum(/datum/antagonist/rev,rev_team) - rev_mind.special_role = ROLE_REV + rev_mind.special_role = BAN_ROLE_REV return TRUE /datum/antagonist/rev/head/proc/demote() @@ -342,7 +343,7 @@ var/list/datum/mind/nonhuman_promotable = list() for(var/datum/mind/khrushchev in non_heads) if(khrushchev.current && !khrushchev.current.incapacitated() && !khrushchev.current.restrained() && khrushchev.current.client && khrushchev.current.stat != DEAD) - if(ROLE_REV in khrushchev.current.client.prefs.be_special) + if(role_preference_enabled(khrushchev.current.client, /datum/role_preference/antagonist/revolutionary)) if(ishuman(khrushchev.current)) promotable += khrushchev else diff --git a/code/modules/antagonists/role_preference/_role_preference.dm b/code/modules/antagonists/role_preference/_role_preference.dm index 415375b67e580..2d79cb6eee893 100644 --- a/code/modules/antagonists/role_preference/_role_preference.dm +++ b/code/modules/antagonists/role_preference/_role_preference.dm @@ -2,14 +2,10 @@ var/name /// A brief description of this role, to display in the preferences menu. var/description - /// The ROLE_X define for this preference. Optional, but must have a poll_ignore_key otherwise. - var/role_key /// The main gamemode that spawns this ROLE_X roundstart. /// This is used to get exp_living requirements by the prefs menu. /// TODO tgui-prefs replace this var/gamemode - /// The POLL_IGNORE_X define for this preference. Optional, but must have a role_key otherwise. - var/poll_ignore_key /// What heading to display this entry under in the preferences menu. Use ROLE_PREFERENCE_CATEGORY defines. var/category /// If this preference is enabled by default. This should be true for ghost polled antagonists, diff --git a/code/modules/antagonists/role_preference/role_changeling.dm b/code/modules/antagonists/role_preference/role_changeling.dm index 7ea7c463d6d83..d22f7a4d4df60 100644 --- a/code/modules/antagonists/role_preference/role_changeling.dm +++ b/code/modules/antagonists/role_preference/role_changeling.dm @@ -3,5 +3,4 @@ description = "A highly intelligent alien predator that is capable of altering their \ shape to flawlessly resemble a human. Transform yourself or others into different identities, and buy from an \ arsenal of biological weaponry with the DNA you collect." - role_key = ROLE_CHANGELING gamemode = /datum/game_mode/changeling diff --git a/code/modules/antagonists/role_preference/role_operative.dm b/code/modules/antagonists/role_preference/role_operative.dm index a2646a4a0a1f3..65cf3a63bdb9d 100644 --- a/code/modules/antagonists/role_preference/role_operative.dm +++ b/code/modules/antagonists/role_preference/role_operative.dm @@ -8,7 +8,6 @@ /datum/role_preference/antagonist/nuclear_operative name = "Nuclear Operative" description = OPERATIVE_DESC - role_key = ROLE_OPERATIVE gamemode = /datum/game_mode/nuclear /datum/role_preference/midround_ghost/nuclear_operative diff --git a/code/modules/antagonists/role_preference/role_traitor.dm b/code/modules/antagonists/role_preference/role_traitor.dm index a1a67e8766711..a50da0c88f3e4 100644 --- a/code/modules/antagonists/role_preference/role_traitor.dm +++ b/code/modules/antagonists/role_preference/role_traitor.dm @@ -6,12 +6,10 @@ /datum/role_preference/antagonist/traitor name = "Traitor" description = TRAITOR_DESC - role_key = ROLE_TRAITOR gamemode = /datum/game_mode/traitor /datum/role_preference/midround_living/traitor name = "Traitor (Sleeper Agent)" description = TRAITOR_DESC - role_key = ROLE_TRAITOR #undef TRAITOR_DESC diff --git a/code/modules/antagonists/role_preference/role_wizard.dm b/code/modules/antagonists/role_preference/role_wizard.dm index cb48535f95f7d..35b283e7ed400 100644 --- a/code/modules/antagonists/role_preference/role_wizard.dm +++ b/code/modules/antagonists/role_preference/role_wizard.dm @@ -2,5 +2,4 @@ name = "Wizard" description = "GREETINGS. WE'RE THE WIZARDS OF THE WIZARD'S FEDERATION. \ Choose between a variety of powerful spells in order to cause chaos among Space Station 13." - role_key = ROLE_WIZARD gamemode = /datum/game_mode/wizard diff --git a/code/modules/antagonists/roundstart_special/special_antagonist.dm b/code/modules/antagonists/roundstart_special/special_antagonist.dm index ac104f03975f4..cb84fe03ebd09 100644 --- a/code/modules/antagonists/roundstart_special/special_antagonist.dm +++ b/code/modules/antagonists/roundstart_special/special_antagonist.dm @@ -24,8 +24,9 @@ var/max_occurrences = 1 var/holidayID = "" //Preferences - var/preference_type = ROLE_TRAITOR - var/special_role_flag = null //Will use antag rep if enabled + var/preference_type = /datum/role_preference/antagonist/traitor + /// If we should use antag rep. + var/use_antag_rep = FALSE /datum/special_role/proc/setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) @@ -72,8 +73,8 @@ //The datum associated with the role /datum/antagonist/special - name = "Role that should not be accessable in game." - job_rank = ROLE_SYNDICATE + name = "Role that should not be accessible in game." + banning_key = BAN_ROLE_ALL_ANTAGONISTS show_in_antagpanel = FALSE show_name_in_check_antagonists = FALSE prevent_roundtype_conversion = FALSE diff --git a/code/modules/antagonists/slaughter/slaughter_antag.dm b/code/modules/antagonists/slaughter/slaughter_antag.dm index 20954522736c8..044cdd2f46840 100644 --- a/code/modules/antagonists/slaughter/slaughter_antag.dm +++ b/code/modules/antagonists/slaughter/slaughter_antag.dm @@ -3,7 +3,7 @@ show_name_in_check_antagonists = TRUE var/objective_verb = "Kill" var/datum/mind/summoner - job_rank = ROLE_ALIEN + banning_key = BAN_ROLE_SLAUGHTER_DEMON show_in_antagpanel = FALSE show_to_ghosts = TRUE diff --git a/code/modules/antagonists/slaughter/slaughterevent.dm b/code/modules/antagonists/slaughter/slaughterevent.dm index 8ad463763ce84..eca6cb04dfc70 100644 --- a/code/modules/antagonists/slaughter/slaughterevent.dm +++ b/code/modules/antagonists/slaughter/slaughterevent.dm @@ -14,7 +14,7 @@ role_name = "slaughter demon" /datum/round_event/ghost_role/slaughter/spawn_role() - var/list/candidates = get_candidates(ROLE_ALIEN, null, ROLE_ALIEN) + var/list/candidates = get_candidates(BAN_ROLE_SLAUGHTER_DEMON, null, null, POLL_IGNORE_SLAUGHTER_DEMON) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/antagonists/space_dragon/carp_rift.dm b/code/modules/antagonists/space_dragon/carp_rift.dm index 7d00520c203d8..50a191de441a1 100644 --- a/code/modules/antagonists/space_dragon/carp_rift.dm +++ b/code/modules/antagonists/space_dragon/carp_rift.dm @@ -143,7 +143,7 @@ /obj/structure/carp_rift/attack_ghost(mob/user) . = ..() - if(user?.client.canGhostRole(ROLE_SPACE_DRAGON, TRUE, flags_1)) + if(can_take_ghost_spawner(user?.client, BAN_ROLE_SPACE_DRAGON, TRUE, flags_1 & ADMIN_SPAWNED_1)) summon_carp(user) /** diff --git a/code/modules/antagonists/space_dragon/space_dragon.dm b/code/modules/antagonists/space_dragon/space_dragon.dm index ce6e8c373c6f7..c1237056d0ed4 100644 --- a/code/modules/antagonists/space_dragon/space_dragon.dm +++ b/code/modules/antagonists/space_dragon/space_dragon.dm @@ -2,7 +2,7 @@ name = "Space Dragon" roundend_category = "space dragons" antagpanel_category = "Space Dragon" - job_rank = ROLE_SPACE_DRAGON + banning_key = BAN_ROLE_SPACE_DRAGON show_in_antagpanel = TRUE show_name_in_check_antagonists = TRUE show_to_ghosts = TRUE diff --git a/code/modules/antagonists/spider/spider.dm b/code/modules/antagonists/spider/spider.dm index 34ca526e9df46..4e2c0e45d1169 100644 --- a/code/modules/antagonists/spider/spider.dm +++ b/code/modules/antagonists/spider/spider.dm @@ -43,7 +43,7 @@ /datum/antagonist/spider name = "Spider" - job_rank = ROLE_SPIDER + banning_key = BAN_ROLE_SPIDER show_in_antagpanel = FALSE prevent_roundtype_conversion = FALSE show_to_ghosts = TRUE diff --git a/code/modules/antagonists/swarmer/swarmer.dm b/code/modules/antagonists/swarmer/swarmer.dm index 07792c8f40c7f..e454e4c896a71 100644 --- a/code/modules/antagonists/swarmer/swarmer.dm +++ b/code/modules/antagonists/swarmer/swarmer.dm @@ -18,8 +18,8 @@ mob_name = "a swarmer" death = FALSE roundstart = FALSE - assignedrole = ROLE_SWARMER - banType = ROLE_SWARMER + assignedrole = BAN_ROLE_SWARMER + banType = BAN_ROLE_SWARMER /obj/effect/mob_spawn/swarmer/Initialize(mapload) . = ..() @@ -701,7 +701,7 @@ /datum/antagonist/swarmer name = "Swarmer" - job_rank = ROLE_SWARMER + banning_key = BAN_ROLE_SWARMER roundend_category = "Swarmer" antagpanel_category = "Swarmer" show_to_ghosts = TRUE @@ -787,8 +787,8 @@ if(istype(new_mob)) new_mob.a_intent = INTENT_HARM M.mind.transfer_to(new_mob) - new_owner.assigned_role = ROLE_SWARMER - new_owner.special_role = ROLE_SWARMER + new_owner.assigned_role = BAN_ROLE_SWARMER + new_owner.special_role = BAN_ROLE_SWARMER qdel(M) return ..() diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 4024b9841bd42..5c0f78f442a41 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -5,10 +5,10 @@ name = "Traitor" roundend_category = "traitors" antagpanel_category = "Traitor" - job_rank = ROLE_TRAITOR + banning_key = BAN_ROLE_TRAITOR antag_moodlet = /datum/mood_event/focused hijack_speed = 0.5 //10 seconds per hijack stage by default - var/special_role = ROLE_TRAITOR + var/special_role = BAN_ROLE_TRAITOR var/employer = "The Syndicate" var/should_give_codewords = TRUE var/should_equip = TRUE diff --git a/code/modules/antagonists/traitor/equipment/contractor.dm b/code/modules/antagonists/traitor/equipment/contractor.dm index fefd8e740cda6..bd7298da06fde 100644 --- a/code/modules/antagonists/traitor/equipment/contractor.dm +++ b/code/modules/antagonists/traitor/equipment/contractor.dm @@ -166,7 +166,7 @@ if (.) to_chat(user, "The uplink vibrates quietly, connecting to nearby agents...") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Contractor Support Unit for [user.real_name]?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_CONTRACTOR_SUPPORT) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Contractor Support Unit for [user.real_name]?", BAN_ROLE_CONTRACTOR_SUPPORT_UNIT, null, null, 100, POLL_IGNORE_CONTRACTOR_SUPPORT) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) diff --git a/code/modules/antagonists/traitor/traitor_spawner.dm b/code/modules/antagonists/traitor/traitor_spawner.dm index 7d42fa8fc4f0c..f3c31485d74f6 100644 --- a/code/modules/antagonists/traitor/traitor_spawner.dm +++ b/code/modules/antagonists/traitor/traitor_spawner.dm @@ -13,9 +13,9 @@ allowAntagTargets = TRUE latejoin_allowed = TRUE protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) - - special_role_flag = ROLE_TRAITOR - role_name = ROLE_TRAITOR + role_name = "Traitor" + preference_type = /datum/role_preference/antagonist/traitor + use_antag_rep = TRUE var/traitors_possible = 4 //hard limit on traitors if scaling is turned off diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm index 384c0974ae718..07eaf2e0b9927 100644 --- a/code/modules/antagonists/wizard/equipment/soulstone.dm +++ b/code/modules/antagonists/wizard/equipment/soulstone.dm @@ -338,7 +338,7 @@ break if(!chosen_ghost) //Failing that, we grab a ghost - var/list/consenting_candidates = pollGhostCandidates("Would you like to play as a Shade?", "Cultist", null, ROLE_CULTIST, 50, POLL_IGNORE_SHADE) + var/list/consenting_candidates = pollGhostCandidates("Would you like to play as a Shade?", BAN_ROLE_CULTIST, null, null, 50, POLL_IGNORE_SHADE) if(consenting_candidates.len) chosen_ghost = pick(consenting_candidates) if(!T) diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm index 236ff7d20deed..63b41a4f57964 100644 --- a/code/modules/antagonists/wizard/wizard.dm +++ b/code/modules/antagonists/wizard/wizard.dm @@ -2,7 +2,7 @@ name = "Space Wizard" roundend_category = "wizards/witches" antagpanel_category = "Wizard" - job_rank = ROLE_WIZARD + banning_key = BAN_ROLE_WIZARD antag_moodlet = /datum/mood_event/focused hijack_speed = 0.5 var/strip = TRUE //strip before equipping @@ -177,12 +177,12 @@ /datum/antagonist/wizard/apply_innate_effects(mob/living/mob_override) var/mob/living/M = mob_override || owner.current update_wiz_icons_added(M, wiz_team ? TRUE : FALSE) //Don't bother showing the icon if you're solo wizard - M.faction |= ROLE_WIZARD + M.faction |= FACTION_WIZARD /datum/antagonist/wizard/remove_innate_effects(mob/living/mob_override) var/mob/living/M = mob_override || owner.current update_wiz_icons_removed(M) - M.faction -= ROLE_WIZARD + M.faction -= FACTION_WIZARD /datum/antagonist/wizard/get_admin_commands() diff --git a/code/modules/antagonists/xeno/xeno.dm b/code/modules/antagonists/xeno/xeno.dm index f9dea74e3e014..70aeaabc42354 100644 --- a/code/modules/antagonists/xeno/xeno.dm +++ b/code/modules/antagonists/xeno/xeno.dm @@ -13,7 +13,7 @@ /datum/antagonist/xeno name = "Xenomorph" - job_rank = ROLE_ALIEN + banning_key = BAN_ROLE_ALIEN show_in_antagpanel = FALSE prevent_roundtype_conversion = FALSE show_to_ghosts = TRUE diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index b91b27443e678..cbb58db018434 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -28,7 +28,7 @@ var/mob_color //Change the mob's color var/assignedrole var/show_flavour = TRUE - var/banType = ROLE_LAVALAND + var/banType var/ghost_usable = TRUE var/use_cooldown = FALSE @@ -39,7 +39,7 @@ if(!uses) to_chat(user, "This spawner is out of charges!") return - if(!user?.client.canGhostRole(banType, use_cooldown, flags_1)) + if(!can_take_ghost_spawner(user?.client, banType, use_cooldown, flags_1 & ADMIN_SPAWNED_1)) return if(QDELETED(src) || QDELETED(user)) return @@ -371,43 +371,6 @@ mob_species = /datum/species/plasmaman outfit = /datum/outfit/plasmaman - -/obj/effect/mob_spawn/human/bartender - name = "Space Bartender" - id_job = JOB_NAME_BARTENDER - id_access_list = list(ACCESS_BAR) - outfit = /datum/outfit/spacebartender - -/obj/effect/mob_spawn/human/bartender/alive - death = FALSE - roundstart = FALSE - random = TRUE - name = "bartender sleeper" - icon = 'icons/obj/machines/sleeper.dmi' - icon_state = "sleeper" - short_desc = "You are a space bartender!" - flavour_text = "Time to mix drinks and change lives. Smoking space drugs makes it easier to understand your patrons' odd dialect." - assignedrole = "Space Bartender" - id_job = JOB_NAME_BARTENDER - use_cooldown = TRUE - -/datum/outfit/spacebartender - name = "Space Bartender" - uniform = /obj/item/clothing/under/rank/civilian/bartender - back = /obj/item/storage/backpack - shoes = /obj/item/clothing/shoes/sneakers/black - suit = /obj/item/clothing/suit/armor/vest - glasses = /obj/item/clothing/glasses/sunglasses/advanced/reagent - id = /obj/item/card/id - -/datum/outfit/spacebartender/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - ..() - - if(visualsOnly) - return - - ADD_TRAIT(H, TRAIT_SOMMELIER, ROUNDSTART_TRAIT) - /obj/effect/mob_spawn/human/beach outfit = /datum/outfit/beachbum @@ -423,6 +386,7 @@ flavour_text = "Ch'yea. You came here, like, on spring break, hopin' to pick up some bangin' hot chicks, y'knaw?" assignedrole = "Beach Bum" use_cooldown = TRUE + banType = BAN_ROLE_BEACH_BUM /obj/effect/mob_spawn/human/beach/alive/lifeguard short_desc = "You're a spunky lifeguard!" @@ -501,29 +465,6 @@ back = /obj/item/storage/backpack/security id = /obj/item/card/id/job/security_officer - -/obj/effect/mob_spawn/human/commander/alive - death = FALSE - roundstart = FALSE - mob_name = "\improper Nanotrasen Commander" - name = "sleeper" - icon = 'icons/obj/machines/sleeper.dmi' - icon_state = "sleeper" - short_desc = "You are a Nanotrasen Commander!" - use_cooldown = TRUE - -/obj/effect/mob_spawn/human/nanotrasensoldier/alive - death = FALSE - roundstart = FALSE - mob_name = "Private Security Officer" - name = "sleeper" - icon = 'icons/obj/machines/sleeper.dmi' - icon_state = "sleeper" - faction = "nanotrasenprivate" - short_desc = "You are a Nanotrasen Private Security Officer!" - use_cooldown = TRUE - - /////////////////Spooky Undead////////////////////// /obj/effect/mob_spawn/human/skeleton @@ -538,30 +479,16 @@ icon = 'icons/effects/blood.dmi' icon_state = "remains" short_desc = "By unknown powers, your skeletal remains have been reanimated!" - flavour_text = "Walk this mortal plain and terrorize all living adventurers who dare cross your path." + flavour_text = "Walk this mortal plane and terrorize all living adventurers who dare cross your path." assignedrole = "Skeleton" use_cooldown = TRUE + banType = BAN_ROLE_SKELETAL_REMAINS /obj/effect/mob_spawn/human/skeleton/alive/equip(mob/living/carbon/human/H) var/obj/item/implant/exile/implant = new/obj/item/implant/exile(H) implant.implant(H) H.set_species(/datum/species/skeleton) -/obj/effect/mob_spawn/human/zombie - name = "rotting corpse" - mob_name = "zombie" - mob_species = /datum/species/zombie - assignedrole = "Zombie" - -/obj/effect/mob_spawn/human/zombie/alive - death = FALSE - roundstart = FALSE - icon = 'icons/effects/blood.dmi' - icon_state = "remains" - short_desc = "By unknown powers, your rotting remains have been resurrected!" - flavour_text = "Walk this mortal plain and terrorize all living adventurers who dare cross your path." - use_cooldown = TRUE - /obj/effect/mob_spawn/human/abductor name = "abductor" mob_name = "alien" @@ -573,25 +500,6 @@ uniform = /obj/item/clothing/under/color/grey shoes = /obj/item/clothing/shoes/combat - -//For ghost bar. -/obj/effect/mob_spawn/human/alive/space_bar_patron - name = "Bar cryogenics" - mob_name = "Bar patron" - random = TRUE - permanent = TRUE - uses = -1 - outfit = /datum/outfit/spacebartender - assignedrole = "Space Bar Patron" - -//ATTACK HAND IGNORING PARENT RETURN VALUE -/obj/effect/mob_spawn/human/alive/space_bar_patron/attack_hand(mob/user) - var/despawn = alert("Return to cryosleep? (Warning, Your mob will be deleted!)",,"Yes","No") - if(despawn != "Yes" || !loc || !Adjacent(user)) - return - user.visible_message("[user.name] climbs back into cryosleep...") - qdel(user) - /datum/outfit/cryobartender name = "Cryogenic Bartender" uniform = /obj/item/clothing/under/rank/civilian/bartender diff --git a/code/modules/awaymissions/mission_code/Academy.dm b/code/modules/awaymissions/mission_code/Academy.dm index c5206f1bda077..ae4da29734467 100644 --- a/code/modules/awaymissions/mission_code/Academy.dm +++ b/code/modules/awaymissions/mission_code/Academy.dm @@ -90,7 +90,7 @@ var/mob/living/current_wizard = null var/next_check = 0 var/cooldown = 600 - var/faction = ROLE_WIZARD + var/faction = FACTION_WIZARD var/braindead_check = 0 /obj/structure/academy_wizard_spawner/New() @@ -125,7 +125,7 @@ if(!current_wizard) return - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as Wizard Academy Defender?", ROLE_WIZARD, null, ROLE_WIZARD, 50, current_wizard) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as Wizard Academy Defender?", BAN_ROLE_WIZARD, null, /datum/role_preference/midround_ghost/wizard, 50, current_wizard) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) @@ -133,6 +133,7 @@ current_wizard.ghostize(FALSE) // on the off chance braindead defender gets back in current_wizard.key = C.key else + affected_mob.playable_bantype = BAN_ROLE_WIZARD current_wizard.ghostize(FALSE,SENTIENCE_FORCE) /obj/structure/academy_wizard_spawner/proc/summon_wizard() @@ -313,7 +314,7 @@ A.setup_master(user) servant_mind.transfer_to(H) - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [user.real_name] Servant?", ROLE_WIZARD, null, ROLE_WIZARD, 50, H) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [user.real_name] Servant?", BAN_ROLE_WIZARD, null, /datum/role_preference/midround_ghost/wizard, 50, H) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) message_admins("[ADMIN_LOOKUPFLW(C)] was spawned as Dice Servant") diff --git a/code/modules/awaymissions/mission_code/snowdin.dm b/code/modules/awaymissions/mission_code/snowdin.dm index 58010ba04324d..b60af0f538dea 100644 --- a/code/modules/awaymissions/mission_code/snowdin.dm +++ b/code/modules/awaymissions/mission_code/snowdin.dm @@ -577,7 +577,7 @@ icon_state = "sleeper" roundstart = FALSE death = FALSE - faction = ROLE_SYNDICATE + faction = FACTION_SYNDICATE outfit = /datum/outfit/snowsyndie short_desc = "You are a syndicate operative recently awoken from cryostasis in an underground outpost." flavour_text = "You are a syndicate operative recently awoken from cryostasis in an underground outpost. Monitor Nanotrasen communications and record information. All intruders should be \ diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 0053170bbd11e..c9df9515dc772 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -963,20 +963,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( ..() -/client/proc/canGhostRole(role, use_cooldown = FALSE, spawned_by_admin) - if(!SSticker.HasRoundStarted()) - return FALSE - if(!(GLOB.ghost_role_flags & GHOSTROLE_SPAWNER) && !(spawned_by_admin & ADMIN_SPAWNED_1)) - to_chat(src, "An admin has temporarily disabled non-admin ghost roles!") - return FALSE - if(role && is_banned_from(key, role)) //role can be null, no reason to check for a roleban if there is no special role assigned - to_chat(src, "You are jobanned!") - return FALSE - if(use_cooldown && next_ghost_role_tick > world.time) - to_chat(src, "You have died recently, you must wait [(next_ghost_role_tick - world.time)/10] seconds until you can use a ghost spawner.") - return FALSE - return TRUE - /client/proc/add_verbs_from_config() if (interviewee) return diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 8022884c346cc..948249e4fa6d4 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -734,7 +734,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) else dat += "Be [capitalize(i)]: [(i in be_special) ? "Enabled" : "Disabled"]
" dat += "
" - dat += "Midround Antagonist: [(toggles & PREFTOGGLE_MIDROUND_ANTAG) ? "Enabled" : "Disabled"]
" dat += " " // i hate myself for this dat += "
Customize Keybinds
" @@ -1927,9 +1926,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) if("pull_requests") chat_toggles ^= CHAT_PULLR - if("allow_midround_antag") - toggles ^= PREFTOGGLE_MIDROUND_ANTAG - if("tgui_input") toggles2 ^= PREFTOGGLE_2_TGUI_INPUT diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm index 51dadb924e579..ad8e4353066b1 100644 --- a/code/modules/client/preferences_toggles.dm +++ b/code/modules/client/preferences_toggles.dm @@ -87,15 +87,6 @@ prefs.save_preferences() SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Arrivalrattle", "[!(prefs.toggles & PREFTOGGLE_DISABLE_ARRIVALRATTLE) ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, maybe you should rethink where your life went so wrong. -/client/verb/togglemidroundantag() - set name = "Toggle Midround Antagonist" - set category = "Preferences" - set desc = "Midround Antagonist" - prefs.toggles ^= PREFTOGGLE_MIDROUND_ANTAG - prefs.save_preferences() - to_chat(usr, "You will [(prefs.toggles & PREFTOGGLE_MIDROUND_ANTAG) ? "now" : "no longer"] be considered for midround antagonist positions.") - SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Midround Antag", "[prefs.toggles & PREFTOGGLE_MIDROUND_ANTAG ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - /client/verb/toggletitlemusic() set name = "Hear/Silence Lobby Music" set category = "Preferences" diff --git a/code/modules/clothing/outfits/vr.dm b/code/modules/clothing/outfits/vr.dm index fa02a1aa26404..bfd90cf88257f 100644 --- a/code/modules/clothing/outfits/vr.dm +++ b/code/modules/clothing/outfits/vr.dm @@ -33,7 +33,7 @@ W.implant(H) var/obj/item/implant/explosive/E = new/obj/item/implant/explosive(H) E.implant(H) - H.faction |= ROLE_SYNDICATE + H.faction |= FACTION_SYNDICATE H.update_icons() /obj/item/paper/fluff/vr/fluke_ops diff --git a/code/modules/events/abductor.dm b/code/modules/events/abductor.dm index b089c96b5f561..25bc6ca0b8552 100755 --- a/code/modules/events/abductor.dm +++ b/code/modules/events/abductor.dm @@ -15,7 +15,7 @@ fakeable = FALSE //Nothing to fake here /datum/round_event/ghost_role/abductor/spawn_role() - var/list/mob/dead/observer/candidates = get_candidates(ROLE_ABDUCTOR, null, ROLE_ABDUCTOR) + var/list/mob/dead/observer/candidates = get_candidates(BAN_ROLE_ABDUCTOR, null, /datum/role_preference/midround_ghost/abductor) if(candidates.len < 2) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/alien_infestation.dm b/code/modules/events/alien_infestation.dm index 7637a58d437ef..c453e425cba01 100644 --- a/code/modules/events/alien_infestation.dm +++ b/code/modules/events/alien_infestation.dm @@ -62,7 +62,7 @@ message_admins("An event attempted to spawn an alien but no suitable vents were found. Shutting down.") return MAP_ERROR - var/list/candidates = get_candidates(ROLE_ALIEN, null, ROLE_ALIEN) + var/list/candidates = get_candidates(BAN_ROLE_ALIEN, null, /datum/role_preference/midround_ghost/xenomorph) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/blob.dm b/code/modules/events/blob.dm index 662cab5c991ce..4a3b7eabfc375 100644 --- a/code/modules/events/blob.dm +++ b/code/modules/events/blob.dm @@ -22,7 +22,7 @@ /datum/round_event/ghost_role/blob/spawn_role() if(!GLOB.blobstart.len) return MAP_ERROR - var/list/candidates = get_candidates(ROLE_BLOB, null, ROLE_BLOB) + var/list/candidates = get_candidates(BAN_ROLE_BLOB, null, /datum/role_preference/midround_ghost/blob) if(!candidates.len) return NOT_ENOUGH_PLAYERS var/mob/dead/observer/new_blob = pick(candidates) diff --git a/code/modules/events/creep_awakening.dm b/code/modules/events/creep_awakening.dm index 1b5643bc53f48..be5bdae5544ee 100644 --- a/code/modules/events/creep_awakening.dm +++ b/code/modules/events/creep_awakening.dm @@ -10,7 +10,7 @@ /datum/round_event/obsessed/start() for(var/mob/living/carbon/human/H in shuffle(GLOB.player_list)) - if(!H.client || !(ROLE_OBSESSED in H.client.prefs.be_special)) + if(!H.client || !role_preference_enabled(H.client, /datum/role_preference/midround_living/obsessed)) continue if(H.stat == DEAD) continue diff --git a/code/modules/events/devil.dm b/code/modules/events/devil.dm index 9073171c98c30..5c7ce045ee2c3 100644 --- a/code/modules/events/devil.dm +++ b/code/modules/events/devil.dm @@ -19,7 +19,7 @@ return MAP_ERROR //selecting a candidate player - var/list/candidates = get_candidates(ROLE_DEVIL, null, ROLE_DEVIL) + var/list/candidates = get_candidates(BAN_ROLE_DEVIL, null, /datum/role_preference/midround_ghost/devil) if(!candidates.len) return NOT_ENOUGH_PLAYERS @@ -53,7 +53,7 @@ /proc/create_devil_mind(key) var/datum/mind/Mind = new /datum/mind(key) - Mind.assigned_role = ROLE_DEVIL - Mind.special_role = ROLE_DEVIL + Mind.assigned_role = BAN_ROLE_DEVIL + Mind.special_role = BAN_ROLE_DEVIL SSticker.mode.devils |= Mind return Mind diff --git a/code/modules/events/fugitive_spawning.dm b/code/modules/events/fugitive_spawning.dm index 227567a955655..c7287d6da1b15 100644 --- a/code/modules/events/fugitive_spawning.dm +++ b/code/modules/events/fugitive_spawning.dm @@ -12,7 +12,7 @@ GLOBAL_LIST_EMPTY(fugitive_backstory_selection) /datum/round_event/ghost_role/fugitives minimum_required = 1 - role_name = ROLE_FUGITIVE + role_name = BAN_ROLE_FUGITIVE fakeable = FALSE /datum/round_event/ghost_role/fugitives/spawn_role() @@ -28,12 +28,12 @@ GLOBAL_LIST_EMPTY(fugitive_backstory_selection) message_admins("No valid spawn locations found, aborting...") return MAP_ERROR var/turf/landing_turf = pick(possible_spawns) - var/list/candidates = get_candidates(ROLE_FUGITIVE, null, ROLE_FUGITIVE) + var/list/candidates = get_candidates(BAN_ROLE_FUGITIVE, null, /datum/role_preference/midround_ghost/fugitive) var/result = spawn_fugitives(landing_turf, candidates, spawned_mobs) if(result != SUCCESSFUL_SPAWN) return result // Switch the round event to "hunter" mode - role_name = ROLE_FUGITIVE_HUNTER + role_name = BAN_ROLE_FUGITIVE_HUNTER return SUCCESSFUL_SPAWN /proc/spawn_fugitives(turf/landing_turf, list/candidates, list/spawned_mobs) @@ -75,8 +75,8 @@ GLOBAL_LIST_EMPTY(fugitive_backstory_selection) player_mind.active = TRUE var/mob/living/carbon/human/S = new(landing_turf) player_mind.transfer_to(S) - player_mind.assigned_role = ROLE_FUGITIVE - player_mind.special_role = ROLE_FUGITIVE + player_mind.assigned_role = BAN_ROLE_FUGITIVE + player_mind.special_role = BAN_ROLE_FUGITIVE var/datum/antagonist/fugitive/A = new() A.backstory = backstory player_mind.add_antag_datum(A) @@ -93,7 +93,7 @@ GLOBAL_LIST_EMPTY(fugitive_backstory_selection) /proc/spawn_hunters() set waitfor = FALSE var/datum/fugitive_type/hunter/backstory = GLOB.hunter_types[admin_select_backstory(GLOB.hunter_types)] - var/list/candidates = pollGhostCandidates("The Fugitive Hunters are looking for a [backstory.name]. Would you like to be considered for this role?", ROLE_FUGITIVE_HUNTER) + var/list/candidates = pollGhostCandidates("The Fugitive Hunters are looking for a [backstory.name]. Would you like to be considered for this role?", BAN_ROLE_FUGITIVE_HUNTER, null, /datum/role_preference/midround_ghost/fugitive_hunter) var/datum/map_template/shuttle/ship = new backstory.ship_type var/x = rand(TRANSITIONEDGE,world.maxx - TRANSITIONEDGE - ship.width) var/y = rand(TRANSITIONEDGE,world.maxy - TRANSITIONEDGE - ship.height) diff --git a/code/modules/events/ghost_role.dm b/code/modules/events/ghost_role.dm index 67ef4002e732a..988d0cfe53187 100644 --- a/code/modules/events/ghost_role.dm +++ b/code/modules/events/ghost_role.dm @@ -55,14 +55,14 @@ // players could be found, and just runtime if anything else happens return TRUE -/datum/round_event/ghost_role/proc/get_candidates(jobban, gametypecheck, be_special) +/datum/round_event/ghost_role/proc/get_candidates(banning_key, gametypecheck, role_preference, poll_ignore = null) // Returns a list of candidates in priority order, with candidates from // `priority_candidates` first, and ghost roles randomly shuffled and // appended after var/list/mob/dead/observer/regular_candidates // don't get their hopes up if(priority_candidates.len < minimum_required) - regular_candidates = pollGhostCandidates("Do you wish to be considered for the special role of '[role_name]'?", jobban, gametypecheck, be_special) + regular_candidates = pollGhostCandidates("Do you wish to be considered for the special role of '[role_name]'?", banning_key, gametypecheck, role_preference, ignore_category = poll_ignore) else regular_candidates = list() diff --git a/code/modules/events/nightmare.dm b/code/modules/events/nightmare.dm index 39a9696a006a9..7670c7a634ae5 100644 --- a/code/modules/events/nightmare.dm +++ b/code/modules/events/nightmare.dm @@ -12,7 +12,7 @@ fakeable = FALSE /datum/round_event/ghost_role/nightmare/spawn_role() - var/list/candidates = get_candidates(ROLE_NIGHTMARE, null, ROLE_NIGHTMARE) + var/list/candidates = get_candidates(BAN_ROLE_NIGHTMARE, null, /datum/role_preference/midround_ghost/nightmare) if(!candidates.len) return NOT_ENOUGH_PLAYERS @@ -34,8 +34,8 @@ var/mob/living/carbon/human/S = new ((pick(spawn_locs))) player_mind.transfer_to(S) - player_mind.assigned_role = ROLE_NIGHTMARE - player_mind.special_role = ROLE_NIGHTMARE + player_mind.assigned_role = BAN_ROLE_NIGHTMARE + player_mind.special_role = BAN_ROLE_NIGHTMARE player_mind.add_antag_datum(/datum/antagonist/nightmare) S.set_species(/datum/species/shadow/nightmare) playsound(S, 'sound/magic/ethereal_exit.ogg', 50, 1, -1) diff --git a/code/modules/events/operative.dm b/code/modules/events/operative.dm index e655d8b0e3367..8e5c9a7c44695 100644 --- a/code/modules/events/operative.dm +++ b/code/modules/events/operative.dm @@ -11,7 +11,7 @@ fakeable = FALSE /datum/round_event/ghost_role/operative/spawn_role() - var/list/candidates = get_candidates(ROLE_OPERATIVE, null, ROLE_OPERATIVE) + var/list/candidates = get_candidates(BAN_ROLE_OPERATIVE, null, /datum/role_preference/midround_ghost/nuclear_operative) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm index 1bfe24da18627..489e3fb070cc5 100644 --- a/code/modules/events/pirates.dm +++ b/code/modules/events/pirates.dm @@ -71,7 +71,7 @@ GLOBAL_VAR_INIT(pirates_spawned, FALSE) if(!skip_answer_check && threat?.answered == PIRATE_RESPONSE_PAY) return - var/list/candidates = pollGhostCandidates("Do you wish to be considered for pirate crew?", ROLE_SPACE_PIRATE) + var/list/candidates = pollGhostCandidates("Do you wish to be considered for pirate crew?", BAN_ROLE_SPACE_PIRATE, null, /datum/role_preference/midround_ghost/pirate) shuffle_inplace(candidates) var/datum/map_template/shuttle/pirate/default/ship = new diff --git a/code/modules/events/sentience.dm b/code/modules/events/sentience.dm index 057a99ae64f0b..282189e4f7a21 100644 --- a/code/modules/events/sentience.dm +++ b/code/modules/events/sentience.dm @@ -40,7 +40,7 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list( /datum/round_event/ghost_role/sentience/spawn_role() var/list/mob/dead/observer/candidates - candidates = get_candidates(ROLE_SENTIENCE, null, ROLE_SENTIENCE) + candidates = get_candidates(BAN_ROLE_SENTIENT_ANIMAL, null, null, POLL_IGNORE_SENTIENT_ANIMAL) // find our chosen mob to breathe life into // Mobs have to be simple animals, mindless, on station, and NOT holograms. diff --git a/code/modules/events/space_dragon.dm b/code/modules/events/space_dragon.dm index c53097490b9dc..81714998e221c 100644 --- a/code/modules/events/space_dragon.dm +++ b/code/modules/events/space_dragon.dm @@ -26,7 +26,7 @@ message_admins("No valid spawn locations found, aborting...") return MAP_ERROR - var/list/candidates = get_candidates(ROLE_SPACE_DRAGON, null, ROLE_SPACE_DRAGON) + var/list/candidates = get_candidates(BAN_ROLE_SPACE_DRAGON, null, /datum/role_preference/midround_ghost/space_dragon) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/special_antag_event.dm b/code/modules/events/special_antag_event.dm index 1f08f2ee2827b..2590dd20e23f9 100644 --- a/code/modules/events/special_antag_event.dm +++ b/code/modules/events/special_antag_event.dm @@ -3,9 +3,10 @@ typepath = /datum/round_event/create_special_antag auto_add = FALSE //Antagonist data - var/antagonist_datum = /datum/antagonist/special + var/datum/antagonist/antagonist_datum = /datum/antagonist/special var/antag_name //The datum of the antag E.G. /datum/antagonist/special/undercover - var/preference_type = ROLE_TRAITOR + var/preference_type = /datum/role_preference/antagonist/traitor + var/banning_key = BAN_ROLE_ALL_ANTAGONISTS var/protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_CHIEFMEDICALOFFICER, JOB_NAME_CHIEFENGINEER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CAPTAIN) /datum/round_event_control/spawn_special_antagonist/runEvent() @@ -13,6 +14,7 @@ E.antag_datum = antagonist_datum E.role_name = antag_name E.preference_type = preference_type + E.banning_key = banning_key E.protected_jobs = protected_jobs E.current_players = get_active_player_count(alive_check = 1, afk_check = 1, human_check = 1) E.control = src @@ -34,15 +36,16 @@ /datum/round_event/create_special_antag fakeable = FALSE var/role_name - var/antag_datum //The datum of the antag E.G. /datum/antagonist/special/undercover - var/preference_type = ROLE_TRAITOR + var/datum/antagonist/antag_datum //The datum of the antag E.G. /datum/antagonist/special/undercover + var/banning_key = BAN_ROLE_ALL_ANTAGONISTS + var/preference_type = /datum/role_preference/antagonist/traitor var/protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_CHIEFMEDICALOFFICER, JOB_NAME_CHIEFENGINEER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CAPTAIN) /datum/round_event/create_special_antag/start() for(var/mob/living/carbon/human/H in shuffle(GLOB.player_list)) - if(!H.client || !(preference_type in H.client.prefs.be_special) || !(H.client.prefs.toggles & PREFTOGGLE_MIDROUND_ANTAG)) + if(!H.client || !role_preference_enabled(H.client, preference_type)) continue - if(is_banned_from(H, list(preference_type))) + if(is_banned_from(H, initial(antag_datum.banning_key))) continue if(H.stat == DEAD) continue diff --git a/code/modules/events/spider_infestation.dm b/code/modules/events/spider_infestation.dm index 4e66933d0d17a..e095e55ea066e 100644 --- a/code/modules/events/spider_infestation.dm +++ b/code/modules/events/spider_infestation.dm @@ -7,7 +7,7 @@ can_malf_fake_alert = TRUE /datum/round_event/ghost_role/spider_infestation - role_name = ROLE_SPIDER + role_name = BAN_ROLE_SPIDER announceWhen = 400 fakeable = TRUE minimum_required = 1 @@ -37,7 +37,7 @@ message_admins("An event attempted to spawn spiders but no suitable vents were found. Aborting.") return MAP_ERROR - var/list/candidates = get_candidates(ROLE_SPIDER, null, ROLE_SPIDER) + var/list/candidates = get_candidates(BAN_ROLE_SPIDER, null, /datum/role_preference/midround_ghost/spider) if(!length(candidates)) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/wizard/imposter.dm b/code/modules/events/wizard/imposter.dm index 1c8ef95baae80..739e57ccb9e3a 100644 --- a/code/modules/events/wizard/imposter.dm +++ b/code/modules/events/wizard/imposter.dm @@ -10,7 +10,7 @@ if(!ishuman(M.current)) continue var/mob/living/carbon/human/W = M.current - var/list/candidates = pollGhostCandidates("Would you like to be an imposter wizard?", ROLE_WIZARD) + var/list/candidates = pollGhostCandidates("Would you like to be an imposter wizard?", BAN_ROLE_WIZARD, null, /datum/role_preference/midround_ghost/wizard) if(!candidates) return //Sad Trombone var/mob/dead/observer/C = pick(candidates) diff --git a/code/modules/food_and_drinks/food/snacks_meat.dm b/code/modules/food_and_drinks/food/snacks_meat.dm index 31b28480aaae6..76442a5d47d1d 100644 --- a/code/modules/food_and_drinks/food/snacks_meat.dm +++ b/code/modules/food_and_drinks/food/snacks_meat.dm @@ -193,7 +193,7 @@ qdel(src) /obj/item/reagent_containers/food/snacks/monkeycube/syndicate - faction = list("neutral", ROLE_SYNDICATE) + faction = list("neutral", FACTION_SYNDICATE) /obj/item/reagent_containers/food/snacks/monkeycube/gorilla name = "gorilla cube" diff --git a/code/modules/guardian/guardian.dm b/code/modules/guardian/guardian.dm index b0db5391383a2..21b62089b2fdb 100644 --- a/code/modules/guardian/guardian.dm +++ b/code/modules/guardian/guardian.dm @@ -584,7 +584,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians /mob/living/simple_animal/hostile/guardian/proc/ResetMe() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [summoner?.current?.name]'s [real_name]?", ROLE_HOLOPARASITE, null, FALSE, 10 SECONDS) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [summoner?.current?.name]'s [real_name]?", BAN_ROLE_HOLOPARASITE, null, null, 10 SECONDS, POLL_IGNORE_HOLOPARASITE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) key = C.key @@ -677,7 +677,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians return G.next_reset = world.time + GUARDIAN_RESET_COOLDOWN to_chat(src, "You attempt to reset [G.real_name]'s personality...") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", ROLE_HOLOPARASITE, null, FALSE, 100) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", BAN_ROLE_HOLOPARASITE, null, null, 100, POLL_IGNORE_HOLOPARASITE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) to_chat(G, "Your user reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance.") diff --git a/code/modules/guardian/guardianbuilder.dm b/code/modules/guardian/guardianbuilder.dm index 88812003c34e6..0bbe19700c9d6 100644 --- a/code/modules/guardian/guardianbuilder.dm +++ b/code/modules/guardian/guardianbuilder.dm @@ -225,7 +225,7 @@ used = FALSE return FALSE // IMPORTANT - if we're debugging, the user gets thrown into the stand - var/list/mob/dead/observer/candidates = debug_mode ? list(user) : pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_HOLOPARASITE, null, FALSE, 100, POLL_IGNORE_HOLOPARASITE) + var/list/mob/dead/observer/candidates = debug_mode ? list(user) : pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", BAN_ROLE_HOLOPARASITE, null, null, 100, POLL_IGNORE_HOLOPARASITE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) var/mob/living/simple_animal/hostile/guardian/G = new(user, theme, guardian_color) diff --git a/code/modules/guardian/standarrow.dm b/code/modules/guardian/standarrow.dm index 66cc660d5d61c..114d8400108c7 100644 --- a/code/modules/guardian/standarrow.dm +++ b/code/modules/guardian/standarrow.dm @@ -157,7 +157,7 @@ G.name = new_name /obj/item/stand_arrow/proc/get_stand(mob/living/carbon/H, datum/guardian_stats/stats) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Guardian Spirit of [H.real_name]?", ROLE_HOLOPARASITE, null, FALSE, 100, POLL_IGNORE_HOLOPARASITE) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Guardian Spirit of [H.real_name]?", BAN_ROLE_HOLOPARASITE, null, null, 100, POLL_IGNORE_HOLOPARASITE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) var/mob/living/simple_animal/hostile/guardian/G = new(H, GUARDIAN_MAGIC, rgb(rand(1, 255), rand(1, 255), rand(1, 255))) diff --git a/code/modules/jobs/jobs.dm b/code/modules/jobs/jobs.dm index b25c481c45868..6794f66860aa2 100644 --- a/code/modules/jobs/jobs.dm +++ b/code/modules/jobs/jobs.dm @@ -68,7 +68,7 @@ GLOBAL_LIST_INIT(security_positions, list( GLOBAL_LIST_INIT(nonhuman_positions, list( JOB_NAME_AI, JOB_NAME_CYBORG, - ROLE_PAI)) + BAN_ROLE_PAI)) // they are for hud_icon-based crew manifest @@ -162,7 +162,7 @@ GLOBAL_LIST_INIT(exp_jobsmap, list( GLOBAL_LIST_INIT(exp_specialmap, list( EXP_TYPE_LIVING = list(), // all living mobs EXP_TYPE_ANTAG = list(), - EXP_TYPE_SPECIAL = list("Lifebringer","Ash Walker","Exile","Servant Golem","Free Golem","Hermit","Translocated Vet","Escaped Prisoner","Hotel Staff","SuperFriend","Space Syndicate","Ancient Crew","Space Doctor","Space Bartender","Beach Bum","Skeleton","Zombie","Space Bar Patron","Lavaland Syndicate",JOB_NAME_PAI,"Ghost Role"), // Ghost roles + EXP_TYPE_SPECIAL = list("Lifebringer","Ash Walker","Exile","Servant Golem","Free Golem","Hermit","Translocated Vet","Escaped Prisoner","Hotel Staff","SuperFriend","Space Syndicate","Ancient Crew","Space Doctor","Beach Bum","Skeleton","Zombie","Lavaland Syndicate",JOB_NAME_PAI,"Ghost Role"), // Ghost roles EXP_TYPE_GHOST = list() // dead people, observers )) GLOBAL_PROTECT(exp_jobsmap) diff --git a/code/modules/mob/living/brain/posibrain.dm b/code/modules/mob/living/brain/posibrain.dm index 352a37cf114dc..a15d55a3e4e08 100644 --- a/code/modules/mob/living/brain/posibrain.dm +++ b/code/modules/mob/living/brain/posibrain.dm @@ -88,7 +88,7 @@ GLOBAL_VAR(posibrain_notify_cooldown) /obj/item/mmi/posibrain/proc/activate(mob/user) if(QDELETED(brainmob)) return - if(is_banned_from(user.ckey, ROLE_POSIBRAIN)) + if(is_banned_from(user.ckey, BAN_ROLE_POSIBRAIN)) to_chat(user, "You are restricted from taking positronic brain spawns at this time.") return if(user.client.get_exp_living(TRUE) <= MINUTES_REQUIRED_BASIC) diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm index 6d6396d172fc1..fbae9c3484784 100644 --- a/code/modules/mob/living/carbon/alien/alien.dm +++ b/code/modules/mob/living/carbon/alien/alien.dm @@ -3,7 +3,7 @@ icon = 'icons/mob/alien.dmi' gender = FEMALE //All xenos are girls!! dna = null - faction = list(ROLE_ALIEN) + faction = list(FACTION_ALIEN) ventcrawler = VENTCRAWLER_ALWAYS sight = SEE_MOBS see_in_dark = 4 diff --git a/code/modules/mob/living/carbon/alien/organs.dm b/code/modules/mob/living/carbon/alien/organs.dm index 133d867a21adb..3e1541526109c 100644 --- a/code/modules/mob/living/carbon/alien/organs.dm +++ b/code/modules/mob/living/carbon/alien/organs.dm @@ -108,12 +108,12 @@ var/recent_queen_death = 0 //Indicates if the queen died recently, aliens are heavily weakened while this is active. /obj/item/organ/alien/hivenode/Insert(mob/living/carbon/M, special = 0) - M.faction |= ROLE_ALIEN + M.faction |= FACTION_ALIEN ADD_TRAIT(M, TRAIT_XENO_IMMUNE, "xeno immune") return ..() /obj/item/organ/alien/hivenode/Remove(mob/living/carbon/M, special = 0) - M.faction -= ROLE_ALIEN + M.faction -= FACTION_ALIEN REMOVE_TRAIT(M, TRAIT_XENO_IMMUNE, "xeno immune") return ..() diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm index d4d0af23f1393..f743b141bc181 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -86,7 +86,7 @@ bursting = TRUE - var/list/candidates = pollGhostCandidates("Do you want to play as an alien larva that will burst out of [owner]?", ROLE_ALIEN, null, ROLE_ALIEN, 100, POLL_IGNORE_ALIEN_LARVA) + var/list/candidates = pollGhostCandidates("Do you want to play as an alien larva that will burst out of [owner]?", BAN_ROLE_ALIEN, null, /datum/role_preference/midround_ghost/xenomorph, 100, POLL_IGNORE_ALIEN_LARVA) if(QDELETED(src) || QDELETED(owner)) return diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index d2e8ced548602..024f2fdcbe4f1 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -133,7 +133,6 @@ //is mob player controllable var/playable = FALSE var/flavor_text = FLAVOR_TEXT_NONE - var/role //Used for determining whether a player is banned from taking control of a given mob, if it is assigned to a category. ///Default X offset var/body_pixel_x_offset = 0 diff --git a/code/modules/mob/living/living_sentience.dm b/code/modules/mob/living/living_sentience.dm index 428c2bf57127a..9214db251e98e 100644 --- a/code/modules/mob/living/living_sentience.dm +++ b/code/modules/mob/living/living_sentience.dm @@ -1,15 +1,19 @@ //WHY ISN'T THIS COMPONENT +/// The ban type to check if somebody attempts to play this "playable mob" +/mob/living/var/playable_bantype + /mob/living/ghostize(can_reenter_corpse, sentience_retention) . = ..() switch(sentience_retention) if (SENTIENCE_RETAIN) if (playable) //so the alert goes through for observing ghosts - set_playable() + set_playable(playable_bantype) if (SENTIENCE_FORCE) - set_playable() + set_playable(playable_bantype) if (SENTIENCE_ERASE) playable = FALSE + playable_bantype = null /mob/living/attack_ghost(mob/user) . = ..() @@ -34,7 +38,7 @@ if(key) to_chat(user, "Someone else already took [name].") return TRUE - if(!user?.client.canGhostRole(role, TRUE, flags_1)) + if(!can_take_ghost_spawner(user?.client, playable_bantype, TRUE, flags_1 & ADMIN_SPAWNED_1)) return key = user.key log_game("[key_name(src)] took control of [name].") @@ -43,8 +47,9 @@ to_chat(src, "[get_spawner_flavour_text()]") return TRUE -/mob/living/proc/set_playable() +/mob/living/proc/set_playable(ban_type = null) playable = TRUE + playable_bantype = ban_type if (!key) //check if there is nobody already inhibiting this mob notify_ghosts("[name] can be controlled", null, enter_link="(Click to play)", source=src, action=NOTIFY_ATTACK, ignore_key = name) LAZYADD(GLOB.mob_spawners["[name]"], src) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index acd95c6d22979..0eee6ce2955ee 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -845,7 +845,7 @@ /mob/living/silicon/robot/modules/syndicate icon_state = "synd_sec" - faction = list(ROLE_SYNDICATE) + faction = list(FACTION_SYNDICATE) bubble_icon = "syndibot" req_access = list(ACCESS_SYNDICATE) lawupdate = FALSE diff --git a/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm b/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm index b873ae662559e..9f666beb24f90 100644 --- a/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm +++ b/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm @@ -24,7 +24,7 @@ desc = "The Syndicate sends their regards." emagged = 2 noloot = TRUE - faction = list(ROLE_SYNDICATE) + faction = list(FACTION_SYNDICATE) /mob/living/simple_animal/bot/secbot/grievous/nullcrate/ComponentInitialize() . = ..() diff --git a/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm b/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm index 872c689ef5b23..1de423e2df6df 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm @@ -41,7 +41,7 @@ //ATTACK GHOST IGNORING PARENT RETURN VALUE /obj/effect/mob_spawn/drone/attack_ghost(mob/user) - if(is_banned_from(user.ckey, ROLE_DRONE) || QDELETED(src) || QDELETED(user)) + if(is_banned_from(user.ckey, BAN_ROLE_DRONE) || QDELETED(src) || QDELETED(user)) return if(CONFIG_GET(flag/use_age_restriction_for_jobs)) if(!isnum_safe(user.client.player_age)) //apparently what happens when there's no DB connected. just don't let anybody be a drone without admin intervention diff --git a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm index d2d2ff6d28717..852835c6da02b 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm @@ -18,7 +18,7 @@ health = 30 maxHealth = 120 //If you murder other drones and cannibalize them you can get much stronger initial_language_holder = /datum/language_holder/drone/syndicate - faction = list(ROLE_SYNDICATE) + faction = list(FACTION_SYNDICATE) speak_emote = list("hisses") bubble_icon = "syndibot" heavy_emp_damage = 10 diff --git a/code/modules/mob/living/simple_animal/hostile/alien.dm b/code/modules/mob/living/simple_animal/hostile/alien.dm index 0caec5891313b..17c3eb3269287 100644 --- a/code/modules/mob/living/simple_animal/hostile/alien.dm +++ b/code/modules/mob/living/simple_animal/hostile/alien.dm @@ -24,7 +24,7 @@ attack_sound = 'sound/weapons/bladeslice.ogg' atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) unsuitable_atmos_damage = 15 - faction = list(ROLE_ALIEN) + faction = list(FACTION_ALIEN) status_flags = CANPUSH minbodytemp = 0 see_in_dark = 8 diff --git a/code/modules/mob/living/simple_animal/hostile/carp.dm b/code/modules/mob/living/simple_animal/hostile/carp.dm index fbed8a758e1e3..137dd27c3010e 100644 --- a/code/modules/mob/living/simple_animal/hostile/carp.dm +++ b/code/modules/mob/living/simple_animal/hostile/carp.dm @@ -144,7 +144,7 @@ gender = FEMALE speak_emote = list("squeaks") gold_core_spawnable = NO_SPAWN - faction = list("carp", ROLE_SYNDICATE) + faction = list("carp", FACTION_SYNDICATE) AIStatus = AI_OFF /// Keeping track of the nuke disk for the functionality of storing it. var/obj/item/disk/nuclear/disky diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm index 41c94dc8bb2d9..f7c8762b52c92 100644 --- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm @@ -65,7 +65,6 @@ var/enriched_fed = 0 var/datum/action/innate/spider/lay_eggs/lay_eggs //the ability to lay eggs, granted to broodmothers var/datum/team/spiders/spider_team = null //utilized by AI controlled broodmothers to pass antag team info onto their eggs without a mind - role = ROLE_SPIDER atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) minbodytemp = 0 diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm index cc838e4a16fb8..5caa1e5231431 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm @@ -156,7 +156,7 @@ While using this makes the system rely on OnFire, it still gives options for tim addtimer(CALLBACK(src, PROC_REF(spawn_elite)), 30) return visible_message("Something within [src] stirs...") - var/list/candidates = pollCandidatesForMob("Do you want to play as a lavaland elite?", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, src, POLL_IGNORE_SENTIENCE_POTION) + var/list/candidates = pollCandidatesForMob("Do you want to play as a lavaland elite?", BAN_ROLE_LAVALAND_ELITE, null, null, 50, src, POLL_IGNORE_LAVALAND_ELITE) if(candidates.len) audible_message("The stirring sounds increase in volume!") elitemind = pick(candidates) diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm index 9d75eb659f748..af8d6db127254 100644 --- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm +++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm @@ -40,7 +40,7 @@ loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier) atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) unsuitable_atmos_damage = 15 - faction = list(ROLE_SYNDICATE) + faction = list(FACTION_SYNDICATE) check_friendly_fire = 1 status_flags = CANPUSH del_on_death = TRUE @@ -287,7 +287,7 @@ environment_smash = ENVIRONMENT_SMASH_NONE attacktext = "cuts" attack_sound = 'sound/weapons/bladeslice.ogg' - faction = list(ROLE_SYNDICATE) + faction = list(FACTION_SYNDICATE) atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) minbodytemp = 0 mob_size = MOB_SIZE_TINY diff --git a/code/modules/mob/living/simple_animal/hostile/wizard.dm b/code/modules/mob/living/simple_animal/hostile/wizard.dm index c365014d62fea..e4f104a8afb4b 100644 --- a/code/modules/mob/living/simple_animal/hostile/wizard.dm +++ b/code/modules/mob/living/simple_animal/hostile/wizard.dm @@ -20,7 +20,7 @@ a_intent = INTENT_HARM atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) unsuitable_atmos_damage = 15 - faction = list(ROLE_WIZARD) + faction = list(FACTION_WIZARD) status_flags = CANPUSH retreat_distance = 3 //out of fireball range diff --git a/code/modules/mob/living/simple_animal/slime/life.dm b/code/modules/mob/living/simple_animal/slime/life.dm index f9ca86f05ec41..14fb68a1e12af 100644 --- a/code/modules/mob/living/simple_animal/slime/life.dm +++ b/code/modules/mob/living/simple_animal/slime/life.dm @@ -5,7 +5,6 @@ var/monkey_bonus_damage = 2 var/attack_cooldown = 0 var/attack_cooldown_time = 20 //How long, in deciseconds, the cooldown of attacks is - role = ROLE_SENTIENCE /mob/living/simple_animal/slime/Life() set invisibility = 0 diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index 3c0cc90964c14..74ee7b4339e89 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -113,7 +113,7 @@ . = ..() set_nutrition(SLIME_DEFAULT_NUTRITION) if(transformeffects & SLIME_EFFECT_LIGHT_PINK) - set_playable() + set_playable(BAN_ROLE_SENTIENCE) /mob/living/simple_animal/slime/Destroy() set_target(null) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 3c1bdf5c5165c..2068abbd1bf3f 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -487,15 +487,17 @@ log_admin("[key_name(usr)] has offered control of ([key_name(M)]) to ghosts.") message_admins("[key_name_admin(usr)] has offered control of ([ADMIN_LOOKUPFLW(M)]) to ghosts") var/poll_message = "Do you want to play as [M.real_name]?" + var/ban_key = BAN_ROLE_ALL_ANTAGONISTS if(M.mind && M.mind.assigned_role) poll_message = "[poll_message] Job:[M.mind.assigned_role]." if(M.mind && M.mind.special_role) poll_message = "[poll_message] Status:[M.mind.special_role]." else if(M.mind) - var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist/) + var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist) if(A) poll_message = "[poll_message] Status:[A.name]." - var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ROLE_PAI, null, FALSE, 100, M) + ban_key = A.banning_key + var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ban_key, null, null, 100, M) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) diff --git a/code/modules/ninja/ninja_event.dm b/code/modules/ninja/ninja_event.dm index 8b8ea63bc9c95..f99987457525f 100644 --- a/code/modules/ninja/ninja_event.dm +++ b/code/modules/ninja/ninja_event.dm @@ -45,7 +45,7 @@ Contents: return MAP_ERROR //selecting a candidate player - var/list/candidates = get_candidates(ROLE_NINJA, null, ROLE_NINJA) + var/list/candidates = get_candidates(BAN_ROLE_NINJA, null, /datum/role_preference/midround_ghost/ninja) if(!candidates.len) return NOT_ENOUGH_PLAYERS @@ -54,8 +54,8 @@ Contents: //Prepare ninja player mind var/datum/mind/Mind = new /datum/mind(key) - Mind.assigned_role = ROLE_NINJA - Mind.special_role = ROLE_NINJA + Mind.assigned_role = BAN_ROLE_NINJA + Mind.special_role = BAN_ROLE_NINJA Mind.active = 1 //spawn the ninja and assign the candidate diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index ac909f14266fd..afdc30caa88ab 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -580,15 +580,17 @@ /obj/item/projectile/magic/wipe/proc/possession_test(var/mob/living/carbon/M) var/datum/brain_trauma/special/imaginary_friend/trapped_owner/trauma = M.gain_trauma(/datum/brain_trauma/special/imaginary_friend/trapped_owner) var/poll_message = "Do you want to play as [M.real_name]?" + var/ban_key = BAN_ROLE_ALL_ANTAGONISTS if(M.mind?.assigned_role) poll_message = "[poll_message] Job:[M.mind.assigned_role]." if(M.mind?.special_role) poll_message = "[poll_message] Status:[M.mind.special_role]." else if(M.mind) - var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist/) + var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist) if(A) poll_message = "[poll_message] Status:[A.name]." - var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ROLE_PAI, null, FALSE, 100, M) + ban_key = A.banning_key + var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ban_key, null, null, 100, M) if(M.stat == DEAD)//boo. return if(LAZYLEN(candidates)) diff --git a/code/modules/religion/rites.dm b/code/modules/religion/rites.dm index e7d6364704a3e..8ad81e32ec637 100644 --- a/code/modules/religion/rites.dm +++ b/code/modules/religion/rites.dm @@ -408,8 +408,7 @@ var/turf/altar_turf = get_turf(religious_tool) new /obj/effect/temp_visual/cult/blood/long(altar_turf) new /obj/effect/temp_visual/dir_setting/curse/long(altar_turf) - var/list/jobbans = list(ROLE_BRAINWASHED, ROLE_DEATHSQUAD, ROLE_DRONE, ROLE_LAVALAND, ROLE_MIND_TRANSFER, ROLE_POSIBRAIN, ROLE_SENTIENCE) - var/list/candidates = pollGhostCandidates("Do you wish to be resurrected as a Holy Summoned Undead?", jobbans, null, FALSE, 100, POLL_IGNORE_HOLYUNDEAD) + var/list/candidates = pollGhostCandidates("Do you wish to be resurrected as a Holy Summoned Undead?", BAN_ROLE_HOLY_SUMMONED, null, null, 100, POLL_IGNORE_HOLYUNDEAD) if(!length(candidates)) to_chat(user, "The soul pool is empty...") new /obj/effect/gibspawner/human/bodypartless(altar_turf) @@ -584,8 +583,7 @@ var/turf/altar_turf = get_turf(religious_tool) new /obj/effect/temp_visual/bluespace_fissure/long(altar_turf) user.visible_message("A tear in reality appears above the altar!") - var/list/jobbans = list(ROLE_BRAINWASHED, ROLE_DEATHSQUAD, ROLE_DRONE, ROLE_LAVALAND, ROLE_MIND_TRANSFER, ROLE_POSIBRAIN, ROLE_SENTIENCE) - var/list/candidates = pollGhostCandidates("Do you wish to be summoned as a Holy Carp?", jobbans, null, FALSE, 100, POLL_IGNORE_HOLYCARP) + var/list/candidates = pollGhostCandidates("Do you wish to be summoned as a Holy Carp?", BAN_ROLE_HOLY_SUMMONED, null, FALSE, 100, POLL_IGNORE_HOLYCARP) if(!length(candidates)) new /obj/effect/gibspawner/generic(altar_turf) user.visible_message("The carp pool was not strong enough to bring forth a space carp.") diff --git a/code/modules/research/xenobiology/crossbreeding/warping.dm b/code/modules/research/xenobiology/crossbreeding/warping.dm index d0bc52b92577e..2bc6dc8e893a9 100644 --- a/code/modules/research/xenobiology/crossbreeding/warping.dm +++ b/code/modules/research/xenobiology/crossbreeding/warping.dm @@ -714,7 +714,7 @@ GLOBAL_DATUM(blue_storage, /obj/item/storage/backpack/holding/bluespace) return to_chat(user, "The rune is trying to repair [host.name]'s soul!") - var/list/candidates = pollCandidatesForMob("Do you want to replace the soul of [host.name]?", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, host, POLL_IGNORE_SHADE)//todo: fix desc + var/list/candidates = pollCandidatesForMob("Do you want to replace the soul of [host.name]?", BAN_ROLE_SENTIENCE, null, null, 50, host, POLL_IGNORE_SHADE)//todo: fix desc if(length(candidates) && !host.key) //check if anyone wanted to play as the dead person and check if no one's in control of the body one last time. var/mob/dead/observer/ghost = pick(candidates) diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index fa7b34f8f9934..cd724eb20b939 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -749,7 +749,7 @@ to_chat(user, "You offer [src] to [SM]...") being_used = TRUE - var/list/candidates = pollCandidatesForMob("Do you want to play as [SM.name]? (Sentience Potion)", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, SM, POLL_IGNORE_SENTIENCE_POTION) // see poll_ignore.dm + var/list/candidates = pollCandidatesForMob("Do you want to play as [SM.name]? (Sentience Potion)", BAN_ROLE_SENTIENCE, null, null, 50, SM, POLL_IGNORE_SENTIENCE_POTION) // see poll_ignore.dm if(length(candidates)) var/mob/dead/observer/C = pick(candidates) SM.key = C.key @@ -804,7 +804,7 @@ if(SM.sentience_type != animal_type) to_chat(user, "You cannot transfer your consciousness to [SM]." ) return ..() - var/jb = is_banned_from(user.ckey, ROLE_MIND_TRANSFER) + var/jb = is_banned_from(user.ckey, BAN_ROLE_MIND_TRANSFER) if(QDELETED(src) || QDELETED(M) || QDELETED(user)) return diff --git a/code/modules/ruins/lavaland_ruin_code.dm b/code/modules/ruins/lavaland_ruin_code.dm index 91b120952e216..53a3aa7a4cf91 100644 --- a/code/modules/ruins/lavaland_ruin_code.dm +++ b/code/modules/ruins/lavaland_ruin_code.dm @@ -116,6 +116,7 @@ outfit = /datum/outfit/lavaland_syndicate assignedrole = "Lavaland Syndicate" use_cooldown = TRUE + banType = BAN_ROLE_LAVALAND_SYNDICATE /obj/effect/mob_spawn/human/lavaland_syndicate/special(mob/living/new_spawn) new_spawn.grant_language(/datum/language/codespeak) @@ -134,7 +135,7 @@ implants = list(/obj/item/implant/weapons_auth) /datum/outfit/lavaland_syndicate/post_equip(mob/living/carbon/human/H) - H.faction |= ROLE_SYNDICATE + H.faction |= FACTION_SYNDICATE /obj/effect/mob_spawn/human/lavaland_syndicate/comms name = "Syndicate Comms Agent" diff --git a/code/modules/ruins/spaceruin_code/hilbertshotel.dm b/code/modules/ruins/spaceruin_code/hilbertshotel.dm index a93d2f85fe2c0..b0008ab29dd95 100644 --- a/code/modules/ruins/spaceruin_code/hilbertshotel.dm +++ b/code/modules/ruins/spaceruin_code/hilbertshotel.dm @@ -496,6 +496,7 @@ GLOBAL_VAR_INIT(hhmysteryRoomNumber, 1337) back = /obj/item/storage/backpack/satchel/leather suit = /obj/item/clothing/suit/toggle/labcoat use_cooldown = TRUE + banType = BAN_ROLE_HOTEL_STAFF /obj/item/paper/crumpled/docslogs name = "Research Logs" diff --git a/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/assassination.dm b/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/assassination.dm index 92ada1e2638ba..164b8eaed0eac 100644 --- a/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/assassination.dm +++ b/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/assassination.dm @@ -35,8 +35,7 @@ /datum/orbital_objective/assassination/generate_objective_stuff(turf/chosen_turf) var/mob/living/carbon/human/created_human = new(chosen_turf) //Maybe polling ghosts would be better than the shintience code - created_human.role = ROLE_TRAITOR - created_human.set_playable() + created_human.set_playable(BAN_ROLE_SURVIVALIST) created_human.mind_initialize() //Remove nearby dangers for(var/mob/living/simple_animal/hostile/SA in range(10, created_human)) diff --git a/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/vip_extraction.dm b/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/vip_extraction.dm index a965e455b6f62..2da2a50023dff 100644 --- a/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/vip_extraction.dm +++ b/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/vip_extraction.dm @@ -36,7 +36,7 @@ /datum/orbital_objective/vip_recovery/generate_objective_stuff(turf/chosen_turf) var/mob/living/carbon/human/created_human = new(chosen_turf) //Maybe polling ghosts would be better than the shintience code - created_human.set_playable() + created_human.set_playable(BAN_ROLE_EXPLORATION_VIP) created_human.mind_initialize() //Remove nearby dangers for(var/mob/living/simple_animal/hostile/SA in range(10, created_human)) diff --git a/code/modules/shuttle/syndicate.dm b/code/modules/shuttle/syndicate.dm index 29ddd49e9f9f4..5cdee0bd98baf 100644 --- a/code/modules/shuttle/syndicate.dm +++ b/code/modules/shuttle/syndicate.dm @@ -29,7 +29,7 @@ . = ..() /obj/machinery/computer/shuttle_flight/syndicate/allowed(mob/M) - if(issilicon(M) && !(ROLE_SYNDICATE in M.faction)) + if(issilicon(M) && !(FACTION_SYNDICATE in M.faction)) return FALSE return ..() diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 1feee5b93344d..eb51dd51cc7df 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -57,8 +57,10 @@ #include "create_and_destroy.dm" #endif +#include "antag_datums.dm" #include "dynamic_ruleset_sanity.dm" #include "enumerables.dm" +#include "gamemode_sanity.dm" #include "keybinding_init.dm" #include "rcd.dm" #include "reagent_id_typos.dm" diff --git a/code/modules/unit_tests/antag_datums.dm b/code/modules/unit_tests/antag_datums.dm new file mode 100644 index 0000000000000..db41a0a164dbc --- /dev/null +++ b/code/modules/unit_tests/antag_datums.dm @@ -0,0 +1,13 @@ +/// Verifies that antag datums have banning_keys. +/datum/unit_test/antag_datum_sanity + +/datum/unit_test/antag_datum_sanity/Run() + for (var/datum/antagonist/antag as anything in subtypesof(/datum/antagonist)) + var/name = initial(antag.name) + if (!name || name == "Antagonist") + Fail("[antag] has no name set!") + if (!initial(antag.banning_key)) + Fail("[antag] has no banning_key set!") + var/category = initial(antag.antagpanel_category) + if (initial(antag.show_in_antagpanel) && (!category || category == "Uncategorized")) + Fail("[antag] shows in the antag panel, but has no category set!") diff --git a/code/modules/unit_tests/dynamic_ruleset_sanity.dm b/code/modules/unit_tests/dynamic_ruleset_sanity.dm index 61200a29bb344..84a111a074591 100644 --- a/code/modules/unit_tests/dynamic_ruleset_sanity.dm +++ b/code/modules/unit_tests/dynamic_ruleset_sanity.dm @@ -10,6 +10,17 @@ Fail("[ruleset] has a scaling_cost, but is also a lone/highlander ruleset.") else if (!has_scaling_cost && !is_lone) Fail("[ruleset] has no scaling cost, but is also not a lone/highlander ruleset.") + var/name = initial(ruleset.name) + if(!name) + Fail("[ruleset] has no name!") + if(name == "Extended" || name == "Meteor") // These rulesets don't spawn antags and are exempt. + continue + var/datum/antagonist/antag_datum = initial(ruleset.antag_datum) + if (!ispath(antag_datum, /datum/antagonist) || !initial(antag_datum.banning_key)) + Fail("[ruleset] has no antag_datum with a banning key!") + var/role_pref = initial(ruleset.role_preference) + if (!role_pref || !ispath(role_pref, /datum/role_preference)) + Fail("[ruleset] has no role preference!") for (var/datum/dynamic_ruleset/midround/ruleset as anything in subtypesof(/datum/dynamic_ruleset/midround) - /datum/dynamic_ruleset/midround/from_ghosts) var/midround_ruleset_style = initial(ruleset.midround_ruleset_style) diff --git a/code/modules/unit_tests/gamemode_sanity.dm b/code/modules/unit_tests/gamemode_sanity.dm new file mode 100644 index 0000000000000..85e0ac5ae56bb --- /dev/null +++ b/code/modules/unit_tests/gamemode_sanity.dm @@ -0,0 +1,18 @@ +/// Verifies that gamemodes have various fields +/datum/unit_test/gamemode_sanity + +/datum/unit_test/gamemode_sanity/Run() + for (var/datum/game_mode/mode as anything in subtypesof(/datum/game_mode)) + var/name = initial(mode.name) + if (!name) + Fail("[mode] has no name set!") + var/config_tag = initial(mode.config_tag) + if (!config_tag) + Fail("[mode] has no config_tag set!") + if(name == "event" || name == "extended" || name == "meteor" || name == "sandbox") // These gamemodes don't spawn antags and are exempt. + continue + if (!initial(mode.banning_key)) + Fail("[mode] has no banning_key set!") + var/role_pref = initial(mode.role_preference) + if (!role_pref || !ispath(role_pref, /datum/role_preference)) + Fail("[mode] has no role_preference set!") diff --git a/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm b/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm index 89d3c2238ac6c..eef1aabbe9b20 100644 --- a/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm +++ b/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm @@ -9,7 +9,7 @@ /datum/xenoartifact_trait/minor/looped/on_item(obj/item/xenoartifact/X, atom/user, atom/item) if(istype(item, /obj/item/multitool)) - to_chat(user, "The [item.name] displays a resistance reading of [X.charge_req*0.1].") + to_chat(user, "The [item.name] displays a resistance reading of [X.charge_req*0.1].") return TRUE return ..() @@ -41,13 +41,13 @@ X.cooldown = -1000 SECONDS //This is better than making a unique interaction in xenoartifact.dm return charges = pick(0, 1, 2) - playsound(get_turf(X), 'sound/machines/capacitor_charge.ogg', 50, TRUE) + playsound(get_turf(X), 'sound/machines/capacitor_charge.ogg', 50, TRUE) X.cooldown = saved_cooldown saved_cooldown = null /datum/xenoartifact_trait/minor/capacitive/on_item(obj/item/xenoartifact/X, atom/user, atom/item) if(istype(item, /obj/item/multitool)) - to_chat(user, "The [item.name] displays an overcharge reading of [charges/3].") + to_chat(user, "The [item.name] displays an overcharge reading of [charges/3].") return TRUE return ..() @@ -131,7 +131,7 @@ man.key = M.ckey /datum/xenoartifact_trait/minor/sentient/proc/get_canidate(obj/item/xenoartifact/X, mob/M) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the maleviolent force inside the [X.name]?", ROLE_SENTIENCE, null, FALSE, 8 SECONDS, POLL_IGNORE_SENTIENCE_POTION) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the maleviolent force inside the [X.name]?", BAN_ROLE_XENOARTIFACT, null, FALSE, 8 SECONDS, POLL_IGNORE_XENOARTIFACT) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) setup_sentience(X, C.ckey) @@ -141,8 +141,8 @@ /datum/xenoartifact_trait/minor/sentient/proc/setup_sentience(obj/item/xenoartifact/X, ckey) if(!(SSzclear.get_free_z_level())) - playsound(get_turf(X), 'sound/machines/buzz-sigh.ogg', 50, TRUE) - return + playsound(get_turf(X), 'sound/machines/buzz-sigh.ogg', 50, TRUE) + return man = new(get_turf(X)) man.name = pick(GLOB.xenoa_artifact_names) man.real_name = "[man.name] - [X]" @@ -197,6 +197,7 @@ short_desc = "You're a maleviolent sentience, possesing an ancient alien artifact." flavour_text = "Return to your master..." use_cooldown = TRUE + banType = BAN_ROLE_SENTIENT_XENOARTIFACT invisibility = 101 var/obj/item/xenoartifact/artifact @@ -232,7 +233,7 @@ X.visible_message("The [X.name] shatters!", "The [X.name] shatters!") var/obj/effect/decal/cleanable/ash/A = new(get_turf(X)) A.color = X.material - playsound(get_turf(X), 'sound/effects/glassbr1.ogg', 50, TRUE) + playsound(get_turf(X), 'sound/effects/glassbr1.ogg', 50, TRUE) qdel(X) //============ @@ -280,7 +281,7 @@ /datum/xenoartifact_trait/minor/wearable/on_init(obj/item/xenoartifact/X) X.slot_flags = ITEM_SLOT_GLOVES - + /datum/xenoartifact_trait/minor/wearable/activate(obj/item/xenoartifact/X, atom/user) X.true_target |= list(user) From 3869f544392d99568cc3c3e98cf4fd18184ad9ab Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 14 Jun 2023 00:10:53 -0400 Subject: [PATCH 004/269] Holy polish --- beestation.dme | 3 + code/__DEFINES/role_preferences.dm | 61 ++++++++---- code/controllers/subsystem/job.dm | 7 +- code/game/gamemodes/clock_cult/clockcult.dm | 1 - code/game/gamemodes/cult/cult.dm | 1 - code/game/gamemodes/devil/devil_game_mode.dm | 1 - code/game/gamemodes/dynamic/dynamic.dm | 9 -- .../gamemodes/dynamic/dynamic_rulesets.dm | 7 -- .../dynamic/dynamic_rulesets_latejoin.dm | 11 --- .../dynamic/dynamic_rulesets_midround.dm | 18 ---- .../dynamic/dynamic_rulesets_roundstart.dm | 9 +- code/game/gamemodes/dynamic/readme.md | 1 - .../gamemodes/eldritch_cult/eldritch_cult.dm | 1 - code/game/gamemodes/game_mode.dm | 27 +----- code/game/gamemodes/gangs/gangs.dm | 3 +- code/game/gamemodes/hivemind/hivemind.dm | 3 +- code/game/gamemodes/incursion/incursion.dm | 3 +- code/game/gamemodes/nuclear/nuclear.dm | 1 - code/game/gamemodes/revolution/revolution.dm | 1 - code/game/gamemodes/traitor/traitor.dm | 1 - code/game/gamemodes/wizard/wizard.dm | 1 - code/game/objects/effects/spiders.dm | 2 +- .../structures/fugitive_role_spawners.dm | 1 + .../objects/structures/ghost_role_spawners.dm | 1 + code/modules/admin/sql_ban_system.dm | 24 +++-- code/modules/admin/verbs/one_click_antag.dm | 20 ++-- .../antagonists/_common/antag_datum.dm | 2 +- .../antagonists/ashwalker/ashwalker.dm | 2 +- .../antagonists/changeling/powers/teratoma.dm | 2 +- .../antagonists/clock_cult/mobs/cogscarab.dm | 5 - .../role_preference/_role_preference.dm | 17 ++-- .../role_preference/role_antagonists.dm | 32 +++++++ .../role_preference/role_changeling.dm | 4 - .../antagonists/role_preference/role_ghost.dm | 1 + .../role_preference/role_midrounds.dm | 50 ++++++++++ .../role_preference/role_operative.dm | 13 --- .../role_preference/role_traitor.dm | 10 -- .../role_preference/role_wizard.dm | 6 +- .../roundstart_special/special_antagonist.dm | 8 +- .../antagonists/slaughter/slaughterevent.dm | 2 +- .../antagonists/space_dragon/carp_rift.dm | 2 +- code/modules/antagonists/swarmer/swarmer.dm | 1 + code/modules/awaymissions/corpse.dm | 6 +- .../awaymissions/mission_code/Academy.dm | 2 +- code/modules/client/preferences.dm | 94 +++++++++++++------ code/modules/events/pirates.dm | 2 +- code/modules/events/sentience.dm | 2 +- code/modules/mob/living/living_sentience.dm | 2 + .../friendly/drone/drones_as_items.dm | 6 -- .../hostile/mining_mobs/elites/elite.dm | 2 +- .../traits/xenoartifact_minors.dm | 2 +- config/dynamic.json | 32 ------- html/admin/banpanel.css | 68 ++++++++------ 53 files changed, 303 insertions(+), 290 deletions(-) create mode 100644 code/modules/antagonists/role_preference/role_antagonists.dm create mode 100644 code/modules/antagonists/role_preference/role_ghost.dm create mode 100644 code/modules/antagonists/role_preference/role_midrounds.dm diff --git a/beestation.dme b/beestation.dme index 9884cd5f3e1e7..a8dc51ae07cb7 100644 --- a/beestation.dme +++ b/beestation.dme @@ -1923,7 +1923,10 @@ #include "code\modules\antagonists\revenant\revenant_spawn_event.dm" #include "code\modules\antagonists\revolution\revolution.dm" #include "code\modules\antagonists\role_preference\_role_preference.dm" +#include "code\modules\antagonists\role_preference\role_antagonists.dm" #include "code\modules\antagonists\role_preference\role_changeling.dm" +#include "code\modules\antagonists\role_preference\role_ghost.dm" +#include "code\modules\antagonists\role_preference\role_midrounds.dm" #include "code\modules\antagonists\role_preference\role_operative.dm" #include "code\modules\antagonists\role_preference\role_traitor.dm" #include "code\modules\antagonists\role_preference\role_wizard.dm" diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index 1090f05f25492..7846c20c7c446 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -10,9 +10,8 @@ #define MINUTES_REQUIRED_COMMAND 1200 //For command positions, to be weighed against the relevant department -// Banning snowflake - global antag ban. Does not include ghost roles that aren't antagonists +// Banning snowflake - global antag ban. Does not include ghost roles that aren't antagonists or forced antagonists #define BAN_ROLE_ALL_ANTAGONISTS "All Antagonists" -#define BAN_ROLE_ALL_ANTAGONISTS_AND_FORCED "All Antagonists and Forced Antagonists" //These are synced with the Database, if you change the values of the defines //then you MUST update the database! @@ -51,6 +50,8 @@ #define BAN_ROLE_FUGITIVE "Fugitive" #define BAN_ROLE_FUGITIVE_HUNTER "Fugitive Hunter" #define BAN_ROLE_SLAUGHTER_DEMON "Slaughter Demon" +#define BAN_ROLE_CONTRACTOR_SUPPORT_UNIT "Contractor Support Unit" +#define BAN_ROLE_PYRO_SLIME "Pyroclastic Anomaly Slime" /// Roles that are antagonists, roundstart or not, and have passes to do.. antagonistry GLOBAL_LIST_INIT(antagonist_bannable_roles, list( @@ -89,8 +90,11 @@ GLOBAL_LIST_INIT(antagonist_bannable_roles, list( BAN_ROLE_FUGITIVE, BAN_ROLE_FUGITIVE_HUNTER, BAN_ROLE_SLAUGHTER_DEMON, + BAN_ROLE_CONTRACTOR_SUPPORT_UNIT, )) +#define BAN_ROLE_FORCED_ANTAGONISTS "Forced Antagonists" + #define BAN_ROLE_BRAINWASHED "Brainwashed Victim" #define BAN_ROLE_HYPNOTIZED "Hypnotized Victim" #define BAN_ROLE_HIVE_VESSEL "Awakened Vessel" @@ -111,7 +115,23 @@ GLOBAL_LIST_INIT(forced_bannable_roles, list( #define BAN_ROLE_EXPERIMENTAL_CLONE "Experimental Clone" #define BAN_ROLE_LAVALAND_ELITE "Lavaland Elite" #define BAN_ROLE_SPECTRAL_BLADE "Spectral Blade" -#define BAN_ROLE_ASHKWALKER "Ashwalker" +#define BAN_ROLE_ASHWALKER "Ashwalker" +#define BAN_ROLE_LIFEBRINGER "Lifebringer" +#define BAN_ROLE_FREE_GOLEM "Free Golem" +#define BAN_ROLE_HERMIT "Hermit" +#define BAN_ROLE_TRANSLOCATED_VET "Translocated Vet" +#define BAN_ROLE_LAVALAND_ESCAPED_PRISONER "Lavaland Escaped Prisoner" +#define BAN_ROLE_BEACH_BUM "Beach Bum" +#define BAN_ROLE_HOTEL_STAFF "Hotel Staff" +#define BAN_ROLE_LAVALAND_SYNDICATE "Lavaland Syndicate" +#define BAN_ROLE_DEMONIC_FRIEND "Demonic Friend" +#define BAN_ROLE_ANCIENT_CREW "Ancient Crew" +#define BAN_ROLE_SKELETAL_REMAINS "Skeletal Remains" +#define BAN_ROLE_SENTIENT_ANIMAL "Sentient Animal" +#define BAN_ROLE_HOLY_SUMMONED "Holy Summoned" +#define BAN_ROLE_SURVIVALIST "Exploration Survivalist" +#define BAN_ROLE_EXPLORATION_VIP "Exploration VIP" +#define BAN_ROLE_SENTIENT_XENOARTIFACT "Sentient Xenoartifiact" /// Any ghost role that is not really an antagonist or doesn't antagonize (lavaland, sentience potion, etc) GLOBAL_LIST_INIT(ghost_role_bannable_roles, list( @@ -122,7 +142,20 @@ GLOBAL_LIST_INIT(ghost_role_bannable_roles, list( BAN_ROLE_EXPERIMENTAL_CLONE, BAN_ROLE_LAVALAND_ELITE, BAN_ROLE_SPECTRAL_BLADE, - BAN_ROLE_ASHKWALKER, + BAN_ROLE_ASHWALKER, + BAN_ROLE_LIFEBRINGER, + BAN_ROLE_FREE_GOLEM, + BAN_ROLE_HERMIT, + BAN_ROLE_TRANSLOCATED_VET, + BAN_ROLE_LAVALAND_ESCAPED_PRISONER, + BAN_ROLE_BEACH_BUM, + BAN_ROLE_HOTEL_STAFF, + BAN_ROLE_LAVALAND_SYNDICATE, + BAN_ROLE_DEMONIC_FRIEND, + BAN_ROLE_ANCIENT_CREW, + BAN_ROLE_SKELETAL_REMAINS, + BAN_ROLE_SENTIENT_ANIMAL, + BAN_ROLE_HOLY_SUMMONED, )) #define BAN_ROLE_IMAGINARY_FRIEND "Imaginary Friend" @@ -142,8 +175,8 @@ GLOBAL_LIST_INIT(other_bannable_roles, list( CRASH("Invalid role_preference_key [role_preference_key] passed to role_preference_enabled!") if(!istype(player) || !player.prefs) return FALSE - var/role_preference_value = player.prefs.be_special[role_preference_key] - if(isnum(role_preference_value) && role_preference_value == 0) // explicitly disabled and not null + var/role_preference_value = player.prefs.be_special["[role_preference_key]"] + if(isnum(role_preference_value) && !role_preference_value) // explicitly disabled and not null return FALSE return TRUE @@ -167,11 +200,6 @@ GLOBAL_LIST_INIT(other_bannable_roles, list( if(feedback) to_chat(player, "You are banned from this role!") return FALSE - if(gamemode_for_age) - if(!gamemode_for_age.age_check(player)) - if(feedback) - to_chat(player, "Your account is not old enough to take this role!") - return FALSE if(req_hours) //minimum living hour count if((player.get_exp_living(TRUE)/60) < req_hours) if(feedback) @@ -179,11 +207,11 @@ GLOBAL_LIST_INIT(other_bannable_roles, list( return FALSE return TRUE -/proc/can_take_ghost_spawner(client/player, banning_key = BAN_ROLE_ALL_ANTAGONISTS, use_cooldown = TRUE, is_admin_spawned = FALSE) - if(!SSticker.HasRoundStarted() || !istype(player)) +/proc/can_take_ghost_spawner(client/player, banning_key = BAN_ROLE_ALL_ANTAGONISTS, use_cooldown = TRUE, is_ghost_role = FALSE, is_admin_spawned = FALSE) + if(!istype(player)) return FALSE - if(!(GLOB.ghost_role_flags & GHOSTROLE_SPAWNER) && !is_admin_spawned) - to_chat(src, "An admin has temporarily disabled non-admin ghost roles!") + if(is_ghost_role && !(GLOB.ghost_role_flags & GHOSTROLE_SPAWNER) && !is_admin_spawned) + to_chat(player, "An admin has temporarily disabled non-admin ghost roles!") return FALSE if(!should_include_for_role( player, @@ -192,7 +220,7 @@ GLOBAL_LIST_INIT(other_bannable_roles, list( )) return FALSE if(use_cooldown && player.next_ghost_role_tick > world.time) - to_chat(src, "You have died recently, you must wait [(player.next_ghost_role_tick - world.time)/10] seconds until you can use a ghost spawner.") + to_chat(player, "You have died recently, you must wait [(player.next_ghost_role_tick - world.time)/10] seconds until you can use a ghost spawner.") return FALSE return TRUE @@ -204,6 +232,7 @@ GLOBAL_LIST_INIT(other_bannable_roles, list( #define ROLE_PREFERENCE_CATEGORY_ANAGONIST "Antagonists" #define ROLE_PREFERENCE_CATEGORY_MIDROUND_LIVING "Midrounds (Living)" #define ROLE_PREFERENCE_CATEGORY_MIDROUND_GHOST "Midrounds (Ghost Poll)" +#define ROLE_PREFERENCE_CATEGORY_GHOST_ROLES "Ghost Roles" GLOBAL_LIST_INIT(role_preference_entries, init_role_preference_entries()) diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index 3b95cadec9ea5..b313a9105c7d8 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -155,8 +155,8 @@ SUBSYSTEM_DEF(job) return FALSE job.current_positions = max(0, job.current_positions - 1) -/datum/controller/subsystem/job/proc/FindOccupationCandidates(datum/job/job, level, flag) - JobDebug("Running FOC, Job: [job], Level: [level], Flag: [flag]") +/datum/controller/subsystem/job/proc/FindOccupationCandidates(datum/job/job, level) + JobDebug("Running FOC, Job: [job], Level: [level]") var/list/candidates = list() for(var/mob/dead/new_player/player in unassigned) if(QDELETED(player) || is_banned_from(player.ckey, job.title)) @@ -168,9 +168,6 @@ SUBSYSTEM_DEF(job) if(job.required_playtime_remaining(player.client)) JobDebug("FOC player not enough xp, Player: [player]") continue - if(flag && (!(flag in player.client.prefs.be_special))) - JobDebug("FOC flag failed, Player: [player], Flag: [flag], ") - continue if(player.mind && (job.title in player.mind.restricted_roles)) JobDebug("FOC incompatible with antagonist role, Player: [player]") continue diff --git a/code/game/gamemodes/clock_cult/clockcult.dm b/code/game/gamemodes/clock_cult/clockcult.dm index f30527ad8c777..b2062695bc3f8 100644 --- a/code/game/gamemodes/clock_cult/clockcult.dm +++ b/code/game/gamemodes/clock_cult/clockcult.dm @@ -34,7 +34,6 @@ GLOBAL_VAR(clockcult_eminence) recommended_enemies = 4 banning_key = BAN_ROLE_SERVANT_OF_RATVAR role_preference = /datum/role_preference/antagonist/clock_cultist - enemy_minimum_age = 14 title_icon = "clockcult" announce_span = "danger" diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm index e249ef351cefc..273b4abd26613 100644 --- a/code/game/gamemodes/cult/cult.dm +++ b/code/game/gamemodes/cult/cult.dm @@ -44,7 +44,6 @@ required_players = 29 required_enemies = 4 recommended_enemies = 4 - enemy_minimum_age = 14 announce_span = "cult" announce_text = "Some crew members are trying to start a cult to Nar'Sie!\n\ diff --git a/code/game/gamemodes/devil/devil_game_mode.dm b/code/game/gamemodes/devil/devil_game_mode.dm index 1f6c83c7c5bca..cf0d65c4e5cd3 100644 --- a/code/game/gamemodes/devil/devil_game_mode.dm +++ b/code/game/gamemodes/devil/devil_game_mode.dm @@ -10,7 +10,6 @@ required_enemies = 1 recommended_enemies = 4 reroll_friendly = 1 - enemy_minimum_age = 0 title_icon = "devil" allowed_special = list(/datum/special_role/traitor) diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm index 8f39c752c8f64..a20203e5e787d 100644 --- a/code/game/gamemodes/dynamic/dynamic.dm +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -709,15 +709,6 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) log_game("DYNAMIC: [src] passed lowpop_lowimpact requirement: ([living_antags_count] antags of [living_players_count] players - [antag_percent]%)") return FALSE -/// Checks if client age is age or older. -/datum/game_mode/dynamic/proc/check_age(client/C, age) - enemy_minimum_age = age - if(get_remaining_days(C) == 0) - enemy_minimum_age = initial(enemy_minimum_age) - return TRUE // Available in 0 days = available right now = player is old enough to play. - enemy_minimum_age = initial(enemy_minimum_age) - return FALSE - /datum/game_mode/dynamic/make_antag_chance(mob/living/carbon/human/newPlayer) if (GLOB.dynamic_forced_extended) return diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets.dm b/code/game/gamemodes/dynamic/dynamic_rulesets.dm index e94f34e86c0f4..0b5c8b8f12e50 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets.dm @@ -22,8 +22,6 @@ var/role_preference = null /// The antagonist datum that is assigned to the mobs mind on ruleset execution. var/datum/antagonist/antag_datum = null - /// The required minimum account age for this ruleset. - var/minimum_required_age = 7 /// If set, and config flag protect_roles_from_antagonist is false, then the rule will not pick players from these roles. var/list/protected_roles = list() /// If set, rule will deny candidates from those roles always. @@ -55,8 +53,6 @@ var/list/requirements = list(40,30,20,10,10,10,10,10,10,10) /// Reference to the mode, use this instead of SSticker.mode. var/datum/game_mode/dynamic/mode = null - /// If a role is to be considered another for the purpose of banning. - var/antag_flag_override = null /// If a ruleset type which is in this list has been executed, then the ruleset will not be executed. var/list/blocking_rules = list() /// The minimum amount of players required for the rule to be considered. @@ -307,9 +303,6 @@ )) continue - if(!mode.check_age(client, minimum_required_age)) - candidates.Remove(P) - continue if(P.mind.special_role) // We really don't want to give antag to an antag. candidates.Remove(P) continue diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm index a52f982c57fac..f69f0ba6f59b5 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm @@ -9,17 +9,6 @@ if (!P.client || !P.mind || !P.mind.assigned_role) // Are they connected? candidates.Remove(P) continue - if(!mode.check_age(P.client, minimum_required_age)) - candidates.Remove(P) - continue - if(antag_flag_override) - if(!(antag_flag_override in P.client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag_override, ROLE_SYNDICATE))) - candidates.Remove(P) - continue - else - if(!(antag_flag in P.client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag, ROLE_SYNDICATE))) - candidates.Remove(P) - continue if (P.mind.assigned_role in restricted_roles) // Does their job allow for it? candidates.Remove(P) else if(length(exclusive_roles) && !(P.mind.assigned_role in exclusive_roles)) // Is the rule exclusive to their job? diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index c4f5d27382f60..737cf35bbac25 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -51,19 +51,6 @@ if (!M.client) // Are they connected? trimmed_list.Remove(M) continue - if(!mode.check_age(M.client, minimum_required_age)) - trimmed_list.Remove(M) - continue -<<<<<<< HEAD - if(antag_flag_override) - if(!(antag_flag_override in M.client.prefs.be_special) || is_banned_from(M.ckey, list(antag_flag_override, ROLE_SYNDICATE))) - trimmed_list.Remove(M) - continue - else - if(!(antag_flag in M.client.prefs.be_special) || is_banned_from(M.ckey, list(antag_flag, ROLE_SYNDICATE))) - trimmed_list.Remove(M) - continue -======= if(!should_include_for_role( M.client, banning_key = initial(antag_datum.banning_key), @@ -71,7 +58,6 @@ poll_ignore_key = role_preference )) continue ->>>>>>> b4bb6c2e7d5 (Today is the day I nuked antag prefs) if (M.mind) if (restrict_ghost_roles && (M.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL])) // Are they playing a ghost role? trimmed_list.Remove(M) @@ -127,11 +113,7 @@ message_admins("Polling [possible_volunteers.len] players to apply for the [name] ruleset.") log_game("DYNAMIC: Polling [possible_volunteers.len] players to apply for the [name] ruleset.") -<<<<<<< HEAD - candidates = pollGhostCandidates("The mode is looking for volunteers to become [antag_flag] for [name]", antag_flag, SSticker.mode, antag_flag_override ? antag_flag_override : antag_flag, poll_time = 300) -======= candidates = pollGhostCandidates("The mode is looking for volunteers to become [initial(antag_datum.name)] for [name]", initial(antag_datum.banning_key), SSticker.mode, role_preference, poll_time = 300) ->>>>>>> b4bb6c2e7d5 (Today is the day I nuked antag prefs) if(!length(candidates)) message_admins("The ruleset [name] received no applications.") diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index 76bd6f9c06e17..512f00ae76a2f 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -9,7 +9,6 @@ name = "Traitors" role_preference = /datum/role_preference/antagonist/traitor antag_datum = /datum/antagonist/traitor - minimum_required_age = 0 protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_roles = list(JOB_NAME_CYBORG) required_candidates = 1 @@ -175,7 +174,6 @@ role_preference = /datum/role_preference/antagonist/wizard antag_datum = /datum/antagonist/wizard flags = HIGH_IMPACT_RULESET | NO_OTHER_ROUNDSTARTS_RULESET | PERSISTENT_RULESET - minimum_required_age = 14 restricted_roles = list(JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) // Just to be sure that a wizard getting picked won't ever imply a Captain or HoS not getting drafted required_candidates = 1 weight = 2 @@ -216,9 +214,8 @@ /datum/dynamic_ruleset/roundstart/bloodcult name = "Blood Cult" - role_preference = /datum/role_preference/antagonist/blood_cult + role_preference = /datum/role_preference/antagonist/blood_cultist antag_datum = /datum/antagonist/cult - minimum_required_age = 14 restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_CHAPLAIN, JOB_NAME_HEADOFPERSONNEL) required_candidates = 2 weight = 3 @@ -275,7 +272,6 @@ role_preference = /datum/role_preference/antagonist/nuclear_operative antag_datum = /datum/antagonist/nukeop var/datum/antagonist/antag_leader_datum = /datum/antagonist/nukeop/leader - minimum_required_age = 14 restricted_roles = list(JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) // Just to be sure that a nukie getting picked won't ever imply a Captain or HoS not getting drafted required_candidates = 5 weight = 3 @@ -300,7 +296,6 @@ assigned += M.mind M.mind.assigned_role = BAN_ROLE_OPERATIVE M.mind.special_role = BAN_ROLE_OPERATIVE - GLOB.pre_setup_antags += M.mind return TRUE /datum/dynamic_ruleset/roundstart/nuclear/execute() @@ -313,7 +308,6 @@ else var/datum/antagonist/nukeop/new_op = new antag_datum() M.add_antag_datum(new_op) - GLOB.pre_setup_antags -= M return TRUE /datum/dynamic_ruleset/roundstart/nuclear/round_result() @@ -361,7 +355,6 @@ persistent = TRUE role_preference = /datum/role_preference/antagonist/revolutionary antag_datum = /datum/antagonist/rev/head - minimum_required_age = 14 restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_CHIEFENGINEER, JOB_NAME_CHIEFMEDICALOFFICER, JOB_NAME_RESEARCHDIRECTOR) required_candidates = 3 weight = 3 diff --git a/code/game/gamemodes/dynamic/readme.md b/code/game/gamemodes/dynamic/readme.md index 233446509f8b1..3610ba681727b 100644 --- a/code/game/gamemodes/dynamic/readme.md +++ b/code/game/gamemodes/dynamic/readme.md @@ -144,7 +144,6 @@ Rulesets have the following variables notable to developers and those interested - Traitor: `antag_cap = list("denominator" = 24)`. This means that for every 24 players, 1 traitor will be added (assuming no scaling). - Nuclear Emergency: `antag_cap = list("denominator" = 18, "offset" = 1)`. For every 18 players, 1 nuke op will be added. Starts at 1, meaning at 30 players, 3 nuke ops will be created, rather than 2. - Revolution: `antag_cap = 3`. There will always be 3 rev-heads, no matter what. -- `minimum_required_age` - The minimum age in order to apply for the ruleset. - `weight` - How likely this ruleset is to be picked. A higher weight results in a higher chance of drafting. - `cost` - The initial cost of the ruleset. This cost is taken from either the roundstart or midround budget, depending on the ruleset. - `scaling_cost` - Cost for every *additional* application of this ruleset. diff --git a/code/game/gamemodes/eldritch_cult/eldritch_cult.dm b/code/game/gamemodes/eldritch_cult/eldritch_cult.dm index 0d364b4025134..eb9a14e221678 100644 --- a/code/game/gamemodes/eldritch_cult/eldritch_cult.dm +++ b/code/game/gamemodes/eldritch_cult/eldritch_cult.dm @@ -11,7 +11,6 @@ required_enemies = 1 recommended_enemies = 4 reroll_friendly = 1 - enemy_minimum_age = 0 allowed_special = list(/datum/special_role/traitor/higher_chance) diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 861ad50e2b412..509244939015a 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -37,7 +37,6 @@ var/round_converted = 0 //0: round not converted, 1: round going to convert, 2: round converted var/reroll_friendly //During mode conversion only these are in the running var/continuous_sanity_checked //Catches some cases where config options could be used to suggest that modes without antagonists should end when all antagonists die - var/enemy_minimum_age = 7 //How many days must players have been playing before they can play this antagonist var/list/allowed_special = list() //Special roles that can spawn (Add things like /datum/antagonist/special/undercover for them to be able to spawn during this gamemode) var/list/active_specials = list() //Special roles that have spawned, and can now spawn late @@ -125,10 +124,10 @@ return //No more candidates, end the selection process, and active specials at this time will be handled by latejoins or not included var/mob/person if(special.use_antag_rep) - person = antag_pick(candidates, special.special_role_preference) + person = antag_pick(candidates, special.preference_type) else person = pick_n_take(candidates) - if(is_banned_from(person.ckey, special.preference_type)) + if(is_banned_from(person.ckey, special.banning_key)) continue if(!person) continue @@ -516,8 +515,7 @@ if(player.client && player.ready == PLAYER_READY_TO_PLAY) if(role_preference_enabled(player.client, role_preference)) if(!is_banned_from(player.ckey, banning_key) && !QDELETED(player)) - if(age_check(player.client)) //Must be older than the minimum age - candidates += player.mind // Get a list of all the people who want to be the antagonist for this round + candidates += player.mind // Get a list of all the people who want to be the antagonist for this round if(restricted_jobs) for(var/datum/mind/player in candidates) @@ -741,25 +739,6 @@ for (var/C in GLOB.admins) to_chat(C, msg.Join()) -//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1 -/datum/game_mode/proc/age_check(client/C) - if(get_remaining_days(C) == 0) - return 1 //Available in 0 days = available right now = player is old enough to play. - return 0 - - -/datum/game_mode/proc/get_remaining_days(client/C) - if(!C) - return 0 - if(!CONFIG_GET(flag/use_age_restriction_for_jobs)) - return 0 - if(!isnum_safe(C.player_age)) - return 0 //This is only a number if the db connection is established, otherwise it is text: "Requires database", meaning these restrictions cannot be enforced - if(!isnum_safe(enemy_minimum_age)) - return 0 - - return max(0, enemy_minimum_age - C.player_age) - /datum/game_mode/proc/remove_antag_for_borging(datum/mind/newborgie) SSticker.mode.remove_cultist(newborgie, 0, 0) remove_servant_of_ratvar(newborgie) diff --git a/code/game/gamemodes/gangs/gangs.dm b/code/game/gamemodes/gangs/gangs.dm index 10aabcfcd9071..da640ad0ed86c 100644 --- a/code/game/gamemodes/gangs/gangs.dm +++ b/code/game/gamemodes/gangs/gangs.dm @@ -6,12 +6,11 @@ GLOBAL_LIST_EMPTY(gangs) name = "gang war" config_tag = "gang" banning_key = BAN_ROLE_GANG - role_preference = /datum/role_preference/antagonist/gang + role_preference = /datum/role_preference/antagonist/gangster restricted_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_AI, JOB_NAME_CYBORG,JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_HEADOFSECURITY) required_players = 30 required_enemies = 2 recommended_enemies = 3 - enemy_minimum_age = 14 announce_span = "danger" announce_text = "A violent turf war has erupted on the station!\n\ diff --git a/code/game/gamemodes/hivemind/hivemind.dm b/code/game/gamemodes/hivemind/hivemind.dm index b71b4c76573bc..f444e2894bc48 100644 --- a/code/game/gamemodes/hivemind/hivemind.dm +++ b/code/game/gamemodes/hivemind/hivemind.dm @@ -5,7 +5,7 @@ GLOBAL_LIST_EMPTY(hivehosts) config_tag = "hivemind" report_type = "hivemind" banning_key = BAN_ROLE_HIVE - role_preference = /datum/role_preference/antagonist/hivemind + role_preference = /datum/role_preference/antagonist/hivemind_host false_report_weight = 5 protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_jobs = list(JOB_NAME_AI, JOB_NAME_CYBORG) @@ -13,7 +13,6 @@ GLOBAL_LIST_EMPTY(hivehosts) required_enemies = 3 recommended_enemies = 3 reroll_friendly = 1 - enemy_minimum_age = 0 announce_span = "danger" announce_text = "The hosts of several psionic hiveminds have infiltrated the station and are looking to assimilate the crew!\n\ diff --git a/code/game/gamemodes/incursion/incursion.dm b/code/game/gamemodes/incursion/incursion.dm index b334d764a1a8d..92ddbcb3ff0bc 100644 --- a/code/game/gamemodes/incursion/incursion.dm +++ b/code/game/gamemodes/incursion/incursion.dm @@ -8,9 +8,8 @@ restricted_jobs = list(JOB_NAME_AI, JOB_NAME_CYBORG) protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE,JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_HEADOFSECURITY, JOB_NAME_CHIEFENGINEER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CHIEFMEDICALOFFICER) banning_key = BAN_ROLE_INCURSION - role_preference = /datum/role_preference/antagonist/incursion + role_preference = /datum/role_preference/antagonist/incursionist false_report_weight = 10 - enemy_minimum_age = 0 announce_span = "danger" announce_text = "A large force of syndicate operatives have infiltrated the ranks of the station and wish to take it by force!\n\ diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index 39b077c470132..42a2827a770ed 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -10,7 +10,6 @@ recommended_enemies = 5 banning_key = BAN_ROLE_OPERATIVE role_preference = /datum/role_preference/antagonist/nuclear_operative - enemy_minimum_age = 14 announce_span = "danger" announce_text = "Syndicate forces are approaching the station in an attempt to destroy it!\n\ diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm index 97352dca8020b..3a9ce291eae1f 100644 --- a/code/game/gamemodes/revolution/revolution.dm +++ b/code/game/gamemodes/revolution/revolution.dm @@ -19,7 +19,6 @@ required_players = 30 required_enemies = 2 recommended_enemies = 3 - enemy_minimum_age = 14 title_icon = "revolution" announce_span = "danger" diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm index e25c5c113cb8f..1974740cd14c0 100644 --- a/code/game/gamemodes/traitor/traitor.dm +++ b/code/game/gamemodes/traitor/traitor.dm @@ -18,7 +18,6 @@ required_enemies = 1 recommended_enemies = 4 reroll_friendly = 1 - enemy_minimum_age = 0 announce_span = "danger" announce_text = "There are Syndicate agents on the station!\n\ diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm index e8a6eff1718b3..6d6c53c217eb3 100644 --- a/code/game/gamemodes/wizard/wizard.dm +++ b/code/game/gamemodes/wizard/wizard.dm @@ -12,7 +12,6 @@ required_players = 20 required_enemies = 1 recommended_enemies = 1 - enemy_minimum_age = 14 round_ends_with_antag_death = 1 announce_span = "danger" announce_text = "There is a space wizard attacking the station!\n\ diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index 241a59cc723a0..c9dab5ba749da 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -120,7 +120,7 @@ /obj/structure/spider/eggcluster/attack_ghost(mob/user) . = ..() - if(!can_take_ghost_spawner(user?.client, BAN_ROLE_SPIDER, TRUE, flags_1 & ADMIN_SPAWNED_1)) + if(!can_take_ghost_spawner(user?.client, BAN_ROLE_SPIDER, TRUE, is_ghost_role = FALSE)) return if(ghost_ready) make_spider(user) diff --git a/code/game/objects/structures/fugitive_role_spawners.dm b/code/game/objects/structures/fugitive_role_spawners.dm index 4381b58fdb68c..8405d6deba27f 100644 --- a/code/game/objects/structures/fugitive_role_spawners.dm +++ b/code/game/objects/structures/fugitive_role_spawners.dm @@ -11,6 +11,7 @@ icon = 'icons/obj/machines/sleeper.dmi' icon_state = "sleeper" banType = BAN_ROLE_FUGITIVE_HUNTER + is_antagonist = TRUE /// This is set by the shuttle template var/datum/fugitive_type/hunter/backstory var/static/leader_spawned = FALSE diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm index df2b6913075c7..c6c3a5215e9f2 100644 --- a/code/game/objects/structures/ghost_role_spawners.dm +++ b/code/game/objects/structures/ghost_role_spawners.dm @@ -497,6 +497,7 @@ flavour_text = "The station refused to pay for your protection, protect the ship, siphon the credits from the station and raid it for even more loot." assignedrole = BAN_ROLE_SPACE_PIRATE banType = BAN_ROLE_SPACE_PIRATE + is_antagonist = TRUE var/rank = "Mate" /obj/effect/mob_spawn/human/pirate/special(mob/living/new_spawn) diff --git a/code/modules/admin/sql_ban_system.dm b/code/modules/admin/sql_ban_system.dm index 566e122dfc5f3..65c6fd9dfaa0e 100644 --- a/code/modules/admin/sql_ban_system.dm +++ b/code/modules/admin/sql_ban_system.dm @@ -1,6 +1,19 @@ #define MAX_ADMINBANS_PER_ADMIN 1 #define MAX_ADMINBANS_PER_HEADMIN 3 +/// Process global ban types +/proc/check_role_ban(ban_cache, role) + if(role in GLOB.antagonist_bannable_roles) + if((BAN_ROLE_ALL_ANTAGONISTS in ban_cache) || ("Syndicate" in ban_cache)) // Legacy "Syndicate" ban + return TRUE + if(role in GLOB.forced_bannable_roles) + if(BAN_ROLE_FORCED_ANTAGONISTS in ban_cache) + return TRUE + if(role in GLOB.ghost_role_bannable_roles) + if(BAN_ROLE_ALL_GHOST in ban_cache) + return TRUE + return role in ban_cache + //checks client ban cache or DB ban table if ckey is banned from one or more roles //doesn't return any details, use only for if statements /proc/is_banned_from(player_ckey, list/roles) @@ -12,9 +25,9 @@ build_ban_cache(C) if(islist(roles)) for(var/R in roles) - if(R in C.ban_cache) + if(check_role_ban(C.ban_cache, R)) return TRUE //they're banned from at least one role, no need to keep checking - else if(roles in C.ban_cache) + else if(check_role_ban(C.ban_cache, roles)) return TRUE else var/values = list( @@ -327,10 +340,9 @@ output += "
" var/list/long_job_lists = list( "Civilian" = GLOB.civilian_positions | JOB_NAME_GIMMICK, - "Special Ban Types" = list(BAN_ROLE_ALL_ANTAGONISTS, BAN_ROLE_ALL_ANTAGONISTS_AND_FORCED, BAN_ROLE_ALL_GHOST_ROLES), - "Antagonist Positions" = GLOB.antagonist_bannable_roles, - "Forced Antagonist Positions" = GLOB.forced_bannable_roles, - "Ghost Roles" = GLOB.ghost_role_bannable_roles, + "Antagonist Positions" = list(BAN_ROLE_ALL_ANTAGONISTS) + GLOB.antagonist_bannable_roles, + "Forced Antagonist Positions" = list(BAN_ROLE_FORCED_ANTAGONISTS) + GLOB.forced_bannable_roles, + "Ghost Roles" = list(BAN_ROLE_ALL_GHOST) + GLOB.ghost_role_bannable_roles, "Other" = GLOB.other_bannable_roles, ) diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm index 469b049da44ed..c31c3066901a2 100644 --- a/code/modules/admin/verbs/one_click_antag.dm +++ b/code/modules/admin/verbs/one_click_antag.dm @@ -60,9 +60,8 @@ for(var/mob/living/carbon/human/applicant in GLOB.player_list) if(isReadytoRumble(applicant, BAN_ROLE_TRAITOR, /datum/role_preference/midround_living/traitor)) - if(temp.age_check(applicant.client)) - if(!(applicant.job in temp.restricted_jobs)) - candidates += applicant + if(!(applicant.job in temp.restricted_jobs)) + candidates += applicant if(candidates.len) var/numTraitors = min(candidates.len, maxCount) @@ -95,9 +94,8 @@ for(var/mob/living/carbon/human/applicant in GLOB.player_list) if(isReadytoRumble(applicant, BAN_ROLE_CHANGELING, /datum/role_preference/antagonist/changeling)) - if(temp.age_check(applicant.client)) - if(!(applicant.job in temp.restricted_jobs)) - candidates += applicant + if(!(applicant.job in temp.restricted_jobs)) + candidates += applicant if(candidates.len) var/numChangelings = min(candidates.len, maxCount) @@ -125,9 +123,8 @@ for(var/mob/living/carbon/human/applicant in GLOB.player_list) if(isReadytoRumble(applicant, BAN_ROLE_REV_HEAD, /datum/role_preference/antagonist/revolutionary)) - if(temp.age_check(applicant.client)) - if(!(applicant.job in temp.restricted_jobs)) - candidates += applicant + if(!(applicant.job in temp.restricted_jobs)) + candidates += applicant if(candidates.len) var/numRevs = min(candidates.len, maxCount) @@ -167,9 +164,8 @@ for(var/mob/living/carbon/human/applicant in GLOB.player_list) if(isReadytoRumble(applicant, BAN_ROLE_CULTIST, /datum/role_preference/antagonist/blood_cultist)) - if(temp.age_check(applicant.client)) - if(!(applicant.job in temp.restricted_jobs)) - candidates += applicant + if(!(applicant.job in temp.restricted_jobs)) + candidates += applicant if(candidates.len) var/numCultists = min(candidates.len, maxCount) diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index 3cb92de24aaf9..36715aee7c5bb 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -128,7 +128,7 @@ GLOBAL_LIST(admin_antag_list) owner.current.ghostize(FALSE) owner.current.key = C.key else - owenr.current.playable_bantype = banning_key + owner.current.playable_bantype = banning_key owner.current.ghostize(FALSE,SENTIENCE_FORCE) ///Called by the remove_antag_datum() and remove_all_antag_datums() mind procs for the antag datum to handle its own removal and deletion. diff --git a/code/modules/antagonists/ashwalker/ashwalker.dm b/code/modules/antagonists/ashwalker/ashwalker.dm index 1e0e54b8e9037..e25c564ea4d6f 100644 --- a/code/modules/antagonists/ashwalker/ashwalker.dm +++ b/code/modules/antagonists/ashwalker/ashwalker.dm @@ -4,7 +4,7 @@ /datum/antagonist/ashwalker name = "Ash Walker" - banning_key = BAN_ROLE_ASHKWALKER + banning_key = BAN_ROLE_ASHWALKER show_in_antagpanel = FALSE show_to_ghosts = TRUE prevent_roundtype_conversion = FALSE diff --git a/code/modules/antagonists/changeling/powers/teratoma.dm b/code/modules/antagonists/changeling/powers/teratoma.dm index 56b0fd907ebff..0fd62d4059baf 100644 --- a/code/modules/antagonists/changeling/powers/teratoma.dm +++ b/code/modules/antagonists/changeling/powers/teratoma.dm @@ -23,7 +23,7 @@ var/datum/antagonist/changeling/c = user.mind.has_antag_datum(/datum/antagonist/changeling) c.chem_charges -= chemical_cost //I'm taking your chemicals hostage! var/turf/A = get_turf(user) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a living teratoma?", BAN_ROLE_TERATOMA, null, null, 5 SECONDS, POLL_IGNORE_TERATOMA) //players must answer rapidly + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a living teratoma?", BAN_ROLE_TERATOMA, null, null, 5 SECONDS, null/*TODO POLL_IGNORE_TERATOMA*/) //players must answer rapidly if(!LAZYLEN(candidates)) //if we got at least one candidate, they're teratoma now to_chat(usr, "You fail at creating a tumor. Perhaps you should try again later?") c.chem_charges += chemical_cost //If it fails we want to refund the chemicals diff --git a/code/modules/antagonists/clock_cult/mobs/cogscarab.dm b/code/modules/antagonists/clock_cult/mobs/cogscarab.dm index f92bca1a3a922..a0bf309af5428 100644 --- a/code/modules/antagonists/clock_cult/mobs/cogscarab.dm +++ b/code/modules/antagonists/clock_cult/mobs/cogscarab.dm @@ -65,11 +65,6 @@ GLOBAL_LIST_INIT(cogscarabs, list()) /obj/effect/mob_spawn/drone/cogscarab/attack_ghost(mob/user) if(is_banned_from(user.ckey, BAN_ROLE_SERVANT_OF_RATVAR) || QDELETED(src) || QDELETED(user)) return - if(CONFIG_GET(flag/use_age_restriction_for_jobs)) - if(!isnum(user.client.player_age)) //apparently what happens when there's no DB connected. just don't let anybody be a drone without admin intervention - if(user.client.player_age < 14) - to_chat(user, "You're too new to play as a drone! Please try again in [14 - user.client.player_age] days.") - return if(!SSticker.mode) to_chat(user, "Can't become a cogscarab before the game has started.") return diff --git a/code/modules/antagonists/role_preference/_role_preference.dm b/code/modules/antagonists/role_preference/_role_preference.dm index 2d79cb6eee893..31ca5ace4063b 100644 --- a/code/modules/antagonists/role_preference/_role_preference.dm +++ b/code/modules/antagonists/role_preference/_role_preference.dm @@ -1,16 +1,7 @@ /datum/role_preference var/name - /// A brief description of this role, to display in the preferences menu. - var/description - /// The main gamemode that spawns this ROLE_X roundstart. - /// This is used to get exp_living requirements by the prefs menu. - /// TODO tgui-prefs replace this - var/gamemode /// What heading to display this entry under in the preferences menu. Use ROLE_PREFERENCE_CATEGORY defines. var/category - /// If this preference is enabled by default. This should be true for ghost polled antagonists, - /// but disabled for roundstart, latejoin, or midround assigned antagonists. - var/enabled_by_default = FALSE /// The base abstract path for this subtype. var/abstract_type = /datum/role_preference @@ -24,8 +15,12 @@ category = ROLE_PREFERENCE_CATEGORY_MIDROUND_LIVING abstract_type = /datum/role_preference/midround_living -/// Includes anything polled from ghosts. +/// Includes anything polled from ghosts that does antagonist stuff /datum/role_preference/midround_ghost category = ROLE_PREFERENCE_CATEGORY_MIDROUND_GHOST abstract_type = /datum/role_preference/midround_ghost - enabled_by_default = TRUE + +/// Ghost roles that are non antagonists +/datum/role_preference/ghost_role + category = ROLE_PREFERENCE_CATEGORY_GHOST_ROLES + abstract_type = /datum/role_preference/ghost_role diff --git a/code/modules/antagonists/role_preference/role_antagonists.dm b/code/modules/antagonists/role_preference/role_antagonists.dm new file mode 100644 index 0000000000000..8c71a2df5b4ff --- /dev/null +++ b/code/modules/antagonists/role_preference/role_antagonists.dm @@ -0,0 +1,32 @@ +/datum/role_preference/antagonist/blood_brother + name = "Blood Brother" + +/datum/role_preference/antagonist/blood_cultist + name = "Blood Cultist" + +/datum/role_preference/antagonist/clock_cultist + name = "Clock Cultist" + +/datum/role_preference/antagonist/devil + name = "Devil" + +/datum/role_preference/antagonist/revolutionary + name = "Head Revolutionary" + +/datum/role_preference/antagonist/heretic + name = "Heretic" + +/datum/role_preference/antagonist/hivemind_host + name = "Hivemind Host" + +/datum/role_preference/antagonist/incursionist + name = "Incursionist" + +/datum/role_preference/antagonist/excommunicate + name = "Excommunicated Syndicate Agent" + +/datum/role_preference/antagonist/gangster + name = "Gangster" + +/datum/role_preference/antagonist/internal_affairs + name = "Internal Affairs Agent" diff --git a/code/modules/antagonists/role_preference/role_changeling.dm b/code/modules/antagonists/role_preference/role_changeling.dm index d22f7a4d4df60..78e6490241da5 100644 --- a/code/modules/antagonists/role_preference/role_changeling.dm +++ b/code/modules/antagonists/role_preference/role_changeling.dm @@ -1,6 +1,2 @@ /datum/role_preference/antagonist/changeling name = "Changeling" - description = "A highly intelligent alien predator that is capable of altering their \ - shape to flawlessly resemble a human. Transform yourself or others into different identities, and buy from an \ - arsenal of biological weaponry with the DNA you collect." - gamemode = /datum/game_mode/changeling diff --git a/code/modules/antagonists/role_preference/role_ghost.dm b/code/modules/antagonists/role_preference/role_ghost.dm new file mode 100644 index 0000000000000..8c8d6360e879b --- /dev/null +++ b/code/modules/antagonists/role_preference/role_ghost.dm @@ -0,0 +1 @@ +///datum/role_preference/ghost_role/test diff --git a/code/modules/antagonists/role_preference/role_midrounds.dm b/code/modules/antagonists/role_preference/role_midrounds.dm new file mode 100644 index 0000000000000..404f8d6a211ba --- /dev/null +++ b/code/modules/antagonists/role_preference/role_midrounds.dm @@ -0,0 +1,50 @@ +/datum/role_preference/midround_ghost/blob + name = "Blob" + +/datum/role_preference/midround_ghost/xenomorph + name = "Xenomorph" + +/datum/role_preference/midround_ghost/nightmare + name = "Nightmare" + +/datum/role_preference/midround_ghost/space_dragon + name = "Space Dragon" + +/datum/role_preference/midround_ghost/abductor + name = "Abductor" + +/datum/role_preference/midround_ghost/space_pirate + name = "Space Pirate" + +/datum/role_preference/midround_ghost/revenant + name = "Revenant" + +/datum/role_preference/midround_ghost/spider + name = "Spider" + +/datum/role_preference/midround_ghost/swarmer + name = "Swarmer" + +/datum/role_preference/midround_ghost/morph + name = "Morph" + +/datum/role_preference/midround_ghost/fugitive + name = "Fugitive" + +/datum/role_preference/midround_ghost/fugitive_hunter + name = "Fugitive Hunter" + +/datum/role_preference/midround_ghost/slaughter_demon + name = "Slaughter Demon" + +/datum/role_preference/midround_ghost/devil + name = "Devil (Midround)" + +/datum/role_preference/midround_ghost/ninja + name = "Ninja" + +/datum/role_preference/midround_living/malfunctioning_ai + name = "Malfunctioning AI" + +/datum/role_preference/midround_living/obsessed + name = "Obsessed" diff --git a/code/modules/antagonists/role_preference/role_operative.dm b/code/modules/antagonists/role_preference/role_operative.dm index 65cf3a63bdb9d..6b4c78c126bd3 100644 --- a/code/modules/antagonists/role_preference/role_operative.dm +++ b/code/modules/antagonists/role_preference/role_operative.dm @@ -1,18 +1,5 @@ -#define OPERATIVE_DESC "Congratulations, agent. You have been chosen to join the Syndicate \ - Nuclear Operative strike team. Your mission, whether or not you choose \ - to accept it, is to destroy Nanotrasen's most advanced research facility! \ - That's right, you're going to Space Station 13. \ - Retrieve the nuclear authentication disk, use it to activate the nuclear \ - fission explosive, and destroy the station." - /datum/role_preference/antagonist/nuclear_operative name = "Nuclear Operative" - description = OPERATIVE_DESC - gamemode = /datum/game_mode/nuclear /datum/role_preference/midround_ghost/nuclear_operative name = "Nuclear Operative (Assailant)" - description = OPERATIVE_DESC - role_key = ROLE_OPERATIVE - -#undef OPERATIVE_DESC diff --git a/code/modules/antagonists/role_preference/role_traitor.dm b/code/modules/antagonists/role_preference/role_traitor.dm index a50da0c88f3e4..78db926815e20 100644 --- a/code/modules/antagonists/role_preference/role_traitor.dm +++ b/code/modules/antagonists/role_preference/role_traitor.dm @@ -1,15 +1,5 @@ -#define TRAITOR_DESC "An unpaid debt. A score to be settled. Maybe you were just in the wrong \ - place at the wrong time. Whatever the reasons, you were selected to \ - infiltrate Space Station 13. Start with a set of sinister objectives and an uplink to purchase \ - items to get the job done." - /datum/role_preference/antagonist/traitor name = "Traitor" - description = TRAITOR_DESC - gamemode = /datum/game_mode/traitor /datum/role_preference/midround_living/traitor name = "Traitor (Sleeper Agent)" - description = TRAITOR_DESC - -#undef TRAITOR_DESC diff --git a/code/modules/antagonists/role_preference/role_wizard.dm b/code/modules/antagonists/role_preference/role_wizard.dm index 35b283e7ed400..1704c8c9aaa0d 100644 --- a/code/modules/antagonists/role_preference/role_wizard.dm +++ b/code/modules/antagonists/role_preference/role_wizard.dm @@ -1,5 +1,5 @@ /datum/role_preference/antagonist/wizard name = "Wizard" - description = "GREETINGS. WE'RE THE WIZARDS OF THE WIZARD'S FEDERATION. \ - Choose between a variety of powerful spells in order to cause chaos among Space Station 13." - gamemode = /datum/game_mode/wizard + +/datum/role_preference/midround_ghost/wizard + name = "Wizard (Midround)" diff --git a/code/modules/antagonists/roundstart_special/special_antagonist.dm b/code/modules/antagonists/roundstart_special/special_antagonist.dm index cb84fe03ebd09..d18079544db11 100644 --- a/code/modules/antagonists/roundstart_special/special_antagonist.dm +++ b/code/modules/antagonists/roundstart_special/special_antagonist.dm @@ -24,9 +24,10 @@ var/max_occurrences = 1 var/holidayID = "" //Preferences - var/preference_type = /datum/role_preference/antagonist/traitor - /// If we should use antag rep. - var/use_antag_rep = FALSE + var/preference_type = null + /// If we should use antag rep. Do note that having a preference_type enables checking during gamemode execution. + var/use_antag_rep = TRUE + var/banning_key = BAN_ROLE_TRAITOR /datum/special_role/proc/setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) @@ -47,6 +48,7 @@ E.antagonist_datum = attached_antag_datum E.antag_name = role_name E.preference_type = preference_type + E.banning_key = banning_key E.protected_jobs = restricted_jobs E.typepath = /datum/round_event/create_special_antag E.weight = weight diff --git a/code/modules/antagonists/slaughter/slaughterevent.dm b/code/modules/antagonists/slaughter/slaughterevent.dm index eca6cb04dfc70..efb089f595608 100644 --- a/code/modules/antagonists/slaughter/slaughterevent.dm +++ b/code/modules/antagonists/slaughter/slaughterevent.dm @@ -14,7 +14,7 @@ role_name = "slaughter demon" /datum/round_event/ghost_role/slaughter/spawn_role() - var/list/candidates = get_candidates(BAN_ROLE_SLAUGHTER_DEMON, null, null, POLL_IGNORE_SLAUGHTER_DEMON) + var/list/candidates = get_candidates(BAN_ROLE_SLAUGHTER_DEMON, null, null, null/*TODO POLL_IGNORE_SLAUGHTER_DEMON*/) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/antagonists/space_dragon/carp_rift.dm b/code/modules/antagonists/space_dragon/carp_rift.dm index 50a191de441a1..a5b31b4347421 100644 --- a/code/modules/antagonists/space_dragon/carp_rift.dm +++ b/code/modules/antagonists/space_dragon/carp_rift.dm @@ -143,7 +143,7 @@ /obj/structure/carp_rift/attack_ghost(mob/user) . = ..() - if(can_take_ghost_spawner(user?.client, BAN_ROLE_SPACE_DRAGON, TRUE, flags_1 & ADMIN_SPAWNED_1)) + if(can_take_ghost_spawner(user?.client, BAN_ROLE_SPACE_DRAGON, TRUE, is_ghost_role = FALSE)) summon_carp(user) /** diff --git a/code/modules/antagonists/swarmer/swarmer.dm b/code/modules/antagonists/swarmer/swarmer.dm index e454e4c896a71..0f404a6587399 100644 --- a/code/modules/antagonists/swarmer/swarmer.dm +++ b/code/modules/antagonists/swarmer/swarmer.dm @@ -20,6 +20,7 @@ roundstart = FALSE assignedrole = BAN_ROLE_SWARMER banType = BAN_ROLE_SWARMER + is_antagonist = TRUE /obj/effect/mob_spawn/swarmer/Initialize(mapload) . = ..() diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index cbb58db018434..e1e7ed8889aa9 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -31,6 +31,8 @@ var/banType var/ghost_usable = TRUE var/use_cooldown = FALSE + /// If this should ignore admins disabling ghost roles (like lavaland roles), since it's actually an antagonist. + var/is_antagonist = FALSE //ATTACK GHOST IGNORING PARENT RETURN VALUE /obj/effect/mob_spawn/attack_ghost(mob/user) @@ -39,7 +41,9 @@ if(!uses) to_chat(user, "This spawner is out of charges!") return - if(!can_take_ghost_spawner(user?.client, banType, use_cooldown, flags_1 & ADMIN_SPAWNED_1)) + if(!SSticker.HasRoundStarted()) + return + if(!can_take_ghost_spawner(user?.client, banType, use_cooldown, is_ghost_role = !is_antagonist, is_admin_spawned = flags_1 & ADMIN_SPAWNED_1)) return if(QDELETED(src) || QDELETED(user)) return diff --git a/code/modules/awaymissions/mission_code/Academy.dm b/code/modules/awaymissions/mission_code/Academy.dm index ae4da29734467..7c2cbbf164640 100644 --- a/code/modules/awaymissions/mission_code/Academy.dm +++ b/code/modules/awaymissions/mission_code/Academy.dm @@ -133,7 +133,7 @@ current_wizard.ghostize(FALSE) // on the off chance braindead defender gets back in current_wizard.key = C.key else - affected_mob.playable_bantype = BAN_ROLE_WIZARD + current_wizard.playable_bantype = BAN_ROLE_WIZARD current_wizard.ghostize(FALSE,SENTIENCE_FORCE) /obj/structure/academy_wizard_spawner/proc/summon_wizard() diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 948249e4fa6d4..195be67722bc6 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -136,6 +136,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/list/dat = list(TOOLTIP_CSS_SETUP, "
") dat += "Character Settings" + dat += "Antagonist Preferences" dat += "Game Preferences" var/shop_name = "[CONFIG_GET(string/metacurrency_name)] Shop" dat += "[shop_name]" @@ -592,7 +593,9 @@ GLOBAL_LIST_EMPTY(preferences_datums) if (1) // Game Preferences - dat += "
" + dat += "" + // left box + dat += "" // left box closed + // right box + dat += "" + // right box closed - dat += "" // i hate myself for this + dat += "" dat += "" dat += "
" dat += "

General Settings

" dat += "UI Style: [UI_style]
" dat += "Outline: [toggles & PREFTOGGLE_OUTLINE_ENABLED ? "Enabled" : "Disabled"]
" @@ -645,7 +648,10 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "
" dat += "Income Updates: [(chat_toggles & CHAT_BANKCARD) ? "Allowed" : "Muted"]
" + dat += "
" dat += "

TGUI Settings

" dat += "Monitor Lock: [(toggles2 & PREFTOGGLE_2_LOCKED_TGUI) ? "Primary" : "All"]
" dat += "Window Style: [(toggles2 & PREFTOGGLE_2_FANCY_TGUI) ? "Fancy (Borderless)" : "System Window"]
" @@ -710,35 +716,62 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(CONFIG_GET(flag/preference_map_voting)) dat += "Preferred Map: [p_map]
" - dat += "
" - - dat += "

Special Role Settings

" - - if(is_banned_from(user.ckey, ROLE_SYNDICATE)) - dat += "You are banned from antagonist roles.
" - src.be_special = list() - - - for (var/i in GLOB.special_roles) - if(is_banned_from(user.ckey, i)) - dat += "Be [capitalize(i)]: BANNED
" - else - var/days_remaining = null - if(ispath(GLOB.special_roles[i]) && CONFIG_GET(flag/use_age_restriction_for_jobs)) //If it's a game mode antag, check if the player meets the minimum age - var/mode_path = GLOB.special_roles[i] - var/datum/game_mode/temp_mode = new mode_path - days_remaining = temp_mode.get_remaining_days(user.client) - - if(days_remaining) - dat += "Be [capitalize(i)]: \[IN [days_remaining] DAYS]
" - else - dat += "Be [capitalize(i)]: [(i in be_special) ? "Enabled" : "Disabled"]
" - dat += "
" + dat += "
Customize Keybinds
" + if(4) // antagonist preferences window + dat += "" + // + dat += "" + // left box closed + + // + // -------------------------------------------- + // Midround antagonists + ghostspawn roles + dat += "" + // right box closed + + dat += "
" + // -------------------------------------------- + // warning pannel + var/banned = is_banned_from(user.ckey, BAN_ROLE_ALL_ANTAGONISTS) + if(banned) + dat += "

Notification

" + dat += "You are banned from all antagonist type roles.
" + + // -------------------------------------------- + // Antagonist roles + dat += "

Antagonists

" + for (var/typepath in GLOB.role_preference_entries) + var/datum/role_preference/pref = GLOB.role_preference_entries[typepath] + if(pref.category != ROLE_PREFERENCE_CATEGORY_ANAGONIST) + continue + dat += "[pref.name]: [role_preference_enabled(parent, typepath) ? "Enabled" : "Disabled"]
" + dat += "

Midrounds (Living)

" + for (var/typepath in GLOB.role_preference_entries) + var/datum/role_preference/pref = GLOB.role_preference_entries[typepath] + if(pref.category != ROLE_PREFERENCE_CATEGORY_MIDROUND_LIVING) + continue + dat += "[pref.name]: [role_preference_enabled(parent, typepath) ? "Enabled" : "Disabled"]
" + dat += "
" + dat += "

Midrounds (Ghost)

" + for (var/typepath in GLOB.role_preference_entries) + var/datum/role_preference/pref = GLOB.role_preference_entries[typepath] + if(pref.category != ROLE_PREFERENCE_CATEGORY_MIDROUND_GHOST) + continue + dat += "[pref.name]: [role_preference_enabled(parent, typepath) ? "Enabled" : "Disabled"]
" + /*dat += "

Ghost Polls

" + for (var/typepath in GLOB.role_preference_entries) + var/datum/role_preference/pref = GLOB.role_preference_entries[typepath] + if(pref.category != ROLE_PREFERENCE_CATEGORY_GHOST_ROLES) + continue + dat += "[pref.name]: [role_preference_enabled(parent, typepath) ? "Enabled" : "Disabled"]
"*/ + dat += "
" + if(2) //Loadout var/list/type_blacklist = list() if(length(active_character.equipped_gear)) @@ -1871,10 +1904,11 @@ GLOBAL_LIST_EMPTY(preferences_datums) if("be_special") var/be_special_type = href_list["be_special_type"] - if(be_special_type in be_special) - be_special -= be_special_type - else - be_special += be_special_type + var/current = be_special["[be_special_type]"] + if(isnum(current)) + be_special["[be_special_type]"] = !current + else // not set, we assume it's on, so turn it off. + be_special["[be_special_type]"] = FALSE if("name") active_character.be_random_name = !active_character.be_random_name diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm index 489e3fb070cc5..ea1c98cb0d004 100644 --- a/code/modules/events/pirates.dm +++ b/code/modules/events/pirates.dm @@ -71,7 +71,7 @@ GLOBAL_VAR_INIT(pirates_spawned, FALSE) if(!skip_answer_check && threat?.answered == PIRATE_RESPONSE_PAY) return - var/list/candidates = pollGhostCandidates("Do you wish to be considered for pirate crew?", BAN_ROLE_SPACE_PIRATE, null, /datum/role_preference/midround_ghost/pirate) + var/list/candidates = pollGhostCandidates("Do you wish to be considered for pirate crew?", BAN_ROLE_SPACE_PIRATE, null, /datum/role_preference/midround_ghost/space_pirate) shuffle_inplace(candidates) var/datum/map_template/shuttle/pirate/default/ship = new diff --git a/code/modules/events/sentience.dm b/code/modules/events/sentience.dm index 282189e4f7a21..79cb3246f89c4 100644 --- a/code/modules/events/sentience.dm +++ b/code/modules/events/sentience.dm @@ -40,7 +40,7 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list( /datum/round_event/ghost_role/sentience/spawn_role() var/list/mob/dead/observer/candidates - candidates = get_candidates(BAN_ROLE_SENTIENT_ANIMAL, null, null, POLL_IGNORE_SENTIENT_ANIMAL) + candidates = get_candidates(BAN_ROLE_SENTIENT_ANIMAL, null, null, null/*TODO POLL_IGNORE_SENTIENT_ANIMAL*/) // find our chosen mob to breathe life into // Mobs have to be simple animals, mindless, on station, and NOT holograms. diff --git a/code/modules/mob/living/living_sentience.dm b/code/modules/mob/living/living_sentience.dm index 9214db251e98e..5700858670edd 100644 --- a/code/modules/mob/living/living_sentience.dm +++ b/code/modules/mob/living/living_sentience.dm @@ -38,6 +38,8 @@ if(key) to_chat(user, "Someone else already took [name].") return TRUE + if(!SSticker.HasRoundStarted()) + return if(!can_take_ghost_spawner(user?.client, playable_bantype, TRUE, flags_1 & ADMIN_SPAWNED_1)) return key = user.key diff --git a/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm b/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm index 1de423e2df6df..ccb4847cd14f9 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm @@ -43,12 +43,6 @@ /obj/effect/mob_spawn/drone/attack_ghost(mob/user) if(is_banned_from(user.ckey, BAN_ROLE_DRONE) || QDELETED(src) || QDELETED(user)) return - if(CONFIG_GET(flag/use_age_restriction_for_jobs)) - if(!isnum_safe(user.client.player_age)) //apparently what happens when there's no DB connected. just don't let anybody be a drone without admin intervention - return - if(user.client.player_age < DRONE_MINIMUM_AGE) - to_chat(user, "You're too new to play as a drone! Please try again in [DRONE_MINIMUM_AGE - user.client.player_age] days.") - return if(!SSticker.mode) to_chat(user, "Can't become a drone before the game has started.") return diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm index 5caa1e5231431..81143a0280e79 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm @@ -156,7 +156,7 @@ While using this makes the system rely on OnFire, it still gives options for tim addtimer(CALLBACK(src, PROC_REF(spawn_elite)), 30) return visible_message("Something within [src] stirs...") - var/list/candidates = pollCandidatesForMob("Do you want to play as a lavaland elite?", BAN_ROLE_LAVALAND_ELITE, null, null, 50, src, POLL_IGNORE_LAVALAND_ELITE) + var/list/candidates = pollCandidatesForMob("Do you want to play as a lavaland elite?", BAN_ROLE_LAVALAND_ELITE, null, null, 50, src, null/*TODO POLL_IGNORE_LAVALAND_ELITE*/) if(candidates.len) audible_message("The stirring sounds increase in volume!") elitemind = pick(candidates) diff --git a/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm b/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm index eef1aabbe9b20..99b5564d387cc 100644 --- a/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm +++ b/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm @@ -131,7 +131,7 @@ man.key = M.ckey /datum/xenoartifact_trait/minor/sentient/proc/get_canidate(obj/item/xenoartifact/X, mob/M) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the maleviolent force inside the [X.name]?", BAN_ROLE_XENOARTIFACT, null, FALSE, 8 SECONDS, POLL_IGNORE_XENOARTIFACT) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the maleviolent force inside the [X.name]?", BAN_ROLE_SENTIENT_XENOARTIFACT, null, null, 8 SECONDS, null/*TODO POLL_IGNORE_XENOARTIFACT*/) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) setup_sentience(X, C.ckey) diff --git a/config/dynamic.json b/config/dynamic.json index 83a8c953be8ee..567aacd37272f 100644 --- a/config/dynamic.json +++ b/config/dynamic.json @@ -1,38 +1,6 @@ { "Dynamic": {}, "Roundstart": { - "Traitors": { - "cost": 8, - "scaling_cost": 9, - "weight": 5, - "required_candidates": 1, - "minimum_required_age": 0, - "requirements": [ - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10 - ], - "antag_cap": { - "denominator": 24 - }, - "protected_roles": [ - "Security Officer", - "Warden", - "Detective", - "Head of Security", - "Captain" - ], - "restricted_roles": [ - "Cyborg" - ] - }, "Revolution": { "cost": 110 } diff --git a/html/admin/banpanel.css b/html/admin/banpanel.css index f19ef6e1ba2b3..a1845017da910 100644 --- a/html/admin/banpanel.css +++ b/html/admin/banpanel.css @@ -1,79 +1,87 @@ .middle { - margin-left: 5px; - margin-right: 5px; - max-width: 125px; + margin-left: 5px; + margin-right: 5px; + max-width: 125px; } .right { - margin-left: 5px; - margin-right: 5px; - width: 80px; + margin-left: 5px; + margin-right: 5px; + width: 80px; } .reason { - resize: none; - min-height: 40px; - width: 250px; - margin-bottom: 5px; + resize: none; + min-height: 40px; + width: 250px; + margin-bottom: 5px; } .rolegroup { - padding: 3px; - width: 430px; - border: none; - text-align: center; - outline: none; - display: inline-block; + padding: 3px; + width: 430px; + border: none; + text-align: center; + outline: none; + display: inline-block; } .long { - width: 860px; + width: 860px; } .content { - text-align: center; + text-align: center; } .command { - background-color: #948f02; + background-color: #948f02; } .security { - background-color: #a30000; + background-color: #a30000; } .engineering { - background-color: #fb5613; + background-color: #fb5613; } .medical { - background-color: #337296; + background-color: #337296; } .science { - background-color: #993399; + background-color: #993399; } .supply { - background-color: #a8732b; + background-color: #a8732b; } .silicon { - background-color: #ff00ff; + background-color: #ff00ff; } .abstract { - background-color: #708090; + background-color: #708090; } .civilian { - background-color: #6eaa2c; + background-color: #6eaa2c; } -.ghostandotherroles { - background-color: #5c00e6; +.ghostroles { + background-color: #5c00e6; } .antagonistpositions { - background-color: #6d3f40; + background-color: #6d3f40; +} + +.forcedantagonistpositions { + background-color: #064d10; +} + +.other { + background-color: #cf7474; } From 7c3aa2c450cd49804fa1d0abcc70214984bb7085 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 14 Jun 2023 00:43:33 -0400 Subject: [PATCH 005/269] Finish ripping out gamemode age_check --- code/__DEFINES/role_preferences.dm | 4 +++- code/__HELPERS/game.dm | 11 +++++------ code/datums/brain_damage/imaginary_friend.dm | 2 +- code/datums/brain_damage/split_personality.dm | 4 ++-- code/datums/diseases/transformation.dm | 2 +- code/game/gamemodes/changeling/changeling.dm | 3 +-- code/game/gamemodes/changeling/traitor_chan.dm | 3 +-- .../gamemodes/dynamic/dynamic_rulesets_midround.dm | 2 +- code/game/gamemodes/game_mode.dm | 2 +- code/game/machinery/cloning.dm | 2 +- code/game/objects/items/holy_weapons.dm | 2 +- code/modules/admin/fun_balloon.dm | 2 +- code/modules/admin/verbs/one_click_antag.dm | 6 +++--- code/modules/antagonists/_common/antag_datum.dm | 2 +- code/modules/antagonists/_common/antag_spawner.dm | 8 ++++---- code/modules/antagonists/blob/powers.dm | 2 +- .../modules/antagonists/changeling/powers/teratoma.dm | 2 +- .../clock_cult/scriptures/sigil_of_vitality.dm | 2 +- .../clock_cult/scriptures/summon_marauder.dm | 2 +- .../clock_cult/structure/eminence_beacon.dm | 2 +- code/modules/antagonists/cult/runes.dm | 2 +- code/modules/antagonists/heretic/heretic_knowledge.dm | 2 +- .../antagonists/heretic/knowledge/flesh_lore.dm | 2 +- code/modules/antagonists/morph/morph.dm | 2 +- code/modules/antagonists/revenant/revenant.dm | 2 +- .../antagonists/revenant/revenant_spawn_event.dm | 2 +- code/modules/antagonists/slaughter/slaughterevent.dm | 2 +- .../antagonists/traitor/equipment/contractor.dm | 2 +- .../modules/antagonists/wizard/equipment/soulstone.dm | 2 +- code/modules/awaymissions/mission_code/Academy.dm | 4 ++-- code/modules/events/abductor.dm | 2 +- code/modules/events/alien_infestation.dm | 2 +- code/modules/events/blob.dm | 2 +- code/modules/events/devil.dm | 2 +- code/modules/events/fugitive_spawning.dm | 4 ++-- code/modules/events/ghost_role.dm | 4 ++-- code/modules/events/nightmare.dm | 2 +- code/modules/events/operative.dm | 2 +- code/modules/events/pirates.dm | 2 +- code/modules/events/sentience.dm | 2 +- code/modules/events/space_dragon.dm | 2 +- code/modules/events/spider_infestation.dm | 2 +- code/modules/events/wizard/imposter.dm | 2 +- code/modules/guardian/guardian.dm | 4 ++-- code/modules/guardian/guardianbuilder.dm | 2 +- code/modules/guardian/standarrow.dm | 2 +- .../mob/living/carbon/alien/special/alien_embryo.dm | 2 +- .../mob/living/simple_animal/guardian/guardian.dm | 4 ++-- .../simple_animal/hostile/mining_mobs/elites/elite.dm | 2 +- code/modules/mob/mob_helpers.dm | 2 +- code/modules/mob/transform_procs.dm | 2 +- code/modules/ninja/ninja_event.dm | 2 +- code/modules/projectiles/projectile/magic.dm | 2 +- code/modules/religion/rites.dm | 4 ++-- .../research/xenobiology/crossbreeding/warping.dm | 2 +- code/modules/research/xenobiology/xenobiology.dm | 2 +- .../xenoarchaeology/traits/xenoartifact_minors.dm | 2 +- 57 files changed, 75 insertions(+), 76 deletions(-) diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index 7846c20c7c446..30015bfedf90d 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -161,6 +161,7 @@ GLOBAL_LIST_INIT(ghost_role_bannable_roles, list( #define BAN_ROLE_IMAGINARY_FRIEND "Imaginary Friend" #define BAN_ROLE_SPLIT_PERSONALITY "Split Personality" #define BAN_ROLE_MIND_TRANSFER "Mind Transfer Potion" +#define BAN_ROLE_ERT "Emergency Response Team" /// Other roles that don't really fit any of the above, and probably shouldn't be banned with the others as a group /// Little to no impact on anything @@ -168,6 +169,7 @@ GLOBAL_LIST_INIT(other_bannable_roles, list( BAN_ROLE_IMAGINARY_FRIEND, BAN_ROLE_SPLIT_PERSONALITY, BAN_ROLE_MIND_TRANSFER, + BAN_ROLE_ERT, )) /proc/role_preference_enabled(client/player, role_preference_key) @@ -187,7 +189,7 @@ GLOBAL_LIST_INIT(other_bannable_roles, list( /// gamemode_for_age: The gamemode that this role is typically belonging to, since gamemodes store experience requirements, it checks the player's account age. /// req_hours: The amount of living hours required to receive this role. /// feedback: if we should send a to_chat -/proc/should_include_for_role(client/player, banning_key = BAN_ROLE_ALL_ANTAGONISTS, role_preference_key = null, poll_ignore_key = null, datum/game_mode/gamemode_for_age = null, req_hours = 0, feedback = FALSE) +/proc/should_include_for_role(client/player, banning_key = BAN_ROLE_ALL_ANTAGONISTS, role_preference_key = null, poll_ignore_key = null, req_hours = 0, feedback = FALSE) if(QDELETED(player) || (poll_ignore_key && GLOB.poll_ignore[poll_ignore_key] && (player.ckey in GLOB.poll_ignore[poll_ignore_key]))) return FALSE if(role_preference_key) diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 51f363f4deb80..1f7f59c61f859 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -428,7 +428,7 @@ else candidates -= M -/proc/pollGhostCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE, req_hours = 0) +/proc/pollGhostCandidates(Question, jobbanType, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE, req_hours = 0) var/list/candidates = list() if(!(GLOB.ghost_role_flags & GHOSTROLE_STATION_SENTIENCE)) return candidates @@ -436,9 +436,9 @@ for(var/mob/dead/observer/G in GLOB.player_list) candidates += G - return pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category, flashwindow, candidates, req_hours) + return pollCandidates(Question, jobbanType, be_special_flag, poll_time, ignore_category, flashwindow, candidates, req_hours) -/proc/pollCandidates(Question, banning_key, datum/game_mode/gamemode_for_age, role_preference_key = null, poll_time = 300, poll_ignore_key = null, flashwindow = TRUE, list/group = null, req_hours = 0) +/proc/pollCandidates(Question, banning_key, role_preference_key = null, poll_time = 300, poll_ignore_key = null, flashwindow = TRUE, list/group = null, req_hours = 0) var/time_passed = world.time if (!Question) Question = "Would you like to be a special role?" @@ -452,7 +452,6 @@ banning_key = banning_key, role_preference_key = role_preference_key, poll_ignore_key = poll_ignore_key, - gamemode_for_age = gamemode_for_age, req_hours = req_hours )) continue @@ -469,13 +468,13 @@ return result /proc/pollCandidatesForMob(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, mob/M, ignore_category = null) - var/list/L = pollGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category) + var/list/L = pollGhostCandidates(Question, jobbanType, be_special_flag, poll_time, ignore_category) if(QDELETED(M) || !M.loc) return list() return L /proc/pollCandidatesForMobs(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, list/mobs, ignore_category = null) - var/list/L = pollGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category) + var/list/L = pollGhostCandidates(Question, jobbanType, be_special_flag, poll_time, ignore_category) var/i=1 for(var/v in mobs) var/atom/A = v diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index 23d747a0c4fb7..2eb5d808ee5ca 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -46,7 +46,7 @@ if(owner.stat == DEAD || !owner.mind) qdel(src) return - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s imaginary friend?", BAN_ROLE_IMAGINARY_FRIEND, null, null, 75, friend, POLL_IGNORE_IMAGINARYFRIEND) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s imaginary friend?", BAN_ROLE_IMAGINARY_FRIEND, null, 75, friend, POLL_IGNORE_IMAGINARYFRIEND) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) friend.key = C.key diff --git a/code/datums/brain_damage/split_personality.dm b/code/datums/brain_damage/split_personality.dm index 9eef8d0ba3af8..e62f03f70148a 100644 --- a/code/datums/brain_damage/split_personality.dm +++ b/code/datums/brain_damage/split_personality.dm @@ -31,7 +31,7 @@ if(owner.stat == DEAD || !owner.mind) qdel(src) return - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s split personality?", BAN_ROLE_SPLIT_PERSONALITY, null, null, 75, stranger_backseat, POLL_IGNORE_SPLITPERSONALITY) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s split personality?", BAN_ROLE_SPLIT_PERSONALITY, null, 75, stranger_backseat, POLL_IGNORE_SPLITPERSONALITY) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) stranger_backseat.key = C.key @@ -196,7 +196,7 @@ /datum/brain_trauma/severe/split_personality/brainwashing/get_ghost() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s brainwashed mind?", null, null, null, 75, stranger_backseat) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s brainwashed mind?", BAN_ROLE_TRAITOR, null, 75, stranger_backseat) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) stranger_backseat.key = C.key diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm index 9e26286a56601..519b8639614fd 100644 --- a/code/datums/diseases/transformation.dm +++ b/code/datums/diseases/transformation.dm @@ -84,7 +84,7 @@ affected_mob.ghostize(TRUE,SENTIENCE_FORCE) to_chat(affected_mob, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!") - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [affected_mob.name]?", bantype, null, null, 50, affected_mob) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [affected_mob.name]?", bantype, null, 50, affected_mob) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(affected_mob)]) to replace a jobbanned player.") diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm index c3a16eaa185ac..28147cf1c2305 100644 --- a/code/game/gamemodes/changeling/changeling.dm +++ b/code/game/gamemodes/changeling/changeling.dm @@ -78,8 +78,7 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w if(!QDELETED(character) && character.client && should_include_for_role( character.client, banning_key = banning_key, - role_preference_key = role_preference, - gamemode_for_age = src + role_preference_key = role_preference )) if(!(character.job in restricted_jobs)) character.mind.make_Changeling() diff --git a/code/game/gamemodes/changeling/traitor_chan.dm b/code/game/gamemodes/changeling/traitor_chan.dm index 6c769b3202bf1..374866d4a8808 100644 --- a/code/game/gamemodes/changeling/traitor_chan.dm +++ b/code/game/gamemodes/changeling/traitor_chan.dm @@ -76,8 +76,7 @@ if(!QDELETED(character) && character.client && should_include_for_role( character.client, banning_key = BAN_ROLE_CHANGELING, - role_preference_key = /datum/role_preference/antagonist/changeling, - gamemode_for_age = src + role_preference_key = /datum/role_preference/antagonist/changeling )) if(!(character.job in restricted_jobs)) character.mind.make_Changeling() diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index 737cf35bbac25..6d69b0ef72f58 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -113,7 +113,7 @@ message_admins("Polling [possible_volunteers.len] players to apply for the [name] ruleset.") log_game("DYNAMIC: Polling [possible_volunteers.len] players to apply for the [name] ruleset.") - candidates = pollGhostCandidates("The mode is looking for volunteers to become [initial(antag_datum.name)] for [name]", initial(antag_datum.banning_key), SSticker.mode, role_preference, poll_time = 300) + candidates = pollGhostCandidates("The mode is looking for volunteers to become [initial(antag_datum.name)] for [name]", initial(antag_datum.banning_key), role_preference, poll_time = 300) if(!length(candidates)) message_admins("The ruleset [name] received no applications.") diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 509244939015a..e9545d1845152 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -576,7 +576,7 @@ for(var/mob/living/carbon/human/player in GLOB.player_list) if(!QDELETED(player) && player.client && is_station_level(player.z) && !player.mind.special_role) - if(should_include_for_role(player.client, banning_key = banning_key, role_preference_key = role_preference, gamemode_for_age = src)) + if(should_include_for_role(player.client, banning_key = banning_key, role_preference_key = role_preference)) candidates += player.mind // Get a list of all the people who want to be the antagonist for this round var/restricted_list = length(restricted_roles) ? restricted_roles : restricted_jobs diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index c9a7326bb7494..cc46228e787d1 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -296,7 +296,7 @@ /obj/machinery/clonepod/proc/offer_to_ghost(mob/living/carbon/H) set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [H.real_name]'s experimental clone?", BAN_ROLE_EXPERIMENTAL_CLONE, null, null, 300, H, POLL_IGNORE_EXPERIMENTAL_CLONE) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [H.real_name]'s experimental clone?", BAN_ROLE_EXPERIMENTAL_CLONE, null, 300, H, POLL_IGNORE_EXPERIMENTAL_CLONE) if(length(candidates)) var/mob/dead/observer/C = pick(candidates) H.key = C.key diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm index 377133f11f578..3abd4eba2e455 100644 --- a/code/game/objects/items/holy_weapons.dm +++ b/code/game/objects/items/holy_weapons.dm @@ -580,7 +580,7 @@ possessed = TRUE - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", BAN_ROLE_SPECTRAL_BLADE, null, FALSE, 100, POLL_IGNORE_POSSESSED_BLADE) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", BAN_ROLE_SPECTRAL_BLADE, null, 100, POLL_IGNORE_POSSESSED_BLADE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) diff --git a/code/modules/admin/fun_balloon.dm b/code/modules/admin/fun_balloon.dm index 4a6e91e14af54..0b60be52e65ba 100644 --- a/code/modules/admin/fun_balloon.dm +++ b/code/modules/admin/fun_balloon.dm @@ -53,7 +53,7 @@ bodies += M var/question = "Would you like to be [group_name]?" - var/list/candidates = pollCandidatesForMobs(question, BAN_ROLE_SENTIENCE, null, FALSE, 100, bodies) + var/list/candidates = pollCandidatesForMobs(question, BAN_ROLE_SENTIENCE, null, 100, bodies) while(LAZYLEN(candidates) && LAZYLEN(bodies)) var/mob/dead/observer/C = pick_n_take(candidates) var/mob/living/body = pick_n_take(bodies) diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm index c31c3066901a2..feaa63fa24bb3 100644 --- a/code/modules/admin/verbs/one_click_antag.dm +++ b/code/modules/admin/verbs/one_click_antag.dm @@ -139,7 +139,7 @@ /datum/admins/proc/makeWizard() - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for the position of a Wizard Federation 'diplomat'?", BAN_ROLE_WIZARD, null, /datum/role_preference/midround_ghost/wizard) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for the position of a Wizard Federation 'diplomat'?", BAN_ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard) var/mob/dead/observer/selected = pick_n_take(candidates) @@ -182,7 +182,7 @@ /datum/admins/proc/makeNukeTeam(maxCount = 5) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for a nuke team being sent in?", BAN_ROLE_OPERATIVE, null, /datum/role_preference/midround_ghost/nuclear_operative) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for a nuke team being sent in?", BAN_ROLE_OPERATIVE, /datum/role_preference/midround_ghost/nuclear_operative) var/list/mob/dead/observer/chosen = list() var/mob/dead/observer/theghost = null @@ -346,7 +346,7 @@ ertemplate.enforce_human = prefs["enforce_human"]["value"] == "Yes" ? TRUE : FALSE ertemplate.opendoors = prefs["open_armory"]["value"] == "Yes" ? TRUE : FALSE - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for [ertemplate.polldesc] ?", "deathsquad", null, req_hours = 50) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for [ertemplate.polldesc] ?", BAN_ROLE_ERT, req_hours = 50) var/teamSpawned = FALSE if(candidates.len > 0) diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index 36715aee7c5bb..6b12ed1d4dbad 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -120,7 +120,7 @@ GLOBAL_LIST(admin_antag_list) /datum/antagonist/proc/replace_banned_player() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [name]?", banning_key, null, null, 50, owner.current) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [name]?", banning_key, null, 50, owner.current) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!") diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm index 6de246bf77e2f..4f5e163a1f54f 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -58,7 +58,7 @@ if(used) to_chat(H, "You already used this contract!") return - var/list/candidates = pollGhostCandidates("Do you want to play as a wizard's [href_list["school"]] apprentice?", BAN_ROLE_WIZARD, null, /datum/role_preference/midround_ghost/wizard, 150) + var/list/candidates = pollGhostCandidates("Do you want to play as a wizard's [href_list["school"]] apprentice?", BAN_ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, 150) if(LAZYLEN(candidates)) if(QDELETED(src)) return @@ -120,7 +120,7 @@ return to_chat(user, "You activate [src] and wait for confirmation.") - var/list/nuke_candidates = pollGhostCandidates("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", BAN_ROLE_OPERATIVE, null, /datum/role_preference/midround_ghost/nuclear_operative, 150) + var/list/nuke_candidates = pollGhostCandidates("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", BAN_ROLE_OPERATIVE, /datum/role_preference/midround_ghost/nuclear_operative, 150) if(LAZYLEN(nuke_candidates)) if(QDELETED(src) || !check_usability(user)) return @@ -240,7 +240,7 @@ return if(used) return - var/list/candidates = pollCandidatesForMob("Do you want to play as a [initial(demon_type.name)]?", BAN_ROLE_SLAUGHTER_DEMON, null, null, 50, src) + var/list/candidates = pollCandidatesForMob("Do you want to play as a [initial(demon_type.name)]?", BAN_ROLE_SLAUGHTER_DEMON, null, 50, src) if(LAZYLEN(candidates)) if(used || QDELETED(src)) return @@ -292,7 +292,7 @@ return to_chat(user, "You activate [src] and wait for confirmation.") - var/list/candidates = pollGhostCandidates("Do you want to play as a gangster reinforcements?", BAN_ROLE_GANG, null, /datum/role_preference/antagonist/gangster, 150) + var/list/candidates = pollGhostCandidates("Do you want to play as a gangster reinforcements?", BAN_ROLE_GANG, /datum/role_preference/antagonist/gangster, 150) if(LAZYLEN(candidates)) if(QDELETED(src) || !check_usability(user)) return diff --git a/code/modules/antagonists/blob/powers.dm b/code/modules/antagonists/blob/powers.dm index 6be3fa4c4d30b..f5eaaaf920a4b 100644 --- a/code/modules/antagonists/blob/powers.dm +++ b/code/modules/antagonists/blob/powers.dm @@ -175,7 +175,7 @@ B.naut = TRUE //temporary placeholder to prevent creation of more than one per factory. to_chat(src, "You attempt to produce a blobbernaut.") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a [blobstrain.name] blobbernaut?", BAN_ROLE_BLOB, null, /datum/role_preference/midround_ghost/blob, 50) //players must answer rapidly + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a [blobstrain.name] blobbernaut?", BAN_ROLE_BLOB, /datum/role_preference/midround_ghost/blob, 50) //players must answer rapidly if(LAZYLEN(candidates)) //if we got at least one candidate, they're a blobbernaut now. B.max_integrity = initial(B.max_integrity) * 0.25 //factories that produced a blobbernaut have much lower health B.obj_integrity = min(B.obj_integrity, B.max_integrity) diff --git a/code/modules/antagonists/changeling/powers/teratoma.dm b/code/modules/antagonists/changeling/powers/teratoma.dm index 0fd62d4059baf..1afda731e37e5 100644 --- a/code/modules/antagonists/changeling/powers/teratoma.dm +++ b/code/modules/antagonists/changeling/powers/teratoma.dm @@ -23,7 +23,7 @@ var/datum/antagonist/changeling/c = user.mind.has_antag_datum(/datum/antagonist/changeling) c.chem_charges -= chemical_cost //I'm taking your chemicals hostage! var/turf/A = get_turf(user) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a living teratoma?", BAN_ROLE_TERATOMA, null, null, 5 SECONDS, null/*TODO POLL_IGNORE_TERATOMA*/) //players must answer rapidly + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a living teratoma?", BAN_ROLE_TERATOMA, null, 5 SECONDS, null/*TODO POLL_IGNORE_TERATOMA*/) //players must answer rapidly if(!LAZYLEN(candidates)) //if we got at least one candidate, they're teratoma now to_chat(usr, "You fail at creating a tumor. Perhaps you should try again later?") c.chem_charges += chemical_cost //If it fails we want to refund the chemicals diff --git a/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm b/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm index 039e96d3e7d30..6ae17396c2ff1 100644 --- a/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm +++ b/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm @@ -53,7 +53,7 @@ if(M.mind) M.mind.grab_ghost(TRUE) else - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [M.name], an inactive clock cultist?", BAN_ROLE_SERVANT_OF_RATVAR, null, /datum/role_preference/antagonist/clock_cultist, 50, M) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [M.name], an inactive clock cultist?", BAN_ROLE_SERVANT_OF_RATVAR, /datum/role_preference/antagonist/clock_cultist, 50, M) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(M)]) to replace an AFK player.") diff --git a/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm b/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm index f0168dadbda6d..1f1174422b2ca 100644 --- a/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm +++ b/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm @@ -17,7 +17,7 @@ var/mob/dead/observer/selected /datum/clockcult/scripture/marauder/invoke() - candidates = pollGhostCandidates("Would you like to play as a clockwork marauder?", BAN_ROLE_SERVANT_OF_RATVAR, null, /datum/role_preference/antagonist/clock_cultist, 100, POLL_IGNORE_CLOCKWORK) + candidates = pollGhostCandidates("Would you like to play as a clockwork marauder?", BAN_ROLE_SERVANT_OF_RATVAR, /datum/role_preference/antagonist/clock_cultist, 100, POLL_IGNORE_CLOCKWORK) if(LAZYLEN(candidates)) selected = pick(candidates) if(!selected) diff --git a/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm b/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm index ecda97d075145..24ac2eec8e557 100644 --- a/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm +++ b/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm @@ -35,7 +35,7 @@ vote_active = FALSE used = TRUE if(!eminence) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the eminence?", BAN_ROLE_SERVANT_OF_RATVAR, null, /datum/role_preference/antagonist/clock_cultist, 100) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the eminence?", BAN_ROLE_SERVANT_OF_RATVAR, /datum/role_preference/antagonist/clock_cultist, 100) if(LAZYLEN(candidates)) eminence = pick(candidates) else diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm index 70714d2cdb3e7..3ac39cbdba869 100644 --- a/code/modules/antagonists/cult/runes.dm +++ b/code/modules/antagonists/cult/runes.dm @@ -608,7 +608,7 @@ structure_check() searches for nearby cultist structures required for the invoca mob_to_revive.grab_ghost() if(!mob_to_revive.client || mob_to_revive.client.is_afk()) set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [mob_to_revive.name], an inactive blood cultist?", BAN_ROLE_CULTIST, null, /datum/role_preference/antagonist/blood_cultist, 50, mob_to_revive) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [mob_to_revive.name], an inactive blood cultist?", BAN_ROLE_CULTIST, /datum/role_preference/antagonist/blood_cultist, 50, mob_to_revive) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) to_chat(mob_to_revive.mind, "Your physical form has been taken over by another soul due to your inactivity! Ahelp if you wish to regain your form.") diff --git a/code/modules/antagonists/heretic/heretic_knowledge.dm b/code/modules/antagonists/heretic/heretic_knowledge.dm index 79a1f1dbf40f6..ed30a6075ccb1 100644 --- a/code/modules/antagonists/heretic/heretic_knowledge.dm +++ b/code/modules/antagonists/heretic/heretic_knowledge.dm @@ -285,7 +285,7 @@ animate(summoned, 10 SECONDS, alpha = 155) message_admins("A [summoned.name] is being summoned by [ADMIN_LOOKUPFLW(user)] in [ADMIN_COORDJMP(summoned)].") - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [summoned.real_name]?", BAN_ROLE_HERETIC, null, null, 10 SECONDS, summoned) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [summoned.real_name]?", BAN_ROLE_HERETIC, null, 10 SECONDS, summoned) if(!LAZYLEN(candidates)) loc.balloon_alert(user, "Ritual failed, no ghosts") animate(summoned, 0.5 SECONDS, alpha = 0) diff --git a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm index 5a2b9b5a4cde2..f18a90c7fecbe 100644 --- a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm @@ -172,7 +172,7 @@ if(!soon_to_be_ghoul.mind || !soon_to_be_ghoul.client) message_admins("[ADMIN_LOOKUPFLW(user)] is creating a voiceless dead of a body with no player.") - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [soon_to_be_ghoul.real_name], a voiceless dead?", BAN_ROLE_HERETIC, null, null, 5 SECONDS, soon_to_be_ghoul) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [soon_to_be_ghoul.real_name], a voiceless dead?", BAN_ROLE_HERETIC, null, 5 SECONDS, soon_to_be_ghoul) if(!LAZYLEN(candidates)) loc.balloon_alert(user, "Ritual failed, no ghosts") return FALSE diff --git a/code/modules/antagonists/morph/morph.dm b/code/modules/antagonists/morph/morph.dm index 4f28a0313e4fa..1bc2403447ddf 100644 --- a/code/modules/antagonists/morph/morph.dm +++ b/code/modules/antagonists/morph/morph.dm @@ -286,7 +286,7 @@ role_name = BAN_ROLE_MORPH /datum/round_event/ghost_role/morph/spawn_role() - var/list/candidates = get_candidates(BAN_ROLE_MORPH, null, /datum/role_preference/midround_ghost/morph) + var/list/candidates = get_candidates(BAN_ROLE_MORPH, /datum/role_preference/midround_ghost/morph) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm index cd7eaba634740..62aa53a2de721 100644 --- a/code/modules/antagonists/revenant/revenant.dm +++ b/code/modules/antagonists/revenant/revenant.dm @@ -494,7 +494,7 @@ break if(!key_of_revenant) message_admins("The new revenant's old client either could not be found or is in a new, living mob - grabbing a random candidate instead...") - var/list/candidates = pollCandidatesForMob("Do you want to be [revenant.name] (reforming)?", BAN_ROLE_REVENANT, null, /datum/role_preference/midround_ghost/revenant, 50, revenant) + var/list/candidates = pollCandidatesForMob("Do you want to be [revenant.name] (reforming)?", BAN_ROLE_REVENANT, /datum/role_preference/midround_ghost/revenant, 50, revenant) if(!LAZYLEN(candidates)) qdel(revenant) message_admins("No candidates were found for the new revenant. Oh well!") diff --git a/code/modules/antagonists/revenant/revenant_spawn_event.dm b/code/modules/antagonists/revenant/revenant_spawn_event.dm index 78fe247a779d5..7f0285abe8b47 100644 --- a/code/modules/antagonists/revenant/revenant_spawn_event.dm +++ b/code/modules/antagonists/revenant/revenant_spawn_event.dm @@ -26,7 +26,7 @@ message_admins("Event attempted to spawn a revenant, but there were only [deadMobs]/[REVENANT_SPAWN_THRESHOLD] dead mobs.") return WAITING_FOR_SOMETHING - var/list/candidates = get_candidates(BAN_ROLE_REVENANT, null, /datum/role_preference/midround_ghost/revenant) + var/list/candidates = get_candidates(BAN_ROLE_REVENANT, /datum/role_preference/midround_ghost/revenant) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/antagonists/slaughter/slaughterevent.dm b/code/modules/antagonists/slaughter/slaughterevent.dm index efb089f595608..cce60eb46a8a9 100644 --- a/code/modules/antagonists/slaughter/slaughterevent.dm +++ b/code/modules/antagonists/slaughter/slaughterevent.dm @@ -14,7 +14,7 @@ role_name = "slaughter demon" /datum/round_event/ghost_role/slaughter/spawn_role() - var/list/candidates = get_candidates(BAN_ROLE_SLAUGHTER_DEMON, null, null, null/*TODO POLL_IGNORE_SLAUGHTER_DEMON*/) + var/list/candidates = get_candidates(BAN_ROLE_SLAUGHTER_DEMON, null, null/*TODO POLL_IGNORE_SLAUGHTER_DEMON*/) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/antagonists/traitor/equipment/contractor.dm b/code/modules/antagonists/traitor/equipment/contractor.dm index bd7298da06fde..01a8aae45e0f2 100644 --- a/code/modules/antagonists/traitor/equipment/contractor.dm +++ b/code/modules/antagonists/traitor/equipment/contractor.dm @@ -166,7 +166,7 @@ if (.) to_chat(user, "The uplink vibrates quietly, connecting to nearby agents...") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Contractor Support Unit for [user.real_name]?", BAN_ROLE_CONTRACTOR_SUPPORT_UNIT, null, null, 100, POLL_IGNORE_CONTRACTOR_SUPPORT) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Contractor Support Unit for [user.real_name]?", BAN_ROLE_CONTRACTOR_SUPPORT_UNIT, null, 100, POLL_IGNORE_CONTRACTOR_SUPPORT) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm index 07eaf2e0b9927..efeeb1d2de09d 100644 --- a/code/modules/antagonists/wizard/equipment/soulstone.dm +++ b/code/modules/antagonists/wizard/equipment/soulstone.dm @@ -338,7 +338,7 @@ break if(!chosen_ghost) //Failing that, we grab a ghost - var/list/consenting_candidates = pollGhostCandidates("Would you like to play as a Shade?", BAN_ROLE_CULTIST, null, null, 50, POLL_IGNORE_SHADE) + var/list/consenting_candidates = pollGhostCandidates("Would you like to play as a Shade?", BAN_ROLE_CULTIST, null, 50, POLL_IGNORE_SHADE) if(consenting_candidates.len) chosen_ghost = pick(consenting_candidates) if(!T) diff --git a/code/modules/awaymissions/mission_code/Academy.dm b/code/modules/awaymissions/mission_code/Academy.dm index 7c2cbbf164640..2923c046fc16c 100644 --- a/code/modules/awaymissions/mission_code/Academy.dm +++ b/code/modules/awaymissions/mission_code/Academy.dm @@ -125,7 +125,7 @@ if(!current_wizard) return - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as Wizard Academy Defender?", BAN_ROLE_WIZARD, null, /datum/role_preference/midround_ghost/wizard, 50, current_wizard) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as Wizard Academy Defender?", BAN_ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, 50, current_wizard) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) @@ -314,7 +314,7 @@ A.setup_master(user) servant_mind.transfer_to(H) - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [user.real_name] Servant?", BAN_ROLE_WIZARD, null, /datum/role_preference/midround_ghost/wizard, 50, H) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [user.real_name] Servant?", BAN_ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, 50, H) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) message_admins("[ADMIN_LOOKUPFLW(C)] was spawned as Dice Servant") diff --git a/code/modules/events/abductor.dm b/code/modules/events/abductor.dm index 25bc6ca0b8552..041c8d1ac7d13 100755 --- a/code/modules/events/abductor.dm +++ b/code/modules/events/abductor.dm @@ -15,7 +15,7 @@ fakeable = FALSE //Nothing to fake here /datum/round_event/ghost_role/abductor/spawn_role() - var/list/mob/dead/observer/candidates = get_candidates(BAN_ROLE_ABDUCTOR, null, /datum/role_preference/midround_ghost/abductor) + var/list/mob/dead/observer/candidates = get_candidates(BAN_ROLE_ABDUCTOR, /datum/role_preference/midround_ghost/abductor) if(candidates.len < 2) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/alien_infestation.dm b/code/modules/events/alien_infestation.dm index c453e425cba01..c395da49522c0 100644 --- a/code/modules/events/alien_infestation.dm +++ b/code/modules/events/alien_infestation.dm @@ -62,7 +62,7 @@ message_admins("An event attempted to spawn an alien but no suitable vents were found. Shutting down.") return MAP_ERROR - var/list/candidates = get_candidates(BAN_ROLE_ALIEN, null, /datum/role_preference/midround_ghost/xenomorph) + var/list/candidates = get_candidates(BAN_ROLE_ALIEN, /datum/role_preference/midround_ghost/xenomorph) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/blob.dm b/code/modules/events/blob.dm index 4a3b7eabfc375..ab8853ceee537 100644 --- a/code/modules/events/blob.dm +++ b/code/modules/events/blob.dm @@ -22,7 +22,7 @@ /datum/round_event/ghost_role/blob/spawn_role() if(!GLOB.blobstart.len) return MAP_ERROR - var/list/candidates = get_candidates(BAN_ROLE_BLOB, null, /datum/role_preference/midround_ghost/blob) + var/list/candidates = get_candidates(BAN_ROLE_BLOB, /datum/role_preference/midround_ghost/blob) if(!candidates.len) return NOT_ENOUGH_PLAYERS var/mob/dead/observer/new_blob = pick(candidates) diff --git a/code/modules/events/devil.dm b/code/modules/events/devil.dm index 5c7ce045ee2c3..cb27ccce220ff 100644 --- a/code/modules/events/devil.dm +++ b/code/modules/events/devil.dm @@ -19,7 +19,7 @@ return MAP_ERROR //selecting a candidate player - var/list/candidates = get_candidates(BAN_ROLE_DEVIL, null, /datum/role_preference/midround_ghost/devil) + var/list/candidates = get_candidates(BAN_ROLE_DEVIL, /datum/role_preference/midround_ghost/devil) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/fugitive_spawning.dm b/code/modules/events/fugitive_spawning.dm index c7287d6da1b15..adb1cae1815e7 100644 --- a/code/modules/events/fugitive_spawning.dm +++ b/code/modules/events/fugitive_spawning.dm @@ -28,7 +28,7 @@ GLOBAL_LIST_EMPTY(fugitive_backstory_selection) message_admins("No valid spawn locations found, aborting...") return MAP_ERROR var/turf/landing_turf = pick(possible_spawns) - var/list/candidates = get_candidates(BAN_ROLE_FUGITIVE, null, /datum/role_preference/midround_ghost/fugitive) + var/list/candidates = get_candidates(BAN_ROLE_FUGITIVE, /datum/role_preference/midround_ghost/fugitive) var/result = spawn_fugitives(landing_turf, candidates, spawned_mobs) if(result != SUCCESSFUL_SPAWN) return result @@ -93,7 +93,7 @@ GLOBAL_LIST_EMPTY(fugitive_backstory_selection) /proc/spawn_hunters() set waitfor = FALSE var/datum/fugitive_type/hunter/backstory = GLOB.hunter_types[admin_select_backstory(GLOB.hunter_types)] - var/list/candidates = pollGhostCandidates("The Fugitive Hunters are looking for a [backstory.name]. Would you like to be considered for this role?", BAN_ROLE_FUGITIVE_HUNTER, null, /datum/role_preference/midround_ghost/fugitive_hunter) + var/list/candidates = pollGhostCandidates("The Fugitive Hunters are looking for a [backstory.name]. Would you like to be considered for this role?", BAN_ROLE_FUGITIVE_HUNTER, /datum/role_preference/midround_ghost/fugitive_hunter) var/datum/map_template/shuttle/ship = new backstory.ship_type var/x = rand(TRANSITIONEDGE,world.maxx - TRANSITIONEDGE - ship.width) var/y = rand(TRANSITIONEDGE,world.maxy - TRANSITIONEDGE - ship.height) diff --git a/code/modules/events/ghost_role.dm b/code/modules/events/ghost_role.dm index 988d0cfe53187..70546de1fa9ab 100644 --- a/code/modules/events/ghost_role.dm +++ b/code/modules/events/ghost_role.dm @@ -55,14 +55,14 @@ // players could be found, and just runtime if anything else happens return TRUE -/datum/round_event/ghost_role/proc/get_candidates(banning_key, gametypecheck, role_preference, poll_ignore = null) +/datum/round_event/ghost_role/proc/get_candidates(banning_key, role_preference, poll_ignore = null) // Returns a list of candidates in priority order, with candidates from // `priority_candidates` first, and ghost roles randomly shuffled and // appended after var/list/mob/dead/observer/regular_candidates // don't get their hopes up if(priority_candidates.len < minimum_required) - regular_candidates = pollGhostCandidates("Do you wish to be considered for the special role of '[role_name]'?", banning_key, gametypecheck, role_preference, ignore_category = poll_ignore) + regular_candidates = pollGhostCandidates("Do you wish to be considered for the special role of '[role_name]'?", banning_key, role_preference, ignore_category = poll_ignore) else regular_candidates = list() diff --git a/code/modules/events/nightmare.dm b/code/modules/events/nightmare.dm index 7670c7a634ae5..dc95e618756f4 100644 --- a/code/modules/events/nightmare.dm +++ b/code/modules/events/nightmare.dm @@ -12,7 +12,7 @@ fakeable = FALSE /datum/round_event/ghost_role/nightmare/spawn_role() - var/list/candidates = get_candidates(BAN_ROLE_NIGHTMARE, null, /datum/role_preference/midround_ghost/nightmare) + var/list/candidates = get_candidates(BAN_ROLE_NIGHTMARE, /datum/role_preference/midround_ghost/nightmare) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/operative.dm b/code/modules/events/operative.dm index 8e5c9a7c44695..d8366aed3447a 100644 --- a/code/modules/events/operative.dm +++ b/code/modules/events/operative.dm @@ -11,7 +11,7 @@ fakeable = FALSE /datum/round_event/ghost_role/operative/spawn_role() - var/list/candidates = get_candidates(BAN_ROLE_OPERATIVE, null, /datum/role_preference/midround_ghost/nuclear_operative) + var/list/candidates = get_candidates(BAN_ROLE_OPERATIVE, /datum/role_preference/midround_ghost/nuclear_operative) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm index ea1c98cb0d004..945b02eefdb34 100644 --- a/code/modules/events/pirates.dm +++ b/code/modules/events/pirates.dm @@ -71,7 +71,7 @@ GLOBAL_VAR_INIT(pirates_spawned, FALSE) if(!skip_answer_check && threat?.answered == PIRATE_RESPONSE_PAY) return - var/list/candidates = pollGhostCandidates("Do you wish to be considered for pirate crew?", BAN_ROLE_SPACE_PIRATE, null, /datum/role_preference/midround_ghost/space_pirate) + var/list/candidates = pollGhostCandidates("Do you wish to be considered for pirate crew?", BAN_ROLE_SPACE_PIRATE, /datum/role_preference/midround_ghost/space_pirate) shuffle_inplace(candidates) var/datum/map_template/shuttle/pirate/default/ship = new diff --git a/code/modules/events/sentience.dm b/code/modules/events/sentience.dm index 79cb3246f89c4..7f6287407cba4 100644 --- a/code/modules/events/sentience.dm +++ b/code/modules/events/sentience.dm @@ -40,7 +40,7 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list( /datum/round_event/ghost_role/sentience/spawn_role() var/list/mob/dead/observer/candidates - candidates = get_candidates(BAN_ROLE_SENTIENT_ANIMAL, null, null, null/*TODO POLL_IGNORE_SENTIENT_ANIMAL*/) + candidates = get_candidates(BAN_ROLE_SENTIENT_ANIMAL, null, null/*TODO POLL_IGNORE_SENTIENT_ANIMAL*/) // find our chosen mob to breathe life into // Mobs have to be simple animals, mindless, on station, and NOT holograms. diff --git a/code/modules/events/space_dragon.dm b/code/modules/events/space_dragon.dm index 81714998e221c..7ccbf74578b37 100644 --- a/code/modules/events/space_dragon.dm +++ b/code/modules/events/space_dragon.dm @@ -26,7 +26,7 @@ message_admins("No valid spawn locations found, aborting...") return MAP_ERROR - var/list/candidates = get_candidates(BAN_ROLE_SPACE_DRAGON, null, /datum/role_preference/midround_ghost/space_dragon) + var/list/candidates = get_candidates(BAN_ROLE_SPACE_DRAGON, /datum/role_preference/midround_ghost/space_dragon) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/spider_infestation.dm b/code/modules/events/spider_infestation.dm index e095e55ea066e..dadf6d6535264 100644 --- a/code/modules/events/spider_infestation.dm +++ b/code/modules/events/spider_infestation.dm @@ -37,7 +37,7 @@ message_admins("An event attempted to spawn spiders but no suitable vents were found. Aborting.") return MAP_ERROR - var/list/candidates = get_candidates(BAN_ROLE_SPIDER, null, /datum/role_preference/midround_ghost/spider) + var/list/candidates = get_candidates(BAN_ROLE_SPIDER, /datum/role_preference/midround_ghost/spider) if(!length(candidates)) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/wizard/imposter.dm b/code/modules/events/wizard/imposter.dm index 739e57ccb9e3a..119793718e94a 100644 --- a/code/modules/events/wizard/imposter.dm +++ b/code/modules/events/wizard/imposter.dm @@ -10,7 +10,7 @@ if(!ishuman(M.current)) continue var/mob/living/carbon/human/W = M.current - var/list/candidates = pollGhostCandidates("Would you like to be an imposter wizard?", BAN_ROLE_WIZARD, null, /datum/role_preference/midround_ghost/wizard) + var/list/candidates = pollGhostCandidates("Would you like to be an imposter wizard?", BAN_ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard) if(!candidates) return //Sad Trombone var/mob/dead/observer/C = pick(candidates) diff --git a/code/modules/guardian/guardian.dm b/code/modules/guardian/guardian.dm index 21b62089b2fdb..461d893020cf7 100644 --- a/code/modules/guardian/guardian.dm +++ b/code/modules/guardian/guardian.dm @@ -584,7 +584,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians /mob/living/simple_animal/hostile/guardian/proc/ResetMe() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [summoner?.current?.name]'s [real_name]?", BAN_ROLE_HOLOPARASITE, null, null, 10 SECONDS, POLL_IGNORE_HOLOPARASITE) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [summoner?.current?.name]'s [real_name]?", BAN_ROLE_HOLOPARASITE, null, 10 SECONDS, POLL_IGNORE_HOLOPARASITE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) key = C.key @@ -677,7 +677,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians return G.next_reset = world.time + GUARDIAN_RESET_COOLDOWN to_chat(src, "You attempt to reset [G.real_name]'s personality...") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", BAN_ROLE_HOLOPARASITE, null, null, 100, POLL_IGNORE_HOLOPARASITE) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", BAN_ROLE_HOLOPARASITE, null, 100, POLL_IGNORE_HOLOPARASITE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) to_chat(G, "Your user reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance.") diff --git a/code/modules/guardian/guardianbuilder.dm b/code/modules/guardian/guardianbuilder.dm index 0bbe19700c9d6..15e3a95553868 100644 --- a/code/modules/guardian/guardianbuilder.dm +++ b/code/modules/guardian/guardianbuilder.dm @@ -225,7 +225,7 @@ used = FALSE return FALSE // IMPORTANT - if we're debugging, the user gets thrown into the stand - var/list/mob/dead/observer/candidates = debug_mode ? list(user) : pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", BAN_ROLE_HOLOPARASITE, null, null, 100, POLL_IGNORE_HOLOPARASITE) + var/list/mob/dead/observer/candidates = debug_mode ? list(user) : pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", BAN_ROLE_HOLOPARASITE, null, 100, POLL_IGNORE_HOLOPARASITE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) var/mob/living/simple_animal/hostile/guardian/G = new(user, theme, guardian_color) diff --git a/code/modules/guardian/standarrow.dm b/code/modules/guardian/standarrow.dm index 114d8400108c7..1d99e2cf00690 100644 --- a/code/modules/guardian/standarrow.dm +++ b/code/modules/guardian/standarrow.dm @@ -157,7 +157,7 @@ G.name = new_name /obj/item/stand_arrow/proc/get_stand(mob/living/carbon/H, datum/guardian_stats/stats) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Guardian Spirit of [H.real_name]?", BAN_ROLE_HOLOPARASITE, null, null, 100, POLL_IGNORE_HOLOPARASITE) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Guardian Spirit of [H.real_name]?", BAN_ROLE_HOLOPARASITE, null, 100, POLL_IGNORE_HOLOPARASITE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) var/mob/living/simple_animal/hostile/guardian/G = new(H, GUARDIAN_MAGIC, rgb(rand(1, 255), rand(1, 255), rand(1, 255))) diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm index f743b141bc181..e0102014c9442 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -86,7 +86,7 @@ bursting = TRUE - var/list/candidates = pollGhostCandidates("Do you want to play as an alien larva that will burst out of [owner]?", BAN_ROLE_ALIEN, null, /datum/role_preference/midround_ghost/xenomorph, 100, POLL_IGNORE_ALIEN_LARVA) + var/list/candidates = pollGhostCandidates("Do you want to play as an alien larva that will burst out of [owner]?", BAN_ROLE_ALIEN, /datum/role_preference/midround_ghost/xenomorph, 100, POLL_IGNORE_ALIEN_LARVA) if(QDELETED(src) || QDELETED(owner)) return diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm index d2aaa7fabc14b..2527778c15324 100644 --- a/code/modules/mob/living/simple_animal/guardian/guardian.dm +++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm @@ -459,7 +459,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians var/mob/living/simple_animal/hostile/guardian/G = input(src, "Pick the guardian you wish to reset", "Guardian Reset") as null|anything in sort_names(guardians) if(G) to_chat(src, "You attempt to reset [G.real_name]'s personality...") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", ROLE_PAI, null, FALSE, 100) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", BAN_ROLE_HOLOPARASITE, null, 100, POLL_IGNORE_HOLOPARASITE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) to_chat(G, "Your user reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance.") @@ -537,7 +537,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians return used = TRUE to_chat(user, "[use_message]") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_HOLOPARASITE) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", BAN_ROLE_HOLOPARASITE, null, 100, POLL_IGNORE_HOLOPARASITE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm index 81143a0280e79..5799bee128c5a 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm @@ -156,7 +156,7 @@ While using this makes the system rely on OnFire, it still gives options for tim addtimer(CALLBACK(src, PROC_REF(spawn_elite)), 30) return visible_message("Something within [src] stirs...") - var/list/candidates = pollCandidatesForMob("Do you want to play as a lavaland elite?", BAN_ROLE_LAVALAND_ELITE, null, null, 50, src, null/*TODO POLL_IGNORE_LAVALAND_ELITE*/) + var/list/candidates = pollCandidatesForMob("Do you want to play as a lavaland elite?", BAN_ROLE_LAVALAND_ELITE, null, 50, src, null/*TODO POLL_IGNORE_LAVALAND_ELITE*/) if(candidates.len) audible_message("The stirring sounds increase in volume!") elitemind = pick(candidates) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 2068abbd1bf3f..574e8ae693773 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -497,7 +497,7 @@ if(A) poll_message = "[poll_message] Status:[A.name]." ban_key = A.banning_key - var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ban_key, null, null, 100, M) + var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ban_key, null, 100, M) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index ba27ecc606528..939340d3b9592 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -558,7 +558,7 @@ to_chat(src, "You are job banned from cyborg! Appeal your job ban if you want to avoid this in the future!") ghostize(FALSE) - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [src]?", "[src]", null, JOB_NAME_CYBORG, 50, src) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [src]?", JOB_NAME_CYBORG, null, 50, src) if(LAZYLEN(candidates)) var/mob/dead/observer/chosen_candidate = pick(candidates) message_admins("[key_name_admin(chosen_candidate)] has taken control of ([key_name_admin(src)]) to replace a jobbanned player.") diff --git a/code/modules/ninja/ninja_event.dm b/code/modules/ninja/ninja_event.dm index f99987457525f..8b59e0257bfe7 100644 --- a/code/modules/ninja/ninja_event.dm +++ b/code/modules/ninja/ninja_event.dm @@ -45,7 +45,7 @@ Contents: return MAP_ERROR //selecting a candidate player - var/list/candidates = get_candidates(BAN_ROLE_NINJA, null, /datum/role_preference/midround_ghost/ninja) + var/list/candidates = get_candidates(BAN_ROLE_NINJA, /datum/role_preference/midround_ghost/ninja) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index afdc30caa88ab..f4dd71678fcde 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -590,7 +590,7 @@ if(A) poll_message = "[poll_message] Status:[A.name]." ban_key = A.banning_key - var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ban_key, null, null, 100, M) + var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ban_key, null, 100, M) if(M.stat == DEAD)//boo. return if(LAZYLEN(candidates)) diff --git a/code/modules/religion/rites.dm b/code/modules/religion/rites.dm index 8ad81e32ec637..8bd8614989517 100644 --- a/code/modules/religion/rites.dm +++ b/code/modules/religion/rites.dm @@ -408,7 +408,7 @@ var/turf/altar_turf = get_turf(religious_tool) new /obj/effect/temp_visual/cult/blood/long(altar_turf) new /obj/effect/temp_visual/dir_setting/curse/long(altar_turf) - var/list/candidates = pollGhostCandidates("Do you wish to be resurrected as a Holy Summoned Undead?", BAN_ROLE_HOLY_SUMMONED, null, null, 100, POLL_IGNORE_HOLYUNDEAD) + var/list/candidates = pollGhostCandidates("Do you wish to be resurrected as a Holy Summoned Undead?", BAN_ROLE_HOLY_SUMMONED, null, 100, POLL_IGNORE_HOLYUNDEAD) if(!length(candidates)) to_chat(user, "The soul pool is empty...") new /obj/effect/gibspawner/human/bodypartless(altar_turf) @@ -583,7 +583,7 @@ var/turf/altar_turf = get_turf(religious_tool) new /obj/effect/temp_visual/bluespace_fissure/long(altar_turf) user.visible_message("A tear in reality appears above the altar!") - var/list/candidates = pollGhostCandidates("Do you wish to be summoned as a Holy Carp?", BAN_ROLE_HOLY_SUMMONED, null, FALSE, 100, POLL_IGNORE_HOLYCARP) + var/list/candidates = pollGhostCandidates("Do you wish to be summoned as a Holy Carp?", BAN_ROLE_HOLY_SUMMONED, null, 100, POLL_IGNORE_HOLYCARP) if(!length(candidates)) new /obj/effect/gibspawner/generic(altar_turf) user.visible_message("The carp pool was not strong enough to bring forth a space carp.") diff --git a/code/modules/research/xenobiology/crossbreeding/warping.dm b/code/modules/research/xenobiology/crossbreeding/warping.dm index 2bc6dc8e893a9..a34e1678977bd 100644 --- a/code/modules/research/xenobiology/crossbreeding/warping.dm +++ b/code/modules/research/xenobiology/crossbreeding/warping.dm @@ -714,7 +714,7 @@ GLOBAL_DATUM(blue_storage, /obj/item/storage/backpack/holding/bluespace) return to_chat(user, "The rune is trying to repair [host.name]'s soul!") - var/list/candidates = pollCandidatesForMob("Do you want to replace the soul of [host.name]?", BAN_ROLE_SENTIENCE, null, null, 50, host, POLL_IGNORE_SHADE)//todo: fix desc + var/list/candidates = pollCandidatesForMob("Do you want to replace the soul of [host.name]?", BAN_ROLE_SENTIENCE, null, 50, host, POLL_IGNORE_SHADE)//todo: fix desc if(length(candidates) && !host.key) //check if anyone wanted to play as the dead person and check if no one's in control of the body one last time. var/mob/dead/observer/ghost = pick(candidates) diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index cd724eb20b939..bcf827494c266 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -749,7 +749,7 @@ to_chat(user, "You offer [src] to [SM]...") being_used = TRUE - var/list/candidates = pollCandidatesForMob("Do you want to play as [SM.name]? (Sentience Potion)", BAN_ROLE_SENTIENCE, null, null, 50, SM, POLL_IGNORE_SENTIENCE_POTION) // see poll_ignore.dm + var/list/candidates = pollCandidatesForMob("Do you want to play as [SM.name]? (Sentience Potion)", BAN_ROLE_SENTIENCE, null, 50, SM, POLL_IGNORE_SENTIENCE_POTION) // see poll_ignore.dm if(length(candidates)) var/mob/dead/observer/C = pick(candidates) SM.key = C.key diff --git a/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm b/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm index 99b5564d387cc..a0aa94801ffe3 100644 --- a/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm +++ b/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm @@ -131,7 +131,7 @@ man.key = M.ckey /datum/xenoartifact_trait/minor/sentient/proc/get_canidate(obj/item/xenoartifact/X, mob/M) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the maleviolent force inside the [X.name]?", BAN_ROLE_SENTIENT_XENOARTIFACT, null, null, 8 SECONDS, null/*TODO POLL_IGNORE_XENOARTIFACT*/) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the maleviolent force inside the [X.name]?", BAN_ROLE_SENTIENT_XENOARTIFACT, null, 8 SECONDS, null/*TODO POLL_IGNORE_XENOARTIFACT*/) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) setup_sentience(X, C.ckey) From f462fd32b891cfa30e69e38a566612b1da9195a7 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 14 Jun 2023 01:00:06 -0400 Subject: [PATCH 006/269] Pass tests --- code/__DEFINES/role_preferences.dm | 3 +++ code/game/gamemodes/dynamic/dynamic_simulations.dm | 5 ----- code/modules/antagonists/abductor/abductor.dm | 1 + code/modules/antagonists/blood_contract/blood_contract.dm | 2 ++ code/modules/antagonists/devil/imp/imp.dm | 1 + code/modules/antagonists/devil/sintouched/objectives.dm | 1 + code/modules/antagonists/ert/ert.dm | 1 + code/modules/antagonists/greentext/greentext.dm | 1 + code/modules/antagonists/guardian/guardian.dm | 1 + code/modules/antagonists/highlander/highlander.dm | 1 + code/modules/antagonists/magic_servant/servant.dm | 1 + code/modules/antagonists/official/official.dm | 1 + code/modules/antagonists/revenant/revenant_antag.dm | 1 + code/modules/antagonists/revolution/revolution.dm | 1 + code/modules/antagonists/santa/santa.dm | 1 + code/modules/antagonists/separatist/separatist.dm | 1 + code/modules/antagonists/survivalist/survivalist.dm | 1 + code/modules/antagonists/valentines/heartbreaker.dm | 1 + code/modules/antagonists/valentines/valentine.dm | 1 + code/modules/antagonists/wishgranter/wishgranter.dm | 1 + code/modules/unit_tests/antag_datums.dm | 2 ++ code/modules/unit_tests/gamemode_sanity.dm | 3 ++- 22 files changed, 26 insertions(+), 6 deletions(-) diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index 30015bfedf90d..79f4eeb53f889 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -172,6 +172,9 @@ GLOBAL_LIST_INIT(other_bannable_roles, list( BAN_ROLE_ERT, )) +/// Do not ban this role. Oh my god. Please. +#define UNBANNABLE_ANTAGONIST "Unbannable" + /proc/role_preference_enabled(client/player, role_preference_key) if(!ispath(role_preference_key, /datum/role_preference)) CRASH("Invalid role_preference_key [role_preference_key] passed to role_preference_enabled!") diff --git a/code/game/gamemodes/dynamic/dynamic_simulations.dm b/code/game/gamemodes/dynamic/dynamic_simulations.dm index 4b48955ce7585..acba5d1cc42b9 100644 --- a/code/game/gamemodes/dynamic/dynamic_simulations.dm +++ b/code/game/gamemodes/dynamic/dynamic_simulations.dm @@ -29,11 +29,6 @@ var/datum/client_interface/mock_client = new var/datum/preferences/prefs = new - var/list/be_special = list() - for (var/special_role in GLOB.special_roles) - be_special += special_role - - prefs.be_special = be_special mock_client.prefs = prefs mock_new_player.mock_client = mock_client diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm index 0353fac388ad6..433f9117bdc14 100644 --- a/code/modules/antagonists/abductor/abductor.dm +++ b/code/modules/antagonists/abductor/abductor.dm @@ -183,6 +183,7 @@ name = "Abductee" roundend_category = "abductees" antagpanel_category = "Abductee" + banning_key = UNBANNABLE_ANTAGONIST /datum/antagonist/abductee/on_gain() give_objective() diff --git a/code/modules/antagonists/blood_contract/blood_contract.dm b/code/modules/antagonists/blood_contract/blood_contract.dm index f2faecdd458d6..c1b79bc13b166 100644 --- a/code/modules/antagonists/blood_contract/blood_contract.dm +++ b/code/modules/antagonists/blood_contract/blood_contract.dm @@ -4,6 +4,7 @@ show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE var/duration = 2 MINUTES + banning_key = UNBANNABLE_ANTAGONIST /datum/antagonist/blood_contract/on_gain() . = ..() @@ -37,6 +38,7 @@ for(var/mob/living/carbon/human/P in GLOB.player_list) if(P == H) continue + log_game("[key_name(P)] was selected to kill [key_name(H)] by blood contract") // holy shit why is there no antag datum. I'm doing a huge refactor so I don't have time for one but I had to add this log here to_chat(P, "You have an overwhelming desire to kill [H]. [H.p_theyve(TRUE)] been marked red! Whoever [H.p_they()] [H.p_were()], friend or foe, go kill [H.p_them()]!") var/obj/item/I = new /obj/item/kitchen/knife/butcher(get_turf(P)) diff --git a/code/modules/antagonists/devil/imp/imp.dm b/code/modules/antagonists/devil/imp/imp.dm index bbe9b3978c732..2d21d1d0a10d7 100644 --- a/code/modules/antagonists/devil/imp/imp.dm +++ b/code/modules/antagonists/devil/imp/imp.dm @@ -64,6 +64,7 @@ name = "Imp" antagpanel_category = "Devil" show_in_roundend = FALSE + banning_key = BAN_ROLE_DEVIL /datum/antagonist/imp/on_gain() . = ..() diff --git a/code/modules/antagonists/devil/sintouched/objectives.dm b/code/modules/antagonists/devil/sintouched/objectives.dm index fcbb8dcbb55de..877086de53626 100644 --- a/code/modules/antagonists/devil/sintouched/objectives.dm +++ b/code/modules/antagonists/devil/sintouched/objectives.dm @@ -1,5 +1,6 @@ /datum/objective/sintouched completed = 1 + banning_key = UNBANNABLE_ANTAGONIST /datum/objective/sintouched/gluttony explanation_text = "Everything is so delicious. Go eat everything." diff --git a/code/modules/antagonists/ert/ert.dm b/code/modules/antagonists/ert/ert.dm index 9a8fc442b0596..528250607b1bf 100644 --- a/code/modules/antagonists/ert/ert.dm +++ b/code/modules/antagonists/ert/ert.dm @@ -22,6 +22,7 @@ show_to_ghosts = TRUE antag_moodlet = /datum/mood_event/focused count_against_dynamic_roll_chance = FALSE + banning_key = BAN_ROLE_ERT /datum/antagonist/ert/on_gain() if(random_names) diff --git a/code/modules/antagonists/greentext/greentext.dm b/code/modules/antagonists/greentext/greentext.dm index a40ab260538f4..3409e71041054 100644 --- a/code/modules/antagonists/greentext/greentext.dm +++ b/code/modules/antagonists/greentext/greentext.dm @@ -3,6 +3,7 @@ show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE //Not that it will be there for long count_against_dynamic_roll_chance = FALSE + banning_key = UNBANNABLE_ANTAGONIST /datum/antagonist/greentext/proc/forge_objectives() var/datum/objective/O = new /datum/objective("Succeed") diff --git a/code/modules/antagonists/guardian/guardian.dm b/code/modules/antagonists/guardian/guardian.dm index 8854023825773..06fb75a12d19d 100644 --- a/code/modules/antagonists/guardian/guardian.dm +++ b/code/modules/antagonists/guardian/guardian.dm @@ -5,6 +5,7 @@ show_in_antagpanel = FALSE var/datum/guardian_stats/stats var/datum/mind/summoner + banning_key = BAN_ROLE_HOLOPARASITE /datum/antagonist/guardian/roundend_report() var/list/parts = list() diff --git a/code/modules/antagonists/highlander/highlander.dm b/code/modules/antagonists/highlander/highlander.dm index be8064f7f5760..869be192c1aa4 100644 --- a/code/modules/antagonists/highlander/highlander.dm +++ b/code/modules/antagonists/highlander/highlander.dm @@ -5,6 +5,7 @@ show_name_in_check_antagonists = TRUE can_elimination_hijack = ELIMINATION_ENABLED count_against_dynamic_roll_chance = FALSE + banning_key = BAN_ROLE_ALL_ANTAGONISTS /datum/antagonist/highlander/apply_innate_effects(mob/living/mob_override) var/mob/living/L = owner.current || mob_override diff --git a/code/modules/antagonists/magic_servant/servant.dm b/code/modules/antagonists/magic_servant/servant.dm index 3277991446c64..7af4022d79c76 100644 --- a/code/modules/antagonists/magic_servant/servant.dm +++ b/code/modules/antagonists/magic_servant/servant.dm @@ -3,6 +3,7 @@ show_in_roundend = FALSE show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE + banning_key = BAN_ROLE_WIZARD /datum/antagonist/magic_servant/proc/setup_master(mob/M) var/datum/objective/O = new("Serve [M.real_name].") diff --git a/code/modules/antagonists/official/official.dm b/code/modules/antagonists/official/official.dm index b31421e46eaad..d54a152784aa6 100644 --- a/code/modules/antagonists/official/official.dm +++ b/code/modules/antagonists/official/official.dm @@ -6,6 +6,7 @@ var/datum/objective/mission var/datum/team/ert/ert_team show_to_ghosts = TRUE + banning_key = BAN_ROLE_ERT /datum/antagonist/official/greet() to_chat(owner, "You are a CentCom Official.") diff --git a/code/modules/antagonists/revenant/revenant_antag.dm b/code/modules/antagonists/revenant/revenant_antag.dm index 169d23d25c0da..261d6973e47e6 100644 --- a/code/modules/antagonists/revenant/revenant_antag.dm +++ b/code/modules/antagonists/revenant/revenant_antag.dm @@ -3,6 +3,7 @@ show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE show_to_ghosts = TRUE + banning_key = BAN_ROLE_REVENANT /datum/antagonist/revenant/greet() owner.announce_objectives() diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm index fa09bd74f1522..c2e5ffa4b2dc7 100644 --- a/code/modules/antagonists/revolution/revolution.dm +++ b/code/modules/antagonists/revolution/revolution.dm @@ -290,6 +290,7 @@ /datum/antagonist/revolution_enemy name = "Enemy of the Revolution" show_in_antagpanel = FALSE + banning_key = UNBANNABLE_ANTAGONIST /datum/antagonist/revolution_enemy/on_gain() owner.special_role = "revolution enemy" diff --git a/code/modules/antagonists/santa/santa.dm b/code/modules/antagonists/santa/santa.dm index fb1d304f8bb69..563e89059cb82 100644 --- a/code/modules/antagonists/santa/santa.dm +++ b/code/modules/antagonists/santa/santa.dm @@ -3,6 +3,7 @@ show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE show_to_ghosts = TRUE + banning_key = UNBANNABLE_ANTAGONIST /datum/antagonist/santa/on_gain() . = ..() diff --git a/code/modules/antagonists/separatist/separatist.dm b/code/modules/antagonists/separatist/separatist.dm index 20a6d84bdec58..cc9a84836e66a 100644 --- a/code/modules/antagonists/separatist/separatist.dm +++ b/code/modules/antagonists/separatist/separatist.dm @@ -6,6 +6,7 @@ show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE var/datum/team/nation/nation + banning_key = BAN_ROLE_ALL_ANTAGONISTS /datum/antagonist/separatist/create_team(datum/team/nation/new_team) if(!new_team) diff --git a/code/modules/antagonists/survivalist/survivalist.dm b/code/modules/antagonists/survivalist/survivalist.dm index 4c36b4e1354f0..4cd879e0a49ac 100644 --- a/code/modules/antagonists/survivalist/survivalist.dm +++ b/code/modules/antagonists/survivalist/survivalist.dm @@ -2,6 +2,7 @@ name = "Survivalist" show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE + banning_key = BAN_ROLE_SURVIVALIST var/greet_message = "" /datum/antagonist/survivalist/proc/forge_objectives() diff --git a/code/modules/antagonists/valentines/heartbreaker.dm b/code/modules/antagonists/valentines/heartbreaker.dm index ab5c39f44febc..806611937eaa7 100644 --- a/code/modules/antagonists/valentines/heartbreaker.dm +++ b/code/modules/antagonists/valentines/heartbreaker.dm @@ -3,6 +3,7 @@ roundend_category = "valentines" show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE + banning_key = BAN_ROLE_ALL_ANTAGONISTS /datum/antagonist/heartbreaker/proc/forge_objectives() diff --git a/code/modules/antagonists/valentines/valentine.dm b/code/modules/antagonists/valentines/valentine.dm index 1e0095961bc6d..8a3084f70339c 100644 --- a/code/modules/antagonists/valentines/valentine.dm +++ b/code/modules/antagonists/valentines/valentine.dm @@ -6,6 +6,7 @@ replace_banned = FALSE var/datum/mind/date count_against_dynamic_roll_chance = FALSE + banning_key = UNBANNABLE_ANTAGONIST /datum/antagonist/valentine/proc/forge_objectives() var/datum/objective/protect/protect_objective = new diff --git a/code/modules/antagonists/wishgranter/wishgranter.dm b/code/modules/antagonists/wishgranter/wishgranter.dm index 497bf49b00d01..960d49bdc467d 100644 --- a/code/modules/antagonists/wishgranter/wishgranter.dm +++ b/code/modules/antagonists/wishgranter/wishgranter.dm @@ -3,6 +3,7 @@ show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE can_elimination_hijack = ELIMINATION_ENABLED + banning_key = BAN_ROLE_ALL_ANTAGONISTS /datum/antagonist/wishgranter/proc/forge_objectives() var/datum/objective/elimination/highlander/elimination_objective = new diff --git a/code/modules/unit_tests/antag_datums.dm b/code/modules/unit_tests/antag_datums.dm index db41a0a164dbc..fdea39f2ea3b5 100644 --- a/code/modules/unit_tests/antag_datums.dm +++ b/code/modules/unit_tests/antag_datums.dm @@ -3,6 +3,8 @@ /datum/unit_test/antag_datum_sanity/Run() for (var/datum/antagonist/antag as anything in subtypesof(/datum/antagonist)) + if(ispath(antag, /datum/antagonist/custom)) + continue var/name = initial(antag.name) if (!name || name == "Antagonist") Fail("[antag] has no name set!") diff --git a/code/modules/unit_tests/gamemode_sanity.dm b/code/modules/unit_tests/gamemode_sanity.dm index 85e0ac5ae56bb..812cb7e04b41d 100644 --- a/code/modules/unit_tests/gamemode_sanity.dm +++ b/code/modules/unit_tests/gamemode_sanity.dm @@ -9,7 +9,8 @@ var/config_tag = initial(mode.config_tag) if (!config_tag) Fail("[mode] has no config_tag set!") - if(name == "event" || name == "extended" || name == "meteor" || name == "sandbox") // These gamemodes don't spawn antags and are exempt. + // These gamemodes don't spawn antags directly and are exempt. + if(name == "event" || name == "extended" || name == "meteor" || name == "sandbox" || name == "secret" || name == "dynamic") continue if (!initial(mode.banning_key)) Fail("[mode] has no banning_key set!") From b7c2f24918974c067ef4ff3f60d759529ff46308 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 14 Jun 2023 01:23:04 -0400 Subject: [PATCH 007/269] Fix --- code/__DEFINES/role_preferences.dm | 1 - code/game/gamemodes/traitor/traitor.dm | 3 +-- code/modules/antagonists/devil/sintouched/objectives.dm | 1 - code/modules/antagonists/devil/sintouched/sintouched.dm | 1 + 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index 79f4eeb53f889..7fd225ce8e76d 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -189,7 +189,6 @@ GLOBAL_LIST_INIT(other_bannable_roles, list( /// banning_key: BAN_ROLE_X used for this role - to check if the player is banned. /// role_preference_key: The /datum/role_preference typepath to check if the player has the role enabled and would like to receive the poll. /// poll_ignore_key: The POLL_IGNORE_X define for this role, used for temporarily disabling ghost polls for high volume roles. -/// gamemode_for_age: The gamemode that this role is typically belonging to, since gamemodes store experience requirements, it checks the player's account age. /// req_hours: The amount of living hours required to receive this role. /// feedback: if we should send a to_chat /proc/should_include_for_role(client/player, banning_key = BAN_ROLE_ALL_ANTAGONISTS, role_preference_key = null, poll_ignore_key = null, req_hours = 0, feedback = FALSE) diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm index 1974740cd14c0..9bc584085dfc0 100644 --- a/code/game/gamemodes/traitor/traitor.dm +++ b/code/game/gamemodes/traitor/traitor.dm @@ -96,8 +96,7 @@ if(!QDELETED(character) && character.client && should_include_for_role( character.client, banning_key = banning_key, - role_preference_key = role_preference, - gamemode_for_age = src + role_preference_key = role_preference )) if(!(character.job in restricted_jobs)) add_latejoin_traitor(character.mind) diff --git a/code/modules/antagonists/devil/sintouched/objectives.dm b/code/modules/antagonists/devil/sintouched/objectives.dm index 877086de53626..fcbb8dcbb55de 100644 --- a/code/modules/antagonists/devil/sintouched/objectives.dm +++ b/code/modules/antagonists/devil/sintouched/objectives.dm @@ -1,6 +1,5 @@ /datum/objective/sintouched completed = 1 - banning_key = UNBANNABLE_ANTAGONIST /datum/objective/sintouched/gluttony explanation_text = "Everything is so delicious. Go eat everything." diff --git a/code/modules/antagonists/devil/sintouched/sintouched.dm b/code/modules/antagonists/devil/sintouched/sintouched.dm index e11481733fbbd..1b3bfa14a62d7 100644 --- a/code/modules/antagonists/devil/sintouched/sintouched.dm +++ b/code/modules/antagonists/devil/sintouched/sintouched.dm @@ -10,6 +10,7 @@ name = "sintouched" roundend_category = "sintouched" antagpanel_category = "Devil" + banning_key = UNBANNABLE_ANTAGONIST var/sin var/static/list/sins = list(SIN_ACEDIA,SIN_GLUTTONY,SIN_GREED,SIN_SLOTH,SIN_WRATH,SIN_ENVY,SIN_PRIDE) From 58f486557a5ba599c859f50129de6cb79da22424 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 14 Jun 2023 01:32:42 -0400 Subject: [PATCH 008/269] Arrrgh --- code/modules/unit_tests/gamemode_sanity.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/unit_tests/gamemode_sanity.dm b/code/modules/unit_tests/gamemode_sanity.dm index 812cb7e04b41d..ad81a1f1615a2 100644 --- a/code/modules/unit_tests/gamemode_sanity.dm +++ b/code/modules/unit_tests/gamemode_sanity.dm @@ -10,7 +10,7 @@ if (!config_tag) Fail("[mode] has no config_tag set!") // These gamemodes don't spawn antags directly and are exempt. - if(name == "event" || name == "extended" || name == "meteor" || name == "sandbox" || name == "secret" || name == "dynamic") + if(name == "event" || name == "extended" || name == "meteor" || name == "sandbox" || name == "secret" || name == "secret extended" || name == "dynamic mode") continue if (!initial(mode.banning_key)) Fail("[mode] has no banning_key set!") From 50f30f07d0003320adaa62d3f88c5994fed49a2e Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 14 Jun 2023 13:12:37 -0400 Subject: [PATCH 009/269] Poll ignores --- beestation.dme | 1 - code/__HELPERS/game.dm | 31 +++++---- code/_globalvars/lists/poll_ignore.dm | 64 +++++++------------ code/datums/brain_damage/imaginary_friend.dm | 2 +- code/datums/brain_damage/mrat.dm | 2 +- code/datums/brain_damage/split_personality.dm | 4 +- code/datums/diseases/transformation.dm | 2 +- code/game/machinery/cloning.dm | 2 +- code/game/objects/effects/spiders.dm | 2 +- code/game/objects/items/holy_weapons.dm | 2 +- code/modules/admin/fun_balloon.dm | 2 +- code/modules/admin/secrets.dm | 2 +- code/modules/admin/verbs/one_click_antag.dm | 2 +- .../antagonists/_common/antag_datum.dm | 2 +- .../antagonists/_common/antag_spawner.dm | 8 +-- code/modules/antagonists/blob/powers.dm | 2 +- .../antagonists/changeling/powers/teratoma.dm | 2 +- .../scriptures/sigil_of_vitality.dm | 2 +- .../clock_cult/scriptures/summon_marauder.dm | 2 +- .../clock_cult/structure/eminence_beacon.dm | 2 +- code/modules/antagonists/cult/cult_comms.dm | 2 +- code/modules/antagonists/cult/runes.dm | 2 +- .../heretic/knowledge/flesh_lore.dm | 2 +- code/modules/antagonists/revenant/revenant.dm | 2 +- .../antagonists/slaughter/slaughterevent.dm | 2 +- .../traitor/equipment/contractor.dm | 2 +- .../antagonists/wizard/equipment/soulstone.dm | 2 +- .../awaymissions/mission_code/Academy.dm | 4 +- code/modules/events/fugitive_spawning.dm | 2 +- code/modules/events/holiday/xmas.dm | 2 +- code/modules/events/pirates.dm | 2 +- code/modules/events/sentience.dm | 2 +- code/modules/events/spider_infestation.dm | 2 +- code/modules/events/wizard/imposter.dm | 2 +- code/modules/guardian/guardian.dm | 4 +- code/modules/guardian/guardianbuilder.dm | 2 +- code/modules/guardian/standarrow.dm | 2 +- .../mob/dead/observer/notificationprefs.dm | 53 --------------- .../carbon/alien/special/alien_embryo.dm | 2 +- code/modules/mob/living/living_sentience.dm | 4 +- .../living/simple_animal/guardian/guardian.dm | 4 +- .../hostile/mining_mobs/elites/elite.dm | 2 +- code/modules/mob/mob_helpers.dm | 2 +- code/modules/mob/transform_procs.dm | 4 +- code/modules/projectiles/projectile/magic.dm | 2 +- code/modules/religion/rites.dm | 4 +- .../xenobiology/crossbreeding/warping.dm | 2 +- .../research/xenobiology/xenobiology.dm | 2 +- .../traits/xenoartifact_minors.dm | 2 +- .../interfaces/NotificationPreferences.js | 39 ----------- 50 files changed, 96 insertions(+), 202 deletions(-) delete mode 100644 code/modules/mob/dead/observer/notificationprefs.dm delete mode 100644 tgui/packages/tgui/interfaces/NotificationPreferences.js diff --git a/beestation.dme b/beestation.dme index a8dc51ae07cb7..a865c2fec21e7 100644 --- a/beestation.dme +++ b/beestation.dme @@ -2664,7 +2664,6 @@ #include "code\modules\mob\dead\new_player\sprite_accessories.dm" #include "code\modules\mob\dead\observer\login.dm" #include "code\modules\mob\dead\observer\logout.dm" -#include "code\modules\mob\dead\observer\notificationprefs.dm" #include "code\modules\mob\dead\observer\observer.dm" #include "code\modules\mob\dead\observer\observer_movement.dm" #include "code\modules\mob\dead\observer\orbit.dm" diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 1f7f59c61f859..e641b32d548fe 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -428,7 +428,7 @@ else candidates -= M -/proc/pollGhostCandidates(Question, jobbanType, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE, req_hours = 0) +/proc/pollGhostCandidates(Question, jobbanType, role_preference_key, poll_time = 300, ignore_category = null, flashwindow = TRUE, req_hours = 0) var/list/candidates = list() if(!(GLOB.ghost_role_flags & GHOSTROLE_STATION_SENTIENCE)) return candidates @@ -436,12 +436,17 @@ for(var/mob/dead/observer/G in GLOB.player_list) candidates += G - return pollCandidates(Question, jobbanType, be_special_flag, poll_time, ignore_category, flashwindow, candidates, req_hours) + return pollCandidates(Question, jobbanType, role_preference_key, poll_time, ignore_category, flashwindow, candidates, req_hours) /proc/pollCandidates(Question, banning_key, role_preference_key = null, poll_time = 300, poll_ignore_key = null, flashwindow = TRUE, list/group = null, req_hours = 0) var/time_passed = world.time if (!Question) Question = "Would you like to be a special role?" + if(isnull(poll_ignore_key)) // FALSE will not put one, no matter what + if(role_preference_key) + poll_ignore_key = "role_[role_preference_key]" + else if(banning_key) + poll_ignore_key = "ban_[role_preference_key]" var/list/result = list() for(var/m in group) var/mob/M = m @@ -467,14 +472,14 @@ return result -/proc/pollCandidatesForMob(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, mob/M, ignore_category = null) - var/list/L = pollGhostCandidates(Question, jobbanType, be_special_flag, poll_time, ignore_category) +/proc/pollCandidatesForMob(Question, jobbanType, role_preference_key, poll_time = 300, mob/M, ignore_category = null) + var/list/L = pollGhostCandidates(Question, jobbanType, role_preference_key, poll_time, ignore_category) if(QDELETED(M) || !M.loc) return list() return L -/proc/pollCandidatesForMobs(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, list/mobs, ignore_category = null) - var/list/L = pollGhostCandidates(Question, jobbanType, be_special_flag, poll_time, ignore_category) +/proc/pollCandidatesForMobs(Question, jobbanType, role_preference_key, poll_time = 300, list/mobs, ignore_category = null) + var/list/L = pollGhostCandidates(Question, jobbanType, role_preference_key, poll_time, ignore_category) var/i=1 for(var/v in mobs) var/atom/A = v @@ -603,13 +608,12 @@ * Arguments: * * Question: String, what do you want to ask them * * jobbanType: List, Which roles/jobs to exclude from being asked - * * gametypeCheck: Datum, Check if they have the time required for that role - * * be_special_flag: Bool, Only notify ghosts with special antag on + * * role_preference_key: * * poll_time: Integer, How long to poll for in deciseconds(0.1s) * * ignore_category: Define, ignore_category: People with this category(defined in poll_ignore.dm) turned off dont get the message * * flashwindow: Bool, Flash their window to grab their attention */ -/proc/pollMentorGhostCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE) +/proc/pollMentorGhostCandidates(Question, jobbanType, role_preference_key, poll_time = 300, ignore_category = null, flashwindow = TRUE) var/list/candidates = list() if(!(GLOB.ghost_role_flags & GHOSTROLE_STATION_SENTIENCE)) return candidates @@ -618,7 +622,7 @@ if(G.client?.is_mentor()) candidates += G - return pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category, flashwindow, candidates) + return pollCandidates(Question, jobbanType, role_preference_key, poll_time, ignore_category, flashwindow, candidates) /** * Poll mentor ghosts to take control of a mob @@ -628,14 +632,13 @@ * Arguments: * * Question: String, what do you want to ask them * * jobbanType: List, Which roles/jobs to exclude from being asked - * * gametypeCheck: Datum, Check if they have the time required for that role - * * be_special_flag: Bool, Only notify ghosts with special antag on + * * role_preference_key: Bool, Only notify ghosts with special antag on * * poll_time: Integer, How long to poll for in deciseconds(0.1s) * * M: Mob, /mob to offer * * ignore_category: Unknown */ -/proc/pollMentorCandidatesForMob(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, mob/M, ignore_category = null) - var/list/L = pollMentorGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category) +/proc/pollMentorCandidatesForMob(Question, jobbanType, role_preference_key, poll_time = 300, mob/M, ignore_category = null) + var/list/L = pollMentorGhostCandidates(Question, jobbanType, role_preference_key, poll_time, ignore_category) if(!M || QDELETED(M) || !M.loc) return list() return L diff --git a/code/_globalvars/lists/poll_ignore.dm b/code/_globalvars/lists/poll_ignore.dm index 559a667531971..d0e88a819c8b4 100644 --- a/code/_globalvars/lists/poll_ignore.dm +++ b/code/_globalvars/lists/poll_ignore.dm @@ -2,59 +2,41 @@ #define POLL_IGNORE_ALIEN_LARVA "alien_larva" #define POLL_IGNORE_ASHWALKER "ashwalker" -#define POLL_IGNORE_CLOCKWORK "clockwork" -#define POLL_IGNORE_CONSTRUCT "construct" -#define POLL_IGNORE_CONTRACTOR_SUPPORT "contractor_support" -#define POLL_IGNORE_DEFECTIVECLONE "defective_clone" -#define POLL_IGNORE_DRONE "drone" -#define POLL_IGNORE_EXPERIMENTAL_CLONE "experimental_clone" +#define POLL_IGNORE_CLOCKWORK_HELPER "clockwork_helper" +#define POLL_IGNORE_CULT_SHADE "cult_shade" #define POLL_IGNORE_GOLEM "golem" -#define POLL_IGNORE_HOLOPARASITE "holoparasite" +#define POLL_IGNORE_DRONE "drone" #define POLL_IGNORE_HOLYCARP "holy_carp" #define POLL_IGNORE_HOLYUNDEAD "holy_undead" -#define POLL_IGNORE_IMAGINARYFRIEND "imaginary_friend" -#define POLL_IGNORE_MRAT "mrat" #define POLL_IGNORE_PAI "pai" #define POLL_IGNORE_POSIBRAIN "posibrain" -#define POLL_IGNORE_POSSESSED_BLADE "possessed_blade" -#define POLL_IGNORE_PYROSLIME "slime" -#define POLL_IGNORE_SENTIENCE_POTION "sentience_potion" -#define POLL_IGNORE_SHADE "shade" #define POLL_IGNORE_SPECTRAL_BLADE "spectral_blade" +#define POLL_IGNORE_SHADE "shade" #define POLL_IGNORE_SPIDER "spider" -#define POLL_IGNORE_SPLITPERSONALITY "split_personality" -#define POLL_IGNORE_SWARMER "swarmer" -#define POLL_IGNORE_SYNDICATE "syndicate" +#define POLL_IGNORE_MRAT "mrat" +#define POLL_IGNORE_WIZARD_HELPER "wizard_helper" -GLOBAL_LIST_INIT(poll_ignore_desc, list( - POLL_IGNORE_ALIEN_LARVA = "Xenomorph larva", - POLL_IGNORE_ASHWALKER = "Ashwalker eggs", - POLL_IGNORE_CONSTRUCT = "Construct", - POLL_IGNORE_CONTRACTOR_SUPPORT = "Contractor Support Unit", - POLL_IGNORE_DEFECTIVECLONE = "Defective clone", - POLL_IGNORE_DRONE = "Drone shells", - POLL_IGNORE_EXPERIMENTAL_CLONE = "Experimental clone", - POLL_IGNORE_GOLEM = "Golems", - POLL_IGNORE_HOLOPARASITE = "Holoparasite", - POLL_IGNORE_HOLYCARP = "Holy Carp", - POLL_IGNORE_HOLYUNDEAD = "Holy Undead", - POLL_IGNORE_IMAGINARYFRIEND = "Imaginary Friend", - POLL_IGNORE_PAI = "Personal AI", - POLL_IGNORE_POSIBRAIN = "Positronic brain", - POLL_IGNORE_POSSESSED_BLADE = "Possessed blade", - POLL_IGNORE_PYROSLIME = "Slime", - POLL_IGNORE_SENTIENCE_POTION = "Sentience potion", - POLL_IGNORE_SHADE = "Shade", - POLL_IGNORE_SPECTRAL_BLADE = "Spectral blade", - POLL_IGNORE_SPIDER = "Spiders", - POLL_IGNORE_SPLITPERSONALITY = "Split Personality", - POLL_IGNORE_SWARMER = "Swarmer shells", - POLL_IGNORE_SYNDICATE = "Syndicate", +GLOBAL_LIST_INIT(poll_ignore_list, list( + POLL_IGNORE_ALIEN_LARVA, + POLL_IGNORE_ASHWALKER, + POLL_IGNORE_CLOCKWORK_HELPER, + POLL_IGNORE_CULT_SHADE, + POLL_IGNORE_GOLEM, + POLL_IGNORE_DRONE, + POLL_IGNORE_HOLYCARP, + POLL_IGNORE_HOLYUNDEAD, + POLL_IGNORE_PAI, + POLL_IGNORE_POSIBRAIN, + POLL_IGNORE_SPECTRAL_BLADE, + POLL_IGNORE_SHADE, + POLL_IGNORE_SPIDER, + POLL_IGNORE_MRAT, + POLL_IGNORE_WIZARD_HELPER, )) GLOBAL_LIST_INIT(poll_ignore, init_poll_ignore()) /proc/init_poll_ignore() . = list() - for (var/k in GLOB.poll_ignore_desc) + for (var/k in GLOB.poll_ignore_list) .[k] = list() diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index 2eb5d808ee5ca..4930749e8f762 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -46,7 +46,7 @@ if(owner.stat == DEAD || !owner.mind) qdel(src) return - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s imaginary friend?", BAN_ROLE_IMAGINARY_FRIEND, null, 75, friend, POLL_IGNORE_IMAGINARYFRIEND) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s imaginary friend?", BAN_ROLE_IMAGINARY_FRIEND, null, 7.5 SECONDS, friend) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) friend.key = C.key diff --git a/code/datums/brain_damage/mrat.dm b/code/datums/brain_damage/mrat.dm index 102b903b28bf2..fc60206d37b1a 100644 --- a/code/datums/brain_damage/mrat.dm +++ b/code/datums/brain_damage/mrat.dm @@ -12,7 +12,7 @@ /datum/brain_trauma/special/imaginary_friend/mrat/get_ghost() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollMentorCandidatesForMob("Do you want to play as [owner]'s mentor rat?", BAN_ROLE_IMAGINARY_FRIEND, null, null, 75, friend, POLL_IGNORE_MRAT) + var/list/mob/dead/observer/candidates = pollMentorCandidatesForMob("Do you want to play as [owner]'s mentor rat?", BAN_ROLE_IMAGINARY_FRIEND, null, 7.5 SECONDS, friend, POLL_IGNORE_MRAT) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) friend.key = C.key diff --git a/code/datums/brain_damage/split_personality.dm b/code/datums/brain_damage/split_personality.dm index e62f03f70148a..15d761cf4b40d 100644 --- a/code/datums/brain_damage/split_personality.dm +++ b/code/datums/brain_damage/split_personality.dm @@ -31,7 +31,7 @@ if(owner.stat == DEAD || !owner.mind) qdel(src) return - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s split personality?", BAN_ROLE_SPLIT_PERSONALITY, null, 75, stranger_backseat, POLL_IGNORE_SPLITPERSONALITY) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s split personality?", BAN_ROLE_SPLIT_PERSONALITY, null, 7.5 SECONDS, stranger_backseat) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) stranger_backseat.key = C.key @@ -196,7 +196,7 @@ /datum/brain_trauma/severe/split_personality/brainwashing/get_ghost() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s brainwashed mind?", BAN_ROLE_TRAITOR, null, 75, stranger_backseat) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s brainwashed mind?", BAN_ROLE_TRAITOR, null, 7.5 SECONDS, stranger_backseat, ignore_category = FALSE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) stranger_backseat.key = C.key diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm index 519b8639614fd..c571388f6dee5 100644 --- a/code/datums/diseases/transformation.dm +++ b/code/datums/diseases/transformation.dm @@ -84,7 +84,7 @@ affected_mob.ghostize(TRUE,SENTIENCE_FORCE) to_chat(affected_mob, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!") - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [affected_mob.name]?", bantype, null, 50, affected_mob) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [affected_mob.name]?", bantype, null, 7.5 SECONDS, affected_mob, ignore_category = FALSE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(affected_mob)]) to replace a jobbanned player.") diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index cc46228e787d1..e766815d8da01 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -296,7 +296,7 @@ /obj/machinery/clonepod/proc/offer_to_ghost(mob/living/carbon/H) set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [H.real_name]'s experimental clone?", BAN_ROLE_EXPERIMENTAL_CLONE, null, 300, H, POLL_IGNORE_EXPERIMENTAL_CLONE) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [H.real_name]'s experimental clone?", BAN_ROLE_EXPERIMENTAL_CLONE, null, 30 SECONDS, H) if(length(candidates)) var/mob/dead/observer/C = pick(candidates) H.key = C.key diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index c9dab5ba749da..cb6240d4dd45d 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -198,7 +198,7 @@ random_spider = new random_spider(get_turf(src)) random_spider.faction = faction.Copy() random_spider.spider_team = spider_team - random_spider.set_playable(BAN_ROLE_SPIDER) + random_spider.set_playable(BAN_ROLE_SPIDER, POLL_IGNORE_SPIDER) spawns_remaining-- if(!spawns_remaining) qdel(src) diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm index 3abd4eba2e455..ea9cf24e149f1 100644 --- a/code/game/objects/items/holy_weapons.dm +++ b/code/game/objects/items/holy_weapons.dm @@ -580,7 +580,7 @@ possessed = TRUE - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", BAN_ROLE_SPECTRAL_BLADE, null, 100, POLL_IGNORE_POSSESSED_BLADE) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", BAN_ROLE_SPECTRAL_BLADE, null, 10 SECONDS, ignore_category = POLL_IGNORE_SPECTRAL_BLADE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) diff --git a/code/modules/admin/fun_balloon.dm b/code/modules/admin/fun_balloon.dm index 0b60be52e65ba..2373a4691d91b 100644 --- a/code/modules/admin/fun_balloon.dm +++ b/code/modules/admin/fun_balloon.dm @@ -53,7 +53,7 @@ bodies += M var/question = "Would you like to be [group_name]?" - var/list/candidates = pollCandidatesForMobs(question, BAN_ROLE_SENTIENCE, null, 100, bodies) + var/list/candidates = pollCandidatesForMobs(question, BAN_ROLE_SENTIENCE, null, 10 SECONDS, bodies) while(LAZYLEN(candidates) && LAZYLEN(bodies)) var/mob/dead/observer/C = pick_n_take(candidates) var/mob/living/body = pick_n_take(bodies) diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm index 89201c6970e00..300a6dd20b81f 100644 --- a/code/modules/admin/secrets.dm +++ b/code/modules/admin/secrets.dm @@ -789,7 +789,7 @@ GLOBAL_DATUM_INIT(admin_secrets, /datum/admin_secrets, new) var/list/candidates = list() if (prefs["offerghosts"]["value"] == "Yes") - candidates = pollGhostCandidates(replacetext(prefs["ghostpoll"]["value"], "%TYPE%", initial(pathToSpawn.name)), BAN_ROLE_ALL_ANTAGONISTS) + candidates = pollGhostCandidates(replacetext(prefs["ghostpoll"]["value"], "%TYPE%", initial(pathToSpawn.name)), BAN_ROLE_ALL_ANTAGONISTS, ignore_category = FALSE) if (prefs["playersonly"]["value"] == "Yes" && length(candidates) < prefs["minplayers"]["value"]) message_admins("Not enough players signed up to create a portal storm, the minimum was [prefs["minplayers"]["value"]] and the number of signups [length(candidates)]") diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm index feaa63fa24bb3..198b72d4d3d98 100644 --- a/code/modules/admin/verbs/one_click_antag.dm +++ b/code/modules/admin/verbs/one_click_antag.dm @@ -139,7 +139,7 @@ /datum/admins/proc/makeWizard() - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for the position of a Wizard Federation 'diplomat'?", BAN_ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for the position of a Wizard Federation 'diplomat'?", BAN_ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, ignore_category = POLL_IGNORE_WIZARD_HELPER) var/mob/dead/observer/selected = pick_n_take(candidates) diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index 6b12ed1d4dbad..4bdfd8cbec2de 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -120,7 +120,7 @@ GLOBAL_LIST(admin_antag_list) /datum/antagonist/proc/replace_banned_player() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [name]?", banning_key, null, 50, owner.current) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [name]?", banning_key, null, 7.5 SECONDS, owner.current, ignore_category = FALSE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!") diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm index 4f5e163a1f54f..483ace1838954 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -58,7 +58,7 @@ if(used) to_chat(H, "You already used this contract!") return - var/list/candidates = pollGhostCandidates("Do you want to play as a wizard's [href_list["school"]] apprentice?", BAN_ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, 150) + var/list/candidates = pollGhostCandidates("Do you want to play as a wizard's [href_list["school"]] apprentice?", BAN_ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, 15 SECONDS, ignore_category = POLL_IGNORE_WIZARD_HELPER) if(LAZYLEN(candidates)) if(QDELETED(src)) return @@ -120,7 +120,7 @@ return to_chat(user, "You activate [src] and wait for confirmation.") - var/list/nuke_candidates = pollGhostCandidates("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", BAN_ROLE_OPERATIVE, /datum/role_preference/midround_ghost/nuclear_operative, 150) + var/list/nuke_candidates = pollGhostCandidates("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", BAN_ROLE_OPERATIVE, /datum/role_preference/midround_ghost/nuclear_operative, 15 SECONDS) if(LAZYLEN(nuke_candidates)) if(QDELETED(src) || !check_usability(user)) return @@ -240,7 +240,7 @@ return if(used) return - var/list/candidates = pollCandidatesForMob("Do you want to play as a [initial(demon_type.name)]?", BAN_ROLE_SLAUGHTER_DEMON, null, 50, src) + var/list/candidates = pollGhostCandidates("Do you want to play as a [initial(demon_type.name)]?", BAN_ROLE_SLAUGHTER_DEMON, null, 10 SECONDS, ignore_category = FALSE) if(LAZYLEN(candidates)) if(used || QDELETED(src)) return @@ -292,7 +292,7 @@ return to_chat(user, "You activate [src] and wait for confirmation.") - var/list/candidates = pollGhostCandidates("Do you want to play as a gangster reinforcements?", BAN_ROLE_GANG, /datum/role_preference/antagonist/gangster, 150) + var/list/candidates = pollGhostCandidates("Do you want to play as a gangster reinforcements?", BAN_ROLE_GANG, /datum/role_preference/antagonist/gangster, 15 SECONDS) if(LAZYLEN(candidates)) if(QDELETED(src) || !check_usability(user)) return diff --git a/code/modules/antagonists/blob/powers.dm b/code/modules/antagonists/blob/powers.dm index f5eaaaf920a4b..80f0d4fa02802 100644 --- a/code/modules/antagonists/blob/powers.dm +++ b/code/modules/antagonists/blob/powers.dm @@ -175,7 +175,7 @@ B.naut = TRUE //temporary placeholder to prevent creation of more than one per factory. to_chat(src, "You attempt to produce a blobbernaut.") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a [blobstrain.name] blobbernaut?", BAN_ROLE_BLOB, /datum/role_preference/midround_ghost/blob, 50) //players must answer rapidly + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a [blobstrain.name] blobbernaut?", BAN_ROLE_BLOB, /datum/role_preference/midround_ghost/blob, 7.5 SECONDS, ignore_category = POLL_GINORE_BLOB_HELPER) //players must answer rapidly if(LAZYLEN(candidates)) //if we got at least one candidate, they're a blobbernaut now. B.max_integrity = initial(B.max_integrity) * 0.25 //factories that produced a blobbernaut have much lower health B.obj_integrity = min(B.obj_integrity, B.max_integrity) diff --git a/code/modules/antagonists/changeling/powers/teratoma.dm b/code/modules/antagonists/changeling/powers/teratoma.dm index 1afda731e37e5..b017ff30a73e1 100644 --- a/code/modules/antagonists/changeling/powers/teratoma.dm +++ b/code/modules/antagonists/changeling/powers/teratoma.dm @@ -23,7 +23,7 @@ var/datum/antagonist/changeling/c = user.mind.has_antag_datum(/datum/antagonist/changeling) c.chem_charges -= chemical_cost //I'm taking your chemicals hostage! var/turf/A = get_turf(user) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a living teratoma?", BAN_ROLE_TERATOMA, null, 5 SECONDS, null/*TODO POLL_IGNORE_TERATOMA*/) //players must answer rapidly + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a living teratoma?", BAN_ROLE_TERATOMA, null, 7.5 SECONDS) //players must answer rapidly if(!LAZYLEN(candidates)) //if we got at least one candidate, they're teratoma now to_chat(usr, "You fail at creating a tumor. Perhaps you should try again later?") c.chem_charges += chemical_cost //If it fails we want to refund the chemicals diff --git a/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm b/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm index 6ae17396c2ff1..b2f0ee9c0c59c 100644 --- a/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm +++ b/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm @@ -53,7 +53,7 @@ if(M.mind) M.mind.grab_ghost(TRUE) else - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [M.name], an inactive clock cultist?", BAN_ROLE_SERVANT_OF_RATVAR, /datum/role_preference/antagonist/clock_cultist, 50, M) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [M.name], an inactive clock cultist?", BAN_ROLE_SERVANT_OF_RATVAR, /datum/role_preference/antagonist/clock_cultist, 7.5 SECONDS, M) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(M)]) to replace an AFK player.") diff --git a/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm b/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm index 1f1174422b2ca..0339168b1e3ca 100644 --- a/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm +++ b/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm @@ -17,7 +17,7 @@ var/mob/dead/observer/selected /datum/clockcult/scripture/marauder/invoke() - candidates = pollGhostCandidates("Would you like to play as a clockwork marauder?", BAN_ROLE_SERVANT_OF_RATVAR, /datum/role_preference/antagonist/clock_cultist, 100, POLL_IGNORE_CLOCKWORK) + candidates = pollGhostCandidates("Would you like to play as a clockwork marauder?", BAN_ROLE_SERVANT_OF_RATVAR, /datum/role_preference/antagonist/clock_cultist, 10 SECONDS, POLL_IGNORE_CLOCKWORK_HELPER) if(LAZYLEN(candidates)) selected = pick(candidates) if(!selected) diff --git a/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm b/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm index 24ac2eec8e557..52dfa4c0d1345 100644 --- a/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm +++ b/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm @@ -35,7 +35,7 @@ vote_active = FALSE used = TRUE if(!eminence) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the eminence?", BAN_ROLE_SERVANT_OF_RATVAR, /datum/role_preference/antagonist/clock_cultist, 100) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the eminence?", BAN_ROLE_SERVANT_OF_RATVAR, /datum/role_preference/antagonist/clock_cultist, 10 SECONDS) if(LAZYLEN(candidates)) eminence = pick(candidates) else diff --git a/code/modules/antagonists/cult/cult_comms.dm b/code/modules/antagonists/cult/cult_comms.dm index 402e5cdcb47cc..e84033698cc4b 100644 --- a/code/modules/antagonists/cult/cult_comms.dm +++ b/code/modules/antagonists/cult/cult_comms.dm @@ -108,7 +108,7 @@ if(B.current && B.current != Nominee && !B.current.incapacitated()) SEND_SOUND(B.current, 'sound/magic/exit_blood.ogg') asked_cultists += B.current - var/list/yes_voters = pollCandidates("[Nominee] seeks to lead your cult, do you support [Nominee.p_them()]?", poll_time = 300, group = asked_cultists) + var/list/yes_voters = pollCandidates("[Nominee] seeks to lead your cult, do you support [Nominee.p_them()]?", poll_time = 30 SECONDS, group = asked_cultists) if(QDELETED(Nominee) || Nominee.incapacitated()) team.cult_vote_called = FALSE for(var/datum/mind/B in team.members) diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm index 3ac39cbdba869..d764744526360 100644 --- a/code/modules/antagonists/cult/runes.dm +++ b/code/modules/antagonists/cult/runes.dm @@ -608,7 +608,7 @@ structure_check() searches for nearby cultist structures required for the invoca mob_to_revive.grab_ghost() if(!mob_to_revive.client || mob_to_revive.client.is_afk()) set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [mob_to_revive.name], an inactive blood cultist?", BAN_ROLE_CULTIST, /datum/role_preference/antagonist/blood_cultist, 50, mob_to_revive) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [mob_to_revive.name], an inactive blood cultist?", BAN_ROLE_CULTIST, /datum/role_preference/antagonist/blood_cultist, 7.5 SECONDS, mob_to_revive) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) to_chat(mob_to_revive.mind, "Your physical form has been taken over by another soul due to your inactivity! Ahelp if you wish to regain your form.") diff --git a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm index f18a90c7fecbe..9403798bfe578 100644 --- a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm @@ -172,7 +172,7 @@ if(!soon_to_be_ghoul.mind || !soon_to_be_ghoul.client) message_admins("[ADMIN_LOOKUPFLW(user)] is creating a voiceless dead of a body with no player.") - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [soon_to_be_ghoul.real_name], a voiceless dead?", BAN_ROLE_HERETIC, null, 5 SECONDS, soon_to_be_ghoul) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [soon_to_be_ghoul.real_name], a voiceless dead?", BAN_ROLE_HERETIC, null, 7.5 SECONDS, soon_to_be_ghoul) if(!LAZYLEN(candidates)) loc.balloon_alert(user, "Ritual failed, no ghosts") return FALSE diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm index 62aa53a2de721..62a602083b7bd 100644 --- a/code/modules/antagonists/revenant/revenant.dm +++ b/code/modules/antagonists/revenant/revenant.dm @@ -494,7 +494,7 @@ break if(!key_of_revenant) message_admins("The new revenant's old client either could not be found or is in a new, living mob - grabbing a random candidate instead...") - var/list/candidates = pollCandidatesForMob("Do you want to be [revenant.name] (reforming)?", BAN_ROLE_REVENANT, /datum/role_preference/midround_ghost/revenant, 50, revenant) + var/list/candidates = pollCandidatesForMob("Do you want to be [revenant.name] (reforming)?", BAN_ROLE_REVENANT, /datum/role_preference/midround_ghost/revenant, 7.5 SECONDS, revenant) if(!LAZYLEN(candidates)) qdel(revenant) message_admins("No candidates were found for the new revenant. Oh well!") diff --git a/code/modules/antagonists/slaughter/slaughterevent.dm b/code/modules/antagonists/slaughter/slaughterevent.dm index cce60eb46a8a9..58c1154ee34b2 100644 --- a/code/modules/antagonists/slaughter/slaughterevent.dm +++ b/code/modules/antagonists/slaughter/slaughterevent.dm @@ -14,7 +14,7 @@ role_name = "slaughter demon" /datum/round_event/ghost_role/slaughter/spawn_role() - var/list/candidates = get_candidates(BAN_ROLE_SLAUGHTER_DEMON, null, null/*TODO POLL_IGNORE_SLAUGHTER_DEMON*/) + var/list/candidates = get_candidates(BAN_ROLE_SLAUGHTER_DEMON, null) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/antagonists/traitor/equipment/contractor.dm b/code/modules/antagonists/traitor/equipment/contractor.dm index 01a8aae45e0f2..094bd9f42a85a 100644 --- a/code/modules/antagonists/traitor/equipment/contractor.dm +++ b/code/modules/antagonists/traitor/equipment/contractor.dm @@ -166,7 +166,7 @@ if (.) to_chat(user, "The uplink vibrates quietly, connecting to nearby agents...") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Contractor Support Unit for [user.real_name]?", BAN_ROLE_CONTRACTOR_SUPPORT_UNIT, null, 100, POLL_IGNORE_CONTRACTOR_SUPPORT) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Contractor Support Unit for [user.real_name]?", BAN_ROLE_CONTRACTOR_SUPPORT_UNIT, null, 10 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm index efeeb1d2de09d..32231db4b97c0 100644 --- a/code/modules/antagonists/wizard/equipment/soulstone.dm +++ b/code/modules/antagonists/wizard/equipment/soulstone.dm @@ -338,7 +338,7 @@ break if(!chosen_ghost) //Failing that, we grab a ghost - var/list/consenting_candidates = pollGhostCandidates("Would you like to play as a Shade?", BAN_ROLE_CULTIST, null, 50, POLL_IGNORE_SHADE) + var/list/consenting_candidates = pollGhostCandidates("Would you like to play as a Shade?", BAN_ROLE_CULTIST, null, 5 SECONDS, ignore_category = POLL_IGNORE_CULT_SHADE) if(consenting_candidates.len) chosen_ghost = pick(consenting_candidates) if(!T) diff --git a/code/modules/awaymissions/mission_code/Academy.dm b/code/modules/awaymissions/mission_code/Academy.dm index 2923c046fc16c..1346d79d48ba6 100644 --- a/code/modules/awaymissions/mission_code/Academy.dm +++ b/code/modules/awaymissions/mission_code/Academy.dm @@ -125,7 +125,7 @@ if(!current_wizard) return - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as Wizard Academy Defender?", BAN_ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, 50, current_wizard) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as Wizard Academy Defender?", BAN_ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, 10 SECONDS, current_wizard) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) @@ -314,7 +314,7 @@ A.setup_master(user) servant_mind.transfer_to(H) - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [user.real_name] Servant?", BAN_ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, 50, H) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [user.real_name] Servant?", BAN_ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, 10 SECONDS, H) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) message_admins("[ADMIN_LOOKUPFLW(C)] was spawned as Dice Servant") diff --git a/code/modules/events/fugitive_spawning.dm b/code/modules/events/fugitive_spawning.dm index adb1cae1815e7..09b1245d5be73 100644 --- a/code/modules/events/fugitive_spawning.dm +++ b/code/modules/events/fugitive_spawning.dm @@ -93,7 +93,7 @@ GLOBAL_LIST_EMPTY(fugitive_backstory_selection) /proc/spawn_hunters() set waitfor = FALSE var/datum/fugitive_type/hunter/backstory = GLOB.hunter_types[admin_select_backstory(GLOB.hunter_types)] - var/list/candidates = pollGhostCandidates("The Fugitive Hunters are looking for a [backstory.name]. Would you like to be considered for this role?", BAN_ROLE_FUGITIVE_HUNTER, /datum/role_preference/midround_ghost/fugitive_hunter) + var/list/candidates = pollGhostCandidates("The Fugitive Hunters are looking for a [backstory.name]. Would you like to be considered for this role?", BAN_ROLE_FUGITIVE_HUNTER, /datum/role_preference/midround_ghost/fugitive_hunter, 15 SECONDS) var/datum/map_template/shuttle/ship = new backstory.ship_type var/x = rand(TRANSITIONEDGE,world.maxx - TRANSITIONEDGE - ship.width) var/y = rand(TRANSITIONEDGE,world.maxy - TRANSITIONEDGE - ship.height) diff --git a/code/modules/events/holiday/xmas.dm b/code/modules/events/holiday/xmas.dm index 820dae9ed3715..5a39c3446f672 100644 --- a/code/modules/events/holiday/xmas.dm +++ b/code/modules/events/holiday/xmas.dm @@ -86,7 +86,7 @@ priority_announce("Santa is coming to town!", "Unknown Transmission", SSstation.announcer.get_rand_alert_sound()) /datum/round_event/santa/start() - var/list/candidates = pollGhostCandidates("Santa is coming to town! Do you want to be Santa?", poll_time=150) + var/list/candidates = pollGhostCandidates("Santa is coming to town! Do you want to be Santa?", poll_time = 15 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) santa = new /mob/living/carbon/human(pick(GLOB.blobstart)) diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm index 945b02eefdb34..d1770932269f5 100644 --- a/code/modules/events/pirates.dm +++ b/code/modules/events/pirates.dm @@ -71,7 +71,7 @@ GLOBAL_VAR_INIT(pirates_spawned, FALSE) if(!skip_answer_check && threat?.answered == PIRATE_RESPONSE_PAY) return - var/list/candidates = pollGhostCandidates("Do you wish to be considered for pirate crew?", BAN_ROLE_SPACE_PIRATE, /datum/role_preference/midround_ghost/space_pirate) + var/list/candidates = pollGhostCandidates("Do you wish to be considered for pirate crew?", BAN_ROLE_SPACE_PIRATE, /datum/role_preference/midround_ghost/space_pirate, 15 SECONDS) shuffle_inplace(candidates) var/datum/map_template/shuttle/pirate/default/ship = new diff --git a/code/modules/events/sentience.dm b/code/modules/events/sentience.dm index 7f6287407cba4..dba3e2a3b02a5 100644 --- a/code/modules/events/sentience.dm +++ b/code/modules/events/sentience.dm @@ -40,7 +40,7 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list( /datum/round_event/ghost_role/sentience/spawn_role() var/list/mob/dead/observer/candidates - candidates = get_candidates(BAN_ROLE_SENTIENT_ANIMAL, null, null/*TODO POLL_IGNORE_SENTIENT_ANIMAL*/) + candidates = get_candidates(BAN_ROLE_SENTIENT_ANIMAL, null) // find our chosen mob to breathe life into // Mobs have to be simple animals, mindless, on station, and NOT holograms. diff --git a/code/modules/events/spider_infestation.dm b/code/modules/events/spider_infestation.dm index dadf6d6535264..ce42dc8834f89 100644 --- a/code/modules/events/spider_infestation.dm +++ b/code/modules/events/spider_infestation.dm @@ -37,7 +37,7 @@ message_admins("An event attempted to spawn spiders but no suitable vents were found. Aborting.") return MAP_ERROR - var/list/candidates = get_candidates(BAN_ROLE_SPIDER, /datum/role_preference/midround_ghost/spider) + var/list/candidates = get_candidates(BAN_ROLE_SPIDER, /datum/role_preference/midround_ghost/spider, POLL_IGNORE_SPIDER) if(!length(candidates)) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/wizard/imposter.dm b/code/modules/events/wizard/imposter.dm index 119793718e94a..2b08be84d5c4f 100644 --- a/code/modules/events/wizard/imposter.dm +++ b/code/modules/events/wizard/imposter.dm @@ -10,7 +10,7 @@ if(!ishuman(M.current)) continue var/mob/living/carbon/human/W = M.current - var/list/candidates = pollGhostCandidates("Would you like to be an imposter wizard?", BAN_ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard) + var/list/candidates = pollGhostCandidates("Would you like to be an imposter wizard?", BAN_ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, ignore_category = POLL_IGNORE_WIZARD_HELPER) if(!candidates) return //Sad Trombone var/mob/dead/observer/C = pick(candidates) diff --git a/code/modules/guardian/guardian.dm b/code/modules/guardian/guardian.dm index 461d893020cf7..28061ff61e840 100644 --- a/code/modules/guardian/guardian.dm +++ b/code/modules/guardian/guardian.dm @@ -584,7 +584,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians /mob/living/simple_animal/hostile/guardian/proc/ResetMe() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [summoner?.current?.name]'s [real_name]?", BAN_ROLE_HOLOPARASITE, null, 10 SECONDS, POLL_IGNORE_HOLOPARASITE) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [summoner?.current?.name]'s [real_name]?", BAN_ROLE_HOLOPARASITE, null, 10 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) key = C.key @@ -677,7 +677,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians return G.next_reset = world.time + GUARDIAN_RESET_COOLDOWN to_chat(src, "You attempt to reset [G.real_name]'s personality...") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", BAN_ROLE_HOLOPARASITE, null, 100, POLL_IGNORE_HOLOPARASITE) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", BAN_ROLE_HOLOPARASITE, null, 10 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) to_chat(G, "Your user reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance.") diff --git a/code/modules/guardian/guardianbuilder.dm b/code/modules/guardian/guardianbuilder.dm index 15e3a95553868..8587de0c9b652 100644 --- a/code/modules/guardian/guardianbuilder.dm +++ b/code/modules/guardian/guardianbuilder.dm @@ -225,7 +225,7 @@ used = FALSE return FALSE // IMPORTANT - if we're debugging, the user gets thrown into the stand - var/list/mob/dead/observer/candidates = debug_mode ? list(user) : pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", BAN_ROLE_HOLOPARASITE, null, 100, POLL_IGNORE_HOLOPARASITE) + var/list/mob/dead/observer/candidates = debug_mode ? list(user) : pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", BAN_ROLE_HOLOPARASITE, null, 10 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) var/mob/living/simple_animal/hostile/guardian/G = new(user, theme, guardian_color) diff --git a/code/modules/guardian/standarrow.dm b/code/modules/guardian/standarrow.dm index 1d99e2cf00690..939c976fd9070 100644 --- a/code/modules/guardian/standarrow.dm +++ b/code/modules/guardian/standarrow.dm @@ -157,7 +157,7 @@ G.name = new_name /obj/item/stand_arrow/proc/get_stand(mob/living/carbon/H, datum/guardian_stats/stats) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Guardian Spirit of [H.real_name]?", BAN_ROLE_HOLOPARASITE, null, 100, POLL_IGNORE_HOLOPARASITE) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Guardian Spirit of [H.real_name]?", BAN_ROLE_HOLOPARASITE, null, 10 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) var/mob/living/simple_animal/hostile/guardian/G = new(H, GUARDIAN_MAGIC, rgb(rand(1, 255), rand(1, 255), rand(1, 255))) diff --git a/code/modules/mob/dead/observer/notificationprefs.dm b/code/modules/mob/dead/observer/notificationprefs.dm deleted file mode 100644 index 6370747ad46ba..0000000000000 --- a/code/modules/mob/dead/observer/notificationprefs.dm +++ /dev/null @@ -1,53 +0,0 @@ -/mob/dead/observer/verb/show_notificationprefs() - set category = "Ghost" - set name = "Notification preferences" - set desc = "Notification preferences" - - var/datum/notificationpanel/panel = new(usr) - - panel.ui_interact(usr) - - - -/datum/notificationpanel - var/client/user - -/datum/notificationpanel/New(user) - if (ismob(user)) - var/mob/M = user - if (!M.client) - CRASH("Ghost role notification panel attempted to open to a mob without a client") - src.user = M.client - else - src.user = user - - -/datum/notificationpanel/ui_state(mob/user) - return GLOB.observer_state - -/datum/notificationpanel/ui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "NotificationPreferences") - ui.open() - -/datum/notificationpanel/ui_data(mob/user) - . = list() - .["ignore"] = list() - for(var/key in GLOB.poll_ignore_desc) - .["ignore"] += list(list( - "key" = key, - "enabled" = (user.ckey in GLOB.poll_ignore[key]), - "desc" = GLOB.poll_ignore_desc[key] - )) - - -/datum/notificationpanel/ui_act(action, params) - if(..()) - return - switch (action) - if ("toggle_ignore") - var/key = params["key"] - if (key && islist(GLOB.poll_ignore[key])) - GLOB.poll_ignore[key] ^= list(user.ckey) - . = TRUE diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm index e0102014c9442..1994b664d56ef 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -86,7 +86,7 @@ bursting = TRUE - var/list/candidates = pollGhostCandidates("Do you want to play as an alien larva that will burst out of [owner]?", BAN_ROLE_ALIEN, /datum/role_preference/midround_ghost/xenomorph, 100, POLL_IGNORE_ALIEN_LARVA) + var/list/candidates = pollGhostCandidates("Do you want to play as an alien larva that will burst out of [owner]?", BAN_ROLE_ALIEN, /datum/role_preference/midround_ghost/xenomorph, 10 SECONDS, POLL_IGNORE_ALIEN_LARVA) // separate poll from xeno event spawns if(QDELETED(src) || QDELETED(owner)) return diff --git a/code/modules/mob/living/living_sentience.dm b/code/modules/mob/living/living_sentience.dm index 5700858670edd..84afd66895cd7 100644 --- a/code/modules/mob/living/living_sentience.dm +++ b/code/modules/mob/living/living_sentience.dm @@ -49,11 +49,11 @@ to_chat(src, "[get_spawner_flavour_text()]") return TRUE -/mob/living/proc/set_playable(ban_type = null) +/mob/living/proc/set_playable(ban_type = null, poll_ignore_key = null) playable = TRUE playable_bantype = ban_type if (!key) //check if there is nobody already inhibiting this mob - notify_ghosts("[name] can be controlled", null, enter_link="(Click to play)", source=src, action=NOTIFY_ATTACK, ignore_key = name) + notify_ghosts("[name] can be controlled", null, enter_link="(Click to play)", source=src, action=NOTIFY_ATTACK, ignore_key = poll_ignore_key) LAZYADD(GLOB.mob_spawners["[name]"], src) AddElement(/datum/element/point_of_interest) SSmobs.update_spawners() diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm index 2527778c15324..6414150208698 100644 --- a/code/modules/mob/living/simple_animal/guardian/guardian.dm +++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm @@ -459,7 +459,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians var/mob/living/simple_animal/hostile/guardian/G = input(src, "Pick the guardian you wish to reset", "Guardian Reset") as null|anything in sort_names(guardians) if(G) to_chat(src, "You attempt to reset [G.real_name]'s personality...") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", BAN_ROLE_HOLOPARASITE, null, 100, POLL_IGNORE_HOLOPARASITE) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", BAN_ROLE_HOLOPARASITE, null, 10 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) to_chat(G, "Your user reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance.") @@ -537,7 +537,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians return used = TRUE to_chat(user, "[use_message]") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", BAN_ROLE_HOLOPARASITE, null, 100, POLL_IGNORE_HOLOPARASITE) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", BAN_ROLE_HOLOPARASITE, null, 10 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm index 5799bee128c5a..5cedf5511eea2 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm @@ -156,7 +156,7 @@ While using this makes the system rely on OnFire, it still gives options for tim addtimer(CALLBACK(src, PROC_REF(spawn_elite)), 30) return visible_message("Something within [src] stirs...") - var/list/candidates = pollCandidatesForMob("Do you want to play as a lavaland elite?", BAN_ROLE_LAVALAND_ELITE, null, 50, src, null/*TODO POLL_IGNORE_LAVALAND_ELITE*/) + var/list/candidates = pollCandidatesForMob("Do you want to play as a lavaland elite?", BAN_ROLE_LAVALAND_ELITE, null, 10 SECONDS, src) if(candidates.len) audible_message("The stirring sounds increase in volume!") elitemind = pick(candidates) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 574e8ae693773..2f28c2611c421 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -497,7 +497,7 @@ if(A) poll_message = "[poll_message] Status:[A.name]." ban_key = A.banning_key - var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ban_key, null, 100, M) + var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ban_key, null, 10 SECONDS, M, ignore_category = FALSE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 939340d3b9592..3155d09be87ba 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -558,11 +558,13 @@ to_chat(src, "You are job banned from cyborg! Appeal your job ban if you want to avoid this in the future!") ghostize(FALSE) - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [src]?", JOB_NAME_CYBORG, null, 50, src) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [src]?", JOB_NAME_CYBORG, null, 7.5 SECONDS, src, ignore_category = FALSE) if(LAZYLEN(candidates)) var/mob/dead/observer/chosen_candidate = pick(candidates) message_admins("[key_name_admin(chosen_candidate)] has taken control of ([key_name_admin(src)]) to replace a jobbanned player.") key = chosen_candidate.key + else + set_playable(JOB_NAME_CYBORG) //human -> alien /mob/living/carbon/human/proc/Alienize() diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index f4dd71678fcde..01ea414d4e107 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -590,7 +590,7 @@ if(A) poll_message = "[poll_message] Status:[A.name]." ban_key = A.banning_key - var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ban_key, null, 100, M) + var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ban_key, null, 10 SECONDS, M, ignore_category = FALSE) if(M.stat == DEAD)//boo. return if(LAZYLEN(candidates)) diff --git a/code/modules/religion/rites.dm b/code/modules/religion/rites.dm index 8bd8614989517..141d049842240 100644 --- a/code/modules/religion/rites.dm +++ b/code/modules/religion/rites.dm @@ -408,7 +408,7 @@ var/turf/altar_turf = get_turf(religious_tool) new /obj/effect/temp_visual/cult/blood/long(altar_turf) new /obj/effect/temp_visual/dir_setting/curse/long(altar_turf) - var/list/candidates = pollGhostCandidates("Do you wish to be resurrected as a Holy Summoned Undead?", BAN_ROLE_HOLY_SUMMONED, null, 100, POLL_IGNORE_HOLYUNDEAD) + var/list/candidates = pollGhostCandidates("Do you wish to be resurrected as a Holy Summoned Undead?", BAN_ROLE_HOLY_SUMMONED, null, 10 SECONDS, POLL_IGNORE_HOLYUNDEAD) if(!length(candidates)) to_chat(user, "The soul pool is empty...") new /obj/effect/gibspawner/human/bodypartless(altar_turf) @@ -583,7 +583,7 @@ var/turf/altar_turf = get_turf(religious_tool) new /obj/effect/temp_visual/bluespace_fissure/long(altar_turf) user.visible_message("A tear in reality appears above the altar!") - var/list/candidates = pollGhostCandidates("Do you wish to be summoned as a Holy Carp?", BAN_ROLE_HOLY_SUMMONED, null, 100, POLL_IGNORE_HOLYCARP) + var/list/candidates = pollGhostCandidates("Do you wish to be summoned as a Holy Carp?", BAN_ROLE_HOLY_SUMMONED, null, 10 SECONDS, POLL_IGNORE_HOLYCARP) if(!length(candidates)) new /obj/effect/gibspawner/generic(altar_turf) user.visible_message("The carp pool was not strong enough to bring forth a space carp.") diff --git a/code/modules/research/xenobiology/crossbreeding/warping.dm b/code/modules/research/xenobiology/crossbreeding/warping.dm index a34e1678977bd..646e9762e1e8c 100644 --- a/code/modules/research/xenobiology/crossbreeding/warping.dm +++ b/code/modules/research/xenobiology/crossbreeding/warping.dm @@ -714,7 +714,7 @@ GLOBAL_DATUM(blue_storage, /obj/item/storage/backpack/holding/bluespace) return to_chat(user, "The rune is trying to repair [host.name]'s soul!") - var/list/candidates = pollCandidatesForMob("Do you want to replace the soul of [host.name]?", BAN_ROLE_SENTIENCE, null, 50, host, POLL_IGNORE_SHADE)//todo: fix desc + var/list/candidates = pollCandidatesForMob("Do you want to replace the soul of [host.name]?", BAN_ROLE_SENTIENCE, null, 5 SECONDS, host, POLL_IGNORE_SHADE) if(length(candidates) && !host.key) //check if anyone wanted to play as the dead person and check if no one's in control of the body one last time. var/mob/dead/observer/ghost = pick(candidates) diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index bcf827494c266..d341390420d43 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -749,7 +749,7 @@ to_chat(user, "You offer [src] to [SM]...") being_used = TRUE - var/list/candidates = pollCandidatesForMob("Do you want to play as [SM.name]? (Sentience Potion)", BAN_ROLE_SENTIENCE, null, 50, SM, POLL_IGNORE_SENTIENCE_POTION) // see poll_ignore.dm + var/list/candidates = pollCandidatesForMob("Do you want to play as [SM.name]? (Sentience Potion)", BAN_ROLE_SENTIENCE, null, 5 SECONDS, SM) if(length(candidates)) var/mob/dead/observer/C = pick(candidates) SM.key = C.key diff --git a/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm b/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm index a0aa94801ffe3..d6e9a04940c61 100644 --- a/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm +++ b/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm @@ -131,7 +131,7 @@ man.key = M.ckey /datum/xenoartifact_trait/minor/sentient/proc/get_canidate(obj/item/xenoartifact/X, mob/M) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the maleviolent force inside the [X.name]?", BAN_ROLE_SENTIENT_XENOARTIFACT, null, 8 SECONDS, null/*TODO POLL_IGNORE_XENOARTIFACT*/) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the maleviolent force inside the [X.name]?", BAN_ROLE_SENTIENT_XENOARTIFACT, null, 8 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) setup_sentience(X, C.ckey) diff --git a/tgui/packages/tgui/interfaces/NotificationPreferences.js b/tgui/packages/tgui/interfaces/NotificationPreferences.js deleted file mode 100644 index 0b06987e7784c..0000000000000 --- a/tgui/packages/tgui/interfaces/NotificationPreferences.js +++ /dev/null @@ -1,39 +0,0 @@ -import { useBackend } from '../backend'; -import { Section, Button } from '../components'; -import { Window } from '../layouts'; - -export const NotificationPreferences = (props, context) => { - const { act, data } = useBackend(context); - - const ignoresPreSort = data.ignore || []; - const ignores = ignoresPreSort.sort((a, b) => { - const descA = a.desc.toLowerCase(); - const descB = b.desc.toLowerCase(); - if (descA < descB) { - return -1; - } - if (descA > descB) { - return 1; - } - return 0; - }); - - return ( - - -
- {ignores.map((ignore) => ( -
-
-
- ); -}; From beae3c3e4ed3cf26a274edade8f39f0bb62f0c7d Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 14 Jun 2023 13:15:56 -0400 Subject: [PATCH 010/269] Add back beach bartender - but this time he can speak stoner --- code/_globalvars/lists/poll_ignore.dm | 2 ++ code/modules/antagonists/blob/powers.dm | 2 +- code/modules/awaymissions/corpse.dm | 47 +++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/code/_globalvars/lists/poll_ignore.dm b/code/_globalvars/lists/poll_ignore.dm index d0e88a819c8b4..0f42707aba57b 100644 --- a/code/_globalvars/lists/poll_ignore.dm +++ b/code/_globalvars/lists/poll_ignore.dm @@ -2,6 +2,7 @@ #define POLL_IGNORE_ALIEN_LARVA "alien_larva" #define POLL_IGNORE_ASHWALKER "ashwalker" +#define POLL_IGNORE_BLOB_HELPER "blob_helper" #define POLL_IGNORE_CLOCKWORK_HELPER "clockwork_helper" #define POLL_IGNORE_CULT_SHADE "cult_shade" #define POLL_IGNORE_GOLEM "golem" @@ -19,6 +20,7 @@ GLOBAL_LIST_INIT(poll_ignore_list, list( POLL_IGNORE_ALIEN_LARVA, POLL_IGNORE_ASHWALKER, + POLL_IGNORE_BLOB_HELPER, POLL_IGNORE_CLOCKWORK_HELPER, POLL_IGNORE_CULT_SHADE, POLL_IGNORE_GOLEM, diff --git a/code/modules/antagonists/blob/powers.dm b/code/modules/antagonists/blob/powers.dm index 80f0d4fa02802..6650a0b20e5be 100644 --- a/code/modules/antagonists/blob/powers.dm +++ b/code/modules/antagonists/blob/powers.dm @@ -175,7 +175,7 @@ B.naut = TRUE //temporary placeholder to prevent creation of more than one per factory. to_chat(src, "You attempt to produce a blobbernaut.") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a [blobstrain.name] blobbernaut?", BAN_ROLE_BLOB, /datum/role_preference/midround_ghost/blob, 7.5 SECONDS, ignore_category = POLL_GINORE_BLOB_HELPER) //players must answer rapidly + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a [blobstrain.name] blobbernaut?", BAN_ROLE_BLOB, /datum/role_preference/midround_ghost/blob, 7.5 SECONDS, ignore_category = POLL_IGNORE_BLOB_HELPER) //players must answer rapidly if(LAZYLEN(candidates)) //if we got at least one candidate, they're a blobbernaut now. B.max_integrity = initial(B.max_integrity) * 0.25 //factories that produced a blobbernaut have much lower health B.obj_integrity = min(B.obj_integrity, B.max_integrity) diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index e1e7ed8889aa9..981b5124be960 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -375,6 +375,53 @@ mob_species = /datum/species/plasmaman outfit = /datum/outfit/plasmaman +/obj/effect/mob_spawn/human/bartender + name = "Space Bartender" + id_job = JOB_NAME_BARTENDER + id_access_list = list(ACCESS_BAR) + outfit = /datum/outfit/spacebartender + +/obj/effect/mob_spawn/human/bartender/alive + death = FALSE + roundstart = FALSE + random = TRUE + name = "bartender sleeper" + icon = 'icons/obj/machines/sleeper.dmi' + icon_state = "sleeper" + short_desc = "You are a space bartender!" + flavour_text = "Time to mix drinks and change lives. Smoking space drugs makes it easier to understand your patrons' odd dialect." + assignedrole = "Space Bartender" + id_job = JOB_NAME_BARTENDER + use_cooldown = TRUE + +/obj/effect/mob_spawn/human/bartender/alive/beach + assignedrole = "Beach Bartender" + banType = BAN_ROLE_BEACH_BUM + outfit = /datum/outfit/spacebartender/beach + +/datum/outfit/spacebartender/beach/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + ..() + if(visualsOnly) + return + H.dna.add_mutation(STONER) + +/datum/outfit/spacebartender + name = "Space Bartender" + uniform = /obj/item/clothing/under/rank/civilian/bartender + back = /obj/item/storage/backpack + shoes = /obj/item/clothing/shoes/sneakers/black + suit = /obj/item/clothing/suit/armor/vest + glasses = /obj/item/clothing/glasses/sunglasses/advanced/reagent + id = /obj/item/card/id + +/datum/outfit/spacebartender/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + ..() + + if(visualsOnly) + return + + ADD_TRAIT(H, TRAIT_SOMMELIER, ROUNDSTART_TRAIT) + /obj/effect/mob_spawn/human/beach outfit = /datum/outfit/beachbum From 91eb3cf406b0c6320595e44763a9c3f6d8880622 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 14 Jun 2023 13:16:35 -0400 Subject: [PATCH 011/269] Update maps --- _maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm | 2 +- _maps/RandomZLevels/TheBeach.dmm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm b/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm index a1606179832ad..85f2b14a1caab 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm @@ -29,7 +29,7 @@ /turf/open/floor/plating, /area/ruin/powered/beach) "ah" = ( -/obj/effect/mob_spawn/human/bartender/alive, +/obj/effect/mob_spawn/human/bartender/alive/beach, /turf/open/floor/plating, /area/ruin/powered/beach) "ai" = ( diff --git a/_maps/RandomZLevels/TheBeach.dmm b/_maps/RandomZLevels/TheBeach.dmm index 4fb032dc135e1..3192d54301605 100644 --- a/_maps/RandomZLevels/TheBeach.dmm +++ b/_maps/RandomZLevels/TheBeach.dmm @@ -783,7 +783,7 @@ /turf/open/floor/wood, /area/awaymission/beach) "ck" = ( -/obj/effect/mob_spawn/human/bartender/alive, +/obj/effect/mob_spawn/human/bartender/alive/beach, /turf/open/floor/wood, /area/awaymission/beach) "cl" = ( From 394a84ffa5c3dde9526b8f7596e668dc8e4cbda3 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 14 Jun 2023 13:21:48 -0400 Subject: [PATCH 012/269] Legacy Lavaland ban --- code/modules/admin/sql_ban_system.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/admin/sql_ban_system.dm b/code/modules/admin/sql_ban_system.dm index 65c6fd9dfaa0e..219df92204ada 100644 --- a/code/modules/admin/sql_ban_system.dm +++ b/code/modules/admin/sql_ban_system.dm @@ -10,7 +10,7 @@ if(BAN_ROLE_FORCED_ANTAGONISTS in ban_cache) return TRUE if(role in GLOB.ghost_role_bannable_roles) - if(BAN_ROLE_ALL_GHOST in ban_cache) + if((BAN_ROLE_ALL_GHOST in ban_cache) || ("Lavaland" in ban_cache)) // Legacy "Lavaland" ban return TRUE return role in ban_cache From 5ffccbf0bf186d25f106be0ff0ff486c2b8a16d0 Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Thu, 15 Jun 2023 00:04:38 +0000 Subject: [PATCH 013/269] Automatic changelog compile [ci skip] --- html/changelog.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 49b21a0bd7d90..a4c2fa1377dbe 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -1074,12 +1074,6 @@

Wisemonster updated:

  • Swarmer catwalks are now visible.
- -

13 April 2023

-

Absolucy updated:

-
    -
  • Shuttle buying and night lighting should work again
  • -
GoonStation 13 Development Team From 2b5b03542789273b1656a308481ca3603ef7c0e8 Mon Sep 17 00:00:00 2001 From: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Thu, 15 Jun 2023 17:56:59 +0100 Subject: [PATCH 014/269] Bug Report labels (#9231) * Bug Report labels * Update feature_request.md * Update .github/ISSUE_TEMPLATE/bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 1 + .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 17920c34c3368..e7e3bdbfae20b 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,6 +1,7 @@ --- name: Bug report about: Create a report to help reproduce and fix unexpected behaviour. +labels: Bug --- ## Occurance Details diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 69eddb5a25084..948489529cb95 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,7 +1,7 @@ --- name: Feature request about: Suggest an idea for this project - +labels: Feature-Request --- Feature requests are handled in the repository, but you could also discuss your ideas on beestation discord or forums here: https://forums.beestation13.com/ From 0b7967458925aff39ec51b34677d61c219872e3c Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Fri, 16 Jun 2023 00:04:45 +0000 Subject: [PATCH 015/269] Automatic changelog compile [ci skip] --- html/changelog.html | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index a4c2fa1377dbe..441292457241d 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -1035,45 +1035,6 @@

PestoVerde322 updated:

  • Resized Corgstation to the right size.
  • adjusted some icons being too clear in areas.dmi
  • - -

    14 April 2023

    -

    Absolucy updated:

    -
      -
    • Hair gradients now properly transfer with DNA.
    • -
    -

    CydiaLamiales updated:

    -
      -
    • Added new positive moodlets for wearing flowers and flower crowns
    • -
    • repathed grown flowers into /obj/item/reagent_containers/food/snacks/grown/flower/
    • -
    • repathed flower seeds into /obj/item/seeds/flower/
    • -
    • fixed a typo in the sunflower attack text
    • -
    • Changed the heretic ritual text to reflect it accepting any flower, made them accept Harebells and Rainbow Bunch flowers
    • -
    • Mutadone now transforms non-naturally spawned or monkey cubed monkeys into humans again
    • -
    • Fixed the race not being properly stored when transforming into a monkey/restored from a monkey
    • -
    • added the 'natural' variable to monkeys which determines if they're mutadone proof
    • -
    -

    EvilDragonfiend updated:

    -
      -
    • a missing pill sprite will appear
    • -
    -

    PowerfulBacon updated:

    -
      -
    • Sniper rifles now need to be chambered (Like a shotgun, one use to chamber)
    • -
    • Sniper rifles minimum fire delay decreased from 4s to 1.5s.
    • -
    • Sniper rifle ammo is now brought seperately from the magazine.
    • -
    • Added in explosive sniper rounsd.
    • -
    • Added in EMP sniper rounds.
    • -
    • Added in inferno sniper rounds.
    • -
    • Added in anti-matter sniper rounds.
    • -
    -

    Rukofamicom updated:

    -
      -
    • Plumbing pipes now display properly on all plumbing machines.
    • -
    -

    Wisemonster updated:

    -
      -
    • Swarmer catwalks are now visible.
    • -
    GoonStation 13 Development Team From b2c9658ac903ba3096078fbd897afa41d21a004d Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 7 Jun 2023 21:22:05 -0400 Subject: [PATCH 016/269] tgui Preferences Menu + total rewrite of the preferences backend (#61313) About The Pull Request Rewrites the entire preferences menu in tgui. Rewrites the entire backend to be built upon datumized preferences, rather than constant additions to the preferences base datum. Splits game preferences into its own window. Antagonists are now split into their individual rulesets. You can now be a roundstart heretic without signing up for latejoin heretic, as an example. This iteration matches parity, and provides very little new functionality, but adding anything new will be much easier. Fixes #60823 Fixes #28907 Fixes #44887 Fixes #59912 Fixes #58458 Fixes #59181 Major TODOs Quirk icons, from @Fikou (with some slight adjustments from me) Lore text, from @EOBGames (4/6, need moths and then ethereal lore from @AMonkeyThatCodes) Heavy documentation on how one would add new preferences, species, jobs, etc A lot of specialized testing so that people's real data don't get corrupted Changelog cl Mothblocks, Floyd on lots of the design refactor: The preferences menu has been completely rewritten in tgui. refactor: The "Stop Sounds" verb has been moved to OOC. /cl --- code/__DEFINES/DNA.dm | 29 +- code/__DEFINES/antagonists.dm | 3 + code/__DEFINES/colors.dm | 3 + code/__DEFINES/food.dm | 30 +- code/__DEFINES/ghost.dm | 31 +- code/__DEFINES/menu.dm | 3 - code/__DEFINES/preferences.dm | 57 +- code/__DEFINES/subsystems.dm | 4 +- code/__DEFINES/traits.dm | 3 + code/__HELPERS/_lists.dm | 9 +- code/__HELPERS/admin.dm | 3 + code/__HELPERS/colors.dm | 20 + code/__HELPERS/game.dm | 2 +- code/__HELPERS/global_lists.dm | 1 - code/__HELPERS/icons.dm | 1 - code/__HELPERS/mobs.dm | 35 +- code/__HELPERS/roundend.dm | 1 + code/__HELPERS/sanitize_values.dm | 4 + code/_globalvars/lists/flavor_misc.dm | 19 +- code/_globalvars/lists/names.dm | 20 - code/_globalvars/lists/objects.dm | 2 +- code/_onclick/hud/action_button.dm | 5 +- code/_onclick/hud/ghost.dm | 6 +- code/_onclick/hud/hud.dm | 16 +- code/_onclick/hud/parallax.dm | 4 +- code/_onclick/hud/rendering/plane_master.dm | 4 +- code/_onclick/observer.dm | 2 +- code/controllers/master.dm | 6 + code/controllers/subsystem/atoms.dm | 8 + code/controllers/subsystem/early_assets.dm | 24 + code/controllers/subsystem/job.dm | 41 +- code/controllers/subsystem/mapping.dm | 2 +- code/controllers/subsystem/vote.dm | 4 +- code/datums/action.dm | 2 +- code/datums/brain_damage/imaginary_friend.dm | 7 + code/datums/mind.dm | 22 +- code/datums/view.dm | 6 +- .../dynamic/dynamic_rulesets_latejoin.dm | 1 - .../dynamic/dynamic_rulesets_roundstart.dm | 3 + code/game/machinery/computer/security.dm | 4 +- code/game/machinery/telecomms/broadcasting.dm | 5 - code/game/objects/items.dm | 7 +- code/modules/admin/admin_verbs.dm | 7 - code/modules/admin/poll_management.dm | 4 +- code/modules/admin/sql_ban_system.dm | 3 +- code/modules/admin/verbs/adminsay.dm | 3 +- .../antagonists/_common/antag_datum.dm | 38 + code/modules/antagonists/abductor/abductor.dm | 20 + code/modules/antagonists/blob/blob.dm | 10 + code/modules/antagonists/brother/brother.dm | 28 + .../antagonists/changeling/changeling.dm | 15 + code/modules/antagonists/creep/creep.dm | 37 +- code/modules/antagonists/cult/cult.dm | 36 +- code/modules/antagonists/gang/gang.dm | 6 + code/modules/antagonists/nukeop/nukeop.dm | 43 +- .../antagonists/revenant/revenant_antag.dm | 3 + .../antagonists/revolution/revolution.dm | 40 + .../antagonists/space_dragon/space_dragon.dm | 11 + code/modules/antagonists/swarmer/swarmer.dm | 5 + .../antagonists/traitor/datum_traitor.dm | 16 + code/modules/antagonists/wizard/wizard.dm | 1 + code/modules/antagonists/xeno/xeno.dm | 2 + code/modules/asset_cache/asset_list.dm | 29 + code/modules/asset_cache/asset_list_items.dm | 5 +- code/modules/client/client_defines.dm | 3 + code/modules/client/client_procs.dm | 13 +- code/modules/client/preferences.dm | 2356 +++-------------- code/modules/client/preferences/README.md | 476 ++++ .../modules/client/preferences/_preference.dm | 534 ++++ code/modules/client/preferences/admin.dm | 41 + code/modules/client/preferences/age.dm | 10 + .../client/preferences/ai_core_display.dm | 25 + .../client/preferences/ambient_occlusion.dm | 12 + code/modules/client/preferences/assets.dm | 65 + .../client/preferences/auto_fit_viewport.dm | 7 + code/modules/client/preferences/body_type.dm | 21 + .../preferences/broadcast_login_logout.dm | 5 + .../client/preferences/buttons_locked.dm | 5 + code/modules/client/preferences/clothing.dm | 151 ++ .../client/preferences/darkened_flash.dm | 6 + code/modules/client/preferences/fps.dm | 17 + code/modules/client/preferences/gender.dm | 11 + code/modules/client/preferences/ghost.dm | 178 ++ code/modules/client/preferences/hotkeys.dm | 7 + .../client/preferences/item_outlines.dm | 4 + .../client/preferences/jobless_role.dm | 12 + .../preferences/middleware/_middleware.dm | 52 + .../client/preferences/middleware/antags.dm | 162 ++ .../client/preferences/middleware/jobs.dm | 82 + .../preferences/middleware/keybindings.dm | 89 + .../preferences/middleware/legacy_toggles.dm | 132 + .../client/preferences/middleware/names.dm | 56 + .../client/preferences/middleware/quirks.dm | 89 + .../client/preferences/middleware/random.dm | 84 + .../client/preferences/middleware/species.dm | 35 + code/modules/client/preferences/names.dm | 156 ++ code/modules/client/preferences/ooc.dm | 14 + code/modules/client/preferences/parallax.dm | 38 + code/modules/client/preferences/pda.dm | 17 + code/modules/client/preferences/phobia.dm | 16 + code/modules/client/preferences/pixel_size.dm | 15 + .../preferences/playtime_reward_cloak.dm | 20 + .../client/preferences/preferred_map.dm | 52 + code/modules/client/preferences/random.dm | 53 + code/modules/client/preferences/runechat.dm | 25 + .../client/preferences/scaling_method.dm | 14 + code/modules/client/preferences/screentips.dm | 18 + .../client/preferences/security_department.dm | 27 + code/modules/client/preferences/skin_tone.dm | 37 + code/modules/client/preferences/species.dm | 60 + .../preferences/species_features/basic.dm | 101 + .../preferences/species_features/ethereal.dm | 33 + .../preferences/species_features/felinid.dm | 33 + .../preferences/species_features/lizard.dm | 141 + .../preferences/species_features/moth.dm | 102 + .../preferences/species_features/mutants.dm | 20 + code/modules/client/preferences/tgui.dm | 20 + .../preferences/tgui_prefs_migration.dm | 116 + code/modules/client/preferences/tooltips.dm | 15 + code/modules/client/preferences/ui_style.dm | 26 + .../client/preferences/underwear_color.dm | 15 + .../client/preferences/uplink_location.dm | 26 + code/modules/client/preferences/widescreen.dm | 7 + .../client/preferences/window_flashing.dm | 5 + code/modules/client/preferences_menu.dm | 26 + code/modules/client/preferences_toggles.dm | 5 +- code/modules/client/verbs/ooc.dm | 3 +- code/modules/clothing/spacesuits/hardsuit.dm | 2 +- code/modules/clothing/suits/toggles.dm | 3 +- code/modules/flufftext/Hallucination.dm | 4 +- code/modules/jobs/job_types/_job.dm | 91 + code/modules/jobs/job_types/assistant.dm | 27 +- code/modules/jobs/job_types/chaplain.dm | 10 +- code/modules/jobs/job_types/clown.dm | 5 +- code/modules/jobs/job_types/mime.dm | 5 +- .../jobs/job_types/security_officer.dm | 2 +- code/modules/keybindings/setup.dm | 2 +- .../modules/mob/dead/new_player/new_player.dm | 14 +- .../mob/dead/new_player/preferences_setup.dm | 109 + code/modules/mob/dead/observer/login.dm | 8 +- code/modules/mob/dead/observer/observer.dm | 47 +- code/modules/mob/living/carbon/human/dummy.dm | 20 + .../mob/living/carbon/human/human_helpers.dm | 20 + .../mob/living/carbon/human/species.dm | 5 + .../carbon/human/species_types/ethereal.dm | 7 + .../carbon/human/species_types/felinid.dm | 10 + .../carbon/human/species_types/humans.dm | 5 + code/modules/mob/living/silicon/ai/ai.dm | 6 +- .../living/silicon/ai/ai_portrait_picker.dm | 8 +- .../modules/mob/living/silicon/robot/robot.dm | 4 +- .../living/simple_animal/hostile/zombie.dm | 26 +- code/modules/mob/login.dm | 6 +- code/modules/mob/mob.dm | 14 +- code/modules/mob/mob_helpers.dm | 5 +- code/modules/mob/transform_procs.dm | 2 +- .../file_system/programs/portrait_printer.dm | 8 +- code/modules/reagents/chemistry/holder.dm | 1 - .../modules/research/techweb/_techweb_node.dm | 6 +- .../organs/external/_external_organs.dm | 257 ++ code/modules/surgery/organs/external/wings.dm | 226 ++ code/modules/surgery/organs/organ_internal.dm | 6 + code/modules/tgui/tgui.dm | 8 +- code/modules/tooltip/tooltip.dm | 5 +- .../unit_tests/dynamic_ruleset_sanity.dm | 22 + code/modules/unit_tests/preferences.dm | 51 + code/modules/unit_tests/quirks.dm | 21 + config/game_options.txt | 3 + icons/UI_Icons/antags/obsessed.dmi | Bin 0 -> 2339 bytes tgui/.eslintrc.yml | 4 + tgui/package.json | 1 + tgui/packages/common/collections.ts | 66 + tgui/packages/common/exhaustive.ts | 19 + tgui/packages/tgfont/icons/ATTRIBUTIONS.md | 6 + tgui/packages/tgfont/icons/bad-touch.svg | 23 + tgui/packages/tgfont/icons/non-binary.svg | 17 + tgui/packages/tgfont/icons/prosthetic-leg.svg | 22 + tgui/packages/tgui/components/Button.js | 6 +- tgui/packages/tgui/components/Dropdown.js | 57 +- tgui/packages/tgui/components/FitText.tsx | 89 + tgui/packages/tgui/components/Input.js | 16 +- tgui/packages/tgui/components/KeyListener.tsx | 39 + tgui/packages/tgui/components/Popper.tsx | 9 +- tgui/packages/tgui/components/Stack.tsx | 20 +- .../tgui/components/TrackOutsideClicks.tsx | 38 + tgui/packages/tgui/components/index.js | 3 + tgui/packages/tgui/hotkeys.ts | 7 +- tgui/packages/tgui/http.ts | 17 + tgui/packages/tgui/interfaces/Aquarium.js | 2 +- .../IntegratedCircuit/CircuitInfo.js | 2 +- tgui/packages/tgui/interfaces/NtosNetChat.js | 2 +- tgui/packages/tgui/interfaces/NtosSecurEye.js | 10 +- .../interfaces/PreferencesMenu/AntagsPage.tsx | 207 ++ .../CharacterPreferenceWindow.tsx | 164 ++ .../PreferencesMenu/CharacterPreview.tsx | 15 + .../PreferencesMenu/GamePreferenceWindow.tsx | 77 + .../PreferencesMenu/GamePreferencesPage.tsx | 102 + .../interfaces/PreferencesMenu/JobsPage.tsx | 441 +++ .../PreferencesMenu/KeybindingsPage.tsx | 446 ++++ .../interfaces/PreferencesMenu/MainPage.tsx | 596 +++++ .../interfaces/PreferencesMenu/PageButton.tsx | 30 + .../interfaces/PreferencesMenu/QuirksPage.tsx | 327 +++ .../PreferencesMenu/RandomizationButton.tsx | 64 + .../ServerPreferencesFetcher.tsx | 38 + .../PreferencesMenu/SpeciesPage.tsx | 360 +++ .../interfaces/PreferencesMenu/TabbedMenu.tsx | 94 + .../antagonists/antagonists/abductor.ts | 24 + .../antagonists/antagonists/blob.ts | 20 + .../antagonists/antagonists/blobinfection.ts | 18 + .../antagonists/antagonists/bloodbrother.ts | 17 + .../antagonists/antagonists/changeling.ts | 21 + .../antagonists/antagonists/cultist.ts | 23 + .../antagonists/familyheadaspirant.ts | 18 + .../antagonists/antagonists/gangster.ts | 19 + .../antagonists/headrevolutionary.ts | 20 + .../antagonists/antagonists/heretic.ts | 24 + .../antagonists/hereticsmuggler.ts | 14 + .../antagonists/antagonists/loneoperative.ts | 19 + .../antagonists/antagonists/malfai.ts | 20 + .../antagonists/antagonists/malfaimidround.ts | 18 + .../antagonists/antagonists/monkey.ts | 17 + .../antagonists/antagonists/obsessed.ts | 17 + .../antagonists/antagonists/operative.ts | 25 + .../antagonists/operativemidround.ts | 18 + .../antagonists/antagonists/provocateur.ts | 19 + .../antagonists/antagonists/revenant.ts | 17 + .../antagonists/sentientdisease.ts | 16 + .../antagonists/antagonists/spacedragon.ts | 16 + .../antagonists/antagonists/spaceninja.ts | 24 + .../antagonists/antagonists/swarmer.ts | 22 + .../antagonists/syndicateinfiltrator.ts | 15 + .../antagonists/syndicatesleeperagent.ts | 19 + .../antagonists/antagonists/traitor.ts | 25 + .../antagonists/antagonists/wizard.ts | 20 + .../antagonists/antagonists/wizardmidround.ts | 14 + .../antagonists/antagonists/xenomorph.ts | 16 + .../PreferencesMenu/antagonists/base.ts | 35 + .../tgui/interfaces/PreferencesMenu/data.ts | 155 ++ .../tgui/interfaces/PreferencesMenu/index.tsx | 24 + .../interfaces/PreferencesMenu/jobs/base.ts | 7 + .../PreferencesMenu/jobs/departments.ts | 40 + .../PreferencesMenu/jobs/jobs/ai.ts | 10 + .../PreferencesMenu/jobs/jobs/assistant.ts | 11 + .../jobs/jobs/atmospheric_technician.ts | 11 + .../PreferencesMenu/jobs/jobs/bartender.ts | 10 + .../PreferencesMenu/jobs/jobs/botanist.ts | 10 + .../PreferencesMenu/jobs/jobs/captain.ts | 12 + .../jobs/jobs/cargo_technician.ts | 12 + .../PreferencesMenu/jobs/jobs/chaplain.ts | 11 + .../PreferencesMenu/jobs/jobs/chemist.ts | 11 + .../jobs/jobs/chief_engineer.ts | 11 + .../jobs/jobs/chief_medical_officer.ts | 11 + .../PreferencesMenu/jobs/jobs/clown.ts | 11 + .../PreferencesMenu/jobs/jobs/cook.ts | 10 + .../PreferencesMenu/jobs/jobs/curator.ts | 11 + .../PreferencesMenu/jobs/jobs/cyborg.ts | 10 + .../PreferencesMenu/jobs/jobs/detective.ts | 11 + .../PreferencesMenu/jobs/jobs/geneticist.ts | 11 + .../jobs/jobs/head_of_personnel.ts | 11 + .../jobs/jobs/head_of_security.ts | 12 + .../PreferencesMenu/jobs/jobs/janitor.ts | 11 + .../PreferencesMenu/jobs/jobs/lawyer.ts | 11 + .../jobs/jobs/medical_doctor.ts | 11 + .../PreferencesMenu/jobs/jobs/mime.ts | 10 + .../PreferencesMenu/jobs/jobs/paramedic.ts | 11 + .../PreferencesMenu/jobs/jobs/prisoner.ts | 10 + .../PreferencesMenu/jobs/jobs/psychologist.ts | 11 + .../jobs/jobs/quartermaster.ts | 11 + .../jobs/jobs/research_director.ts | 12 + .../PreferencesMenu/jobs/jobs/roboticist.ts | 10 + .../PreferencesMenu/jobs/jobs/scientist.ts | 10 + .../jobs/jobs/security_officer.ts | 11 + .../PreferencesMenu/jobs/jobs/shaft_miner.ts | 11 + .../jobs/jobs/station_engineer.ts | 11 + .../PreferencesMenu/jobs/jobs/virologist.ts | 11 + .../PreferencesMenu/jobs/jobs/warden.ts | 12 + .../tgui/interfaces/PreferencesMenu/names.tsx | 237 ++ .../preferences/features/base.tsx | 333 +++ .../features/character_preferences/age.tsx | 6 + .../character_preferences/ai_core_display.tsx | 6 + .../character_preferences/body_type.tsx | 6 + .../features/character_preferences/pda.tsx | 15 + .../features/character_preferences/phobia.tsx | 6 + .../playtime_reward_cloak.tsx | 7 + .../security_department.tsx | 6 + .../character_preferences/skin_tone.tsx | 63 + .../character_preferences/uplink_loc.tsx | 6 + .../features/game_preferences/admin.tsx | 15 + .../game_preferences/ambient_occlusion.tsx | 8 + .../game_preferences/auto_fit_viewport.tsx | 7 + .../broadcast_login_logout.tsx | 11 + .../game_preferences/buttons_locked.tsx | 8 + .../game_preferences/darkened_flash.tsx | 12 + .../features/game_preferences/fps.tsx | 56 + .../features/game_preferences/ghost.tsx | 139 + .../features/game_preferences/hotkeys.tsx | 8 + .../game_preferences/item_outlines.tsx | 8 + .../game_preferences/legacy_chat_toggles.tsx | 89 + .../game_preferences/legacy_toggles.tsx | 153 ++ .../features/game_preferences/ooc.tsx | 8 + .../features/game_preferences/parallax.tsx | 7 + .../features/game_preferences/pixel_size.tsx | 13 + .../game_preferences/preferred_map.tsx | 13 + .../features/game_preferences/runechat.tsx | 29 + .../game_preferences/scaling_method.tsx | 11 + .../features/game_preferences/screentips.tsx | 20 + .../features/game_preferences/tgui.tsx | 15 + .../features/game_preferences/tooltips.tsx | 20 + .../features/game_preferences/ui_style.tsx | 63 + .../features/game_preferences/widescreen.tsx | 7 + .../game_preferences/window_flashing.tsx | 12 + .../preferences/features/index.ts | 23 + .../preferences/features/randomization.tsx | 90 + .../preferences/features/species_features.tsx | 51 + .../PreferencesMenu/preferences/gender.ts | 22 + .../preferences/species/base.ts | 33 + .../preferences/species/ethereal.ts | 64 + .../preferences/species/felinid.ts | 28 + .../preferences/species/human.ts | 30 + .../preferences/species/lizard.ts | 32 + .../preferences/species/moth.ts | 38 + .../preferences/species/plasmaman.ts | 71 + .../PreferencesMenu/useRandomToggleState.ts | 4 + tgui/packages/tgui/interfaces/ScannerGate.js | 2 +- tgui/packages/tgui/interfaces/Telecomms.js | 2 - tgui/packages/tgui/layouts/Window.js | 2 +- tgui/packages/tgui/stories/Popper.stories.js | 3 +- tgui/packages/tgui/stories/Tooltip.stories.js | 1 - .../tgui/styles/atomic/centered-image.scss | 7 + .../packages/tgui/styles/atomic/fit-text.scss | 10 + tgui/packages/tgui/styles/colors.scss | 2 + .../tgui/styles/components/Dropdown.scss | 1 - .../tgui/styles/components/LabeledList.scss | 1 - .../styles/interfaces/PreferencesMenu.scss | 241 ++ tgui/packages/tgui/styles/main.scss | 4 + tgui/yarn.lock | 23 + 335 files changed, 12432 insertions(+), 2327 deletions(-) delete mode 100644 code/__DEFINES/menu.dm create mode 100644 code/__HELPERS/admin.dm create mode 100644 code/controllers/subsystem/early_assets.dm create mode 100644 code/modules/client/preferences/README.md create mode 100644 code/modules/client/preferences/_preference.dm create mode 100644 code/modules/client/preferences/admin.dm create mode 100644 code/modules/client/preferences/age.dm create mode 100644 code/modules/client/preferences/ai_core_display.dm create mode 100644 code/modules/client/preferences/ambient_occlusion.dm create mode 100644 code/modules/client/preferences/assets.dm create mode 100644 code/modules/client/preferences/auto_fit_viewport.dm create mode 100644 code/modules/client/preferences/body_type.dm create mode 100644 code/modules/client/preferences/broadcast_login_logout.dm create mode 100644 code/modules/client/preferences/buttons_locked.dm create mode 100644 code/modules/client/preferences/clothing.dm create mode 100644 code/modules/client/preferences/darkened_flash.dm create mode 100644 code/modules/client/preferences/fps.dm create mode 100644 code/modules/client/preferences/gender.dm create mode 100644 code/modules/client/preferences/ghost.dm create mode 100644 code/modules/client/preferences/hotkeys.dm create mode 100644 code/modules/client/preferences/item_outlines.dm create mode 100644 code/modules/client/preferences/jobless_role.dm create mode 100644 code/modules/client/preferences/middleware/_middleware.dm create mode 100644 code/modules/client/preferences/middleware/antags.dm create mode 100644 code/modules/client/preferences/middleware/jobs.dm create mode 100644 code/modules/client/preferences/middleware/keybindings.dm create mode 100644 code/modules/client/preferences/middleware/legacy_toggles.dm create mode 100644 code/modules/client/preferences/middleware/names.dm create mode 100644 code/modules/client/preferences/middleware/quirks.dm create mode 100644 code/modules/client/preferences/middleware/random.dm create mode 100644 code/modules/client/preferences/middleware/species.dm create mode 100644 code/modules/client/preferences/names.dm create mode 100644 code/modules/client/preferences/ooc.dm create mode 100644 code/modules/client/preferences/parallax.dm create mode 100644 code/modules/client/preferences/pda.dm create mode 100644 code/modules/client/preferences/phobia.dm create mode 100644 code/modules/client/preferences/pixel_size.dm create mode 100644 code/modules/client/preferences/playtime_reward_cloak.dm create mode 100644 code/modules/client/preferences/preferred_map.dm create mode 100644 code/modules/client/preferences/random.dm create mode 100644 code/modules/client/preferences/runechat.dm create mode 100644 code/modules/client/preferences/scaling_method.dm create mode 100644 code/modules/client/preferences/screentips.dm create mode 100644 code/modules/client/preferences/security_department.dm create mode 100644 code/modules/client/preferences/skin_tone.dm create mode 100644 code/modules/client/preferences/species.dm create mode 100644 code/modules/client/preferences/species_features/basic.dm create mode 100644 code/modules/client/preferences/species_features/ethereal.dm create mode 100644 code/modules/client/preferences/species_features/felinid.dm create mode 100644 code/modules/client/preferences/species_features/lizard.dm create mode 100644 code/modules/client/preferences/species_features/moth.dm create mode 100644 code/modules/client/preferences/species_features/mutants.dm create mode 100644 code/modules/client/preferences/tgui.dm create mode 100644 code/modules/client/preferences/tgui_prefs_migration.dm create mode 100644 code/modules/client/preferences/tooltips.dm create mode 100644 code/modules/client/preferences/ui_style.dm create mode 100644 code/modules/client/preferences/underwear_color.dm create mode 100644 code/modules/client/preferences/uplink_location.dm create mode 100644 code/modules/client/preferences/widescreen.dm create mode 100644 code/modules/client/preferences/window_flashing.dm create mode 100644 code/modules/client/preferences_menu.dm create mode 100644 code/modules/mob/dead/new_player/preferences_setup.dm create mode 100644 code/modules/surgery/organs/external/_external_organs.dm create mode 100644 code/modules/surgery/organs/external/wings.dm create mode 100644 code/modules/unit_tests/preferences.dm create mode 100644 code/modules/unit_tests/quirks.dm create mode 100644 icons/UI_Icons/antags/obsessed.dmi create mode 100644 tgui/packages/common/exhaustive.ts create mode 100644 tgui/packages/tgfont/icons/ATTRIBUTIONS.md create mode 100644 tgui/packages/tgfont/icons/bad-touch.svg create mode 100644 tgui/packages/tgfont/icons/non-binary.svg create mode 100644 tgui/packages/tgfont/icons/prosthetic-leg.svg create mode 100644 tgui/packages/tgui/components/FitText.tsx create mode 100644 tgui/packages/tgui/components/KeyListener.tsx create mode 100644 tgui/packages/tgui/components/TrackOutsideClicks.tsx create mode 100644 tgui/packages/tgui/http.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/AntagsPage.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreview.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferenceWindow.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/PageButton.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/RandomizationButton.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/ServerPreferencesFetcher.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/SpeciesPage.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/abductor.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/blob.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/blobinfection.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/bloodbrother.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/changeling.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/cultist.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/familyheadaspirant.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/gangster.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/headrevolutionary.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/heretic.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/hereticsmuggler.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/loneoperative.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/malfai.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/malfaimidround.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/monkey.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/obsessed.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/operative.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/operativemidround.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/provocateur.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/revenant.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/sentientdisease.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/spacedragon.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/spaceninja.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/swarmer.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/syndicateinfiltrator.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/syndicatesleeperagent.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/traitor.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/wizard.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/wizardmidround.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/xenomorph.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/base.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/data.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/index.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/base.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/departments.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/ai.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/assistant.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/atmospheric_technician.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/bartender.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/botanist.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/captain.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/cargo_technician.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chaplain.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chemist.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chief_engineer.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chief_medical_officer.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/clown.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/cook.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/curator.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/cyborg.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/detective.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/geneticist.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/head_of_personnel.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/head_of_security.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/janitor.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/lawyer.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/medical_doctor.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/mime.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/paramedic.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/prisoner.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/psychologist.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/quartermaster.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/research_director.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/roboticist.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/scientist.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/security_officer.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/shaft_miner.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/station_engineer.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/virologist.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/warden.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/age.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/ai_core_display.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/body_type.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/pda.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/phobia.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/playtime_reward_cloak.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/security_department.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skin_tone.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/uplink_loc.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/admin.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ambient_occlusion.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/auto_fit_viewport.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/broadcast_login_logout.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/buttons_locked.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/darkened_flash.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/fps.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/hotkeys.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/item_outlines.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/legacy_chat_toggles.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/legacy_toggles.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ooc.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/parallax.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/pixel_size.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/preferred_map.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/runechat.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/scaling_method.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/screentips.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tgui.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tooltips.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ui_style.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/widescreen.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/window_flashing.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/index.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/randomization.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/gender.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/base.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/ethereal.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/felinid.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/human.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/lizard.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/moth.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/plasmaman.ts create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/useRandomToggleState.ts create mode 100644 tgui/packages/tgui/styles/atomic/centered-image.scss create mode 100644 tgui/packages/tgui/styles/atomic/fit-text.scss create mode 100644 tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm index baeb8bf19b762..11efa751f3082 100644 --- a/code/__DEFINES/DNA.dm +++ b/code/__DEFINES/DNA.dm @@ -115,21 +115,20 @@ #define LIPS 5 #define NOBLOOD 6 #define NOTRANSSTING 7 -#define MUTCOLORS_PARTSONLY 8 //! Used if we want the mutant colour to be only used by mutant bodyparts. Don't combine this with MUTCOLORS, or it will be useless. -#define NOZOMBIE 9 -#define NO_UNDERWEAR 10 -#define NOLIVER 11 -#define NOSTOMACH 12 -#define NO_DNA_COPY 13 -#define NOFLASH 14 -#define DYNCOLORS 15 //! Use this if you want to change the race's color without the player being able to pick their own color. AKA special color shifting TRANSLATION: AWFUL. -#define AGENDER 16 -#define NOEYESPRITES 17 //! Do not draw eyes or eyeless overlay -#define NOREAGENTS 18 //! DO NOT PROCESS REAGENTS -#define REVIVESBYHEALING 19 // Will revive on heal when healing and total HP > 0. -#define NOHUSK 20 // Can't be husked. -#define NOMOUTH 21 -#define NOSOCKS 22 // You cannot wear socks. +#define NOZOMBIE 8 +#define NO_UNDERWEAR 9 +#define NOLIVER 10 +#define NOSTOMACH 11 +#define NO_DNA_COPY 12 +#define NOFLASH 13 +#define DYNCOLORS 14 //! Use this if you want to change the race's color without the player being able to pick their own color. AKA special color shifting TRANSLATION: AWFUL. +#define AGENDER 15 +#define NOEYESPRITES 16 //! Do not draw eyes or eyeless overlay +#define NOREAGENTS 17 //! DO NOT PROCESS REAGENTS +#define REVIVESBYHEALING 18 // Will revive on heal when healing and total HP > 0. +#define NOHUSK 19 // Can't be husked. +#define NOMOUTH 20 +#define NOSOCKS 21 // You cannot wear socks. /// Used for determining which wounds are applicable to this species. /// if we have flesh (can suffer slash/piercing/burn wounds, requires they don't have NOBLOOD) diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index 87bbbcdce5d94..1a25d8f3e6a68 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -133,3 +133,6 @@ //Spider webs #define MAX_WEBS_PER_TILE 3 + +/// The dimensions of the antagonist preview icon. Will be scaled to this size. +#define ANTAGONIST_PREVIEW_ICON_SIZE 96 diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index 9f4bc3a801399..452b862d567e3 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -99,3 +99,6 @@ #define AMBIENT_OCCLUSION filter(type="drop_shadow", x=0, y=-2, size=4, color="#04080FAA") /// Icon filter that creates gaussian blur #define GAUSSIAN_BLUR(filter_size) filter(type="blur", size=filter_size) + +// The default color for admin say, used as a fallback when the preference is not enabled +#define DEFAULT_ASAY_COLOR "#FF4500" diff --git a/code/__DEFINES/food.dm b/code/__DEFINES/food.dm index 5b44a718a0efb..95b6229e0654a 100644 --- a/code/__DEFINES/food.dm +++ b/code/__DEFINES/food.dm @@ -19,11 +19,31 @@ #define BUGS (1<<18)*/ #define GORE (1<<19) -#define DRINK_BAD 1 -#define DRINK_NICE 2 -#define DRINK_GOOD 3 -#define DRINK_VERYGOOD 4 -#define DRINK_FANTASTIC 5 +/// A list of food type names, in order of their flags +#define FOOD_FLAGS list( \ + "MEAT", \ + "VEGETABLES", \ + "RAW", \ + "JUNKFOOD", \ + "GRAIN", \ + "FRUIT", \ + "DAIRY", \ + "FRIED", \ + "ALCOHOL", \ + "SUGAR", \ + "GROSS", \ + "TOXIC", \ + "PINEAPPLE", \ + "BREAKFAST", \ + "CLOTH", \ + "GORE", \ +) + +#define DRINK_BAD 1 +#define DRINK_NICE 2 +#define DRINK_GOOD 3 +#define DRINK_VERYGOOD 4 +#define DRINK_FANTASTIC 5 /// Food is "in a container", not in a code sense, but in a literal sense (canned foods) #define FOOD_IN_CONTAINER (1<<0) diff --git a/code/__DEFINES/ghost.dm b/code/__DEFINES/ghost.dm index 0b5826bccab04..7247ba65a0acb 100644 --- a/code/__DEFINES/ghost.dm +++ b/code/__DEFINES/ghost.dm @@ -10,21 +10,13 @@ /// Ghosts will orbit objects in a pentagon #define GHOST_ORBIT_PENTAGON "pentagon" -///////// Ghost showing preferences SQL values: ///////// -/// The main player's ghost will display as a simple white ghost -#define GHOST_ACCS_NONE 1 -/// The main player's ghost will display as a transparent mob -#define GHOST_ACCS_DIR 50 -/// The main player's ghost will display as a transparent mob with clothing -#define GHOST_ACCS_FULL 100 - ///////// Ghost showing preferences ///////// /// The main player's ghost will display as a simple white ghost -#define GHOST_ACCS_NONE_NAME "Default sprites" +#define GHOST_ACCS_NONE "Default sprites" /// The main player's ghost will display as a transparent mob -#define GHOST_ACCS_DIR_NAME "Only directional sprites" +#define GHOST_ACCS_DIR "Only directional sprites" /// The main player's ghost will display as a transparent mob with clothing -#define GHOST_ACCS_FULL_NAME "Full accessories" +#define GHOST_ACCS_FULL "Full accessories" /// The default ghost display selection for the main player #define GHOST_ACCS_DEFAULT_OPTION GHOST_ACCS_FULL @@ -34,26 +26,15 @@ GLOBAL_LIST_INIT(ghost_accs_options, list(GHOST_ACCS_NONE, GHOST_ACCS_DIR, GHOST ///////// Ghost viewing others preferences ///////// /// The other players ghosts will display as a simple white ghost -#define GHOST_OTHERS_SIMPLE 1 -/// The other players ghosts will display as transparent mobs -#define GHOST_OTHERS_DEFAULT_SPRITE 50 -/// The other players ghosts will display as transparent mobs with clothing -#define GHOST_OTHERS_THEIR_SETTING 100 - -///////// Ghost viewing others preferences human-readable: ///////// -/// The other players ghosts will display as a simple white ghost -#define GHOST_OTHERS_SIMPLE_NAME "white ghost" +#define GHOST_OTHERS_SIMPLE "White ghosts" /// The other players ghosts will display as transparent mobs -#define GHOST_OTHERS_DEFAULT_SPRITE_NAME "default sprites" +#define GHOST_OTHERS_DEFAULT_SPRITE "Default sprites" /// The other players ghosts will display as transparent mobs with clothing -#define GHOST_OTHERS_THEIR_SETTING_NAME "their setting" +#define GHOST_OTHERS_THEIR_SETTING "Their sprites" /// The default ghost view others for the main player #define GHOST_OTHERS_DEFAULT_OPTION GHOST_OTHERS_THEIR_SETTING -GLOBAL_LIST_INIT(ghost_others_options, list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DEFAULT_SPRITE, GHOST_OTHERS_THEIR_SETTING)) //Same as ghost_accs_options. - - // DEADCHAT MESSAGE TYPES // /// Deadchat notification for new players who join the round at arrivals #define DEADCHAT_ARRIVALRATTLE "arrivalrattle" diff --git a/code/__DEFINES/menu.dm b/code/__DEFINES/menu.dm deleted file mode 100644 index 60a7a2379c1e5..0000000000000 --- a/code/__DEFINES/menu.dm +++ /dev/null @@ -1,3 +0,0 @@ -#define CHECKBOX_NONE 0 -#define CHECKBOX_GROUP 1 -#define CHECKBOX_TOGGLE 2 diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm index 24b610852b3cc..b4573ba75ecad 100644 --- a/code/__DEFINES/preferences.dm +++ b/code/__DEFINES/preferences.dm @@ -1,5 +1,7 @@ -//Preference toggles +// Legacy preference toggles. +// !!! DO NOT ADD ANY NEW ONES HERE !!! +// Use `/datum/preference/toggle` instead. #define PREFTOGGLE_SOUND_ADMINHELP (1<<0) #define PREFTOGGLE_SOUND_MIDI (1<<1) #define PREFTOGGLE_SOUND_AMBIENCE (1<<2) @@ -31,7 +33,9 @@ // You CANNOT go above 1<<23 in BYOND due to integer limits // Please add subsequent ones as PREFTOGGLE_2_[name] -// If you run out of these, make a third toggles column +// Legacy preference toggles. +// !!! DO NOT ADD ANY NEW ONES HERE !!! +// Use `/datum/preference/toggle` instead. #define PREFTOGGLE_2_FANCY_TGUI (1<<0) #define PREFTOGGLE_2_LOCKED_TGUI (1<<1) #define PREFTOGGLE_2_LOCKED_BUTTONS (1<<2) @@ -56,7 +60,9 @@ #define TOGGLES_2_DEFAULT (PREFTOGGLE_2_FANCY_TGUI|PREFTOGGLE_2_LOCKED_TGUI|PREFTOGGLE_2_LOCKED_BUTTONS|PREFTOGGLE_2_WINDOW_FLASHING|PREFTOGGLE_2_CREW_OBJECTIVES|PREFTOGGLE_2_GHOST_HUD|PREFTOGGLE_2_GHOST_INQUISITIVENESS|PREFTOGGLE_2_AMBIENT_OCCLUSION|PREFTOGGLE_2_AUTO_FIT_VIEWPORT|PREFTOGGLE_2_ENABLE_TIPS|PREFTOGGLE_2_SHOW_CREDITS|PREFTOGGLE_2_HOTKEYS|PREFTOGGLE_2_SOUNDTRACK|PREFTOGGLE_2_TGUI_INPUT|PREFTOGGLE_2_BIG_BUTTONS|PREFTOGGLE_2_SWITCHED_BUTTONS|PREFTOGGLE_2_TGUI_SAY|PREFTOGGLE_2_SOUND_ADMINALERT) -//Chat toggles +// Legacy chat toggles. +// !!! DO NOT ADD ANY NEW ONES HERE !!! +// Use `/datum/preference/toggle` instead. #define CHAT_OOC (1<<0) #define CHAT_DEAD (1<<1) #define CHAT_GHOSTEARS (1<<2) @@ -73,11 +79,11 @@ #define TOGGLES_DEFAULT_CHAT (CHAT_OOC|CHAT_DEAD|CHAT_GHOSTEARS|CHAT_GHOSTSIGHT|CHAT_PRAYER|CHAT_RADIO|CHAT_PULLR|CHAT_GHOSTWHISPER|CHAT_GHOSTPDA|CHAT_GHOSTRADIO|CHAT_BANKCARD|CHAT_GHOSTLAWS|CHAT_GHOSTFOLLOWMINDLESS) -#define PARALLAX_INSANE -1 //for show offs -#define PARALLAX_HIGH 0 //default. -#define PARALLAX_MED 1 -#define PARALLAX_LOW 2 -#define PARALLAX_DISABLE 3 //this option must be the highest number +#define PARALLAX_INSANE "Insane" +#define PARALLAX_HIGH "High" +#define PARALLAX_MED "Medium" +#define PARALLAX_LOW "Low" +#define PARALLAX_DISABLE "Disabled" #define PIXEL_SCALING_AUTO 0 #define PIXEL_SCALING_1X 1 @@ -190,3 +196,38 @@ // True value of max save slots (3 is default, 8 is byond member, +1 to either if you have the extra slot loadout entry). Potential max is 9 #define TRUE_MAX_SAVE_SLOTS 9 + +// Values for /datum/preference/savefile_identifier +/// This preference is character specific. +#define PREFERENCE_CHARACTER "character" +/// This preference is account specific. +#define PREFERENCE_PLAYER "player" + +// Values for /datum/preferences/current_tab +/// Open the character preference window +#define PREFERENCE_TAB_CHARACTER_PREFERENCES 0 + +/// Open the game preferences window +#define PREFERENCE_TAB_GAME_PREFERENCES 1 + +/// Open the keybindings window +#define PREFERENCE_TAB_KEYBINDINGS 2 + +/// These will be shown in the character sidebar, but at the bottom. +#define PREFERENCE_CATEGORY_FEATURES "features" + +/// Any preferences that will show to the sides of the character in the setup menu. +#define PREFERENCE_CATEGORY_CLOTHING "clothing" + +/// Preferences that will be put into the 3rd list, and are not contextual. +#define PREFERENCE_CATEGORY_NON_CONTEXTUAL "non_contextual" + +/// Will be put under the game preferences window. +#define PREFERENCE_CATEGORY_GAME_PREFERENCES "game_preferences" + +/// These will show in the list to the right of the character preview. +#define PREFERENCE_CATEGORY_SECONDARY_FEATURES "secondary_features" + +/// These are preferences that are supplementary for main features, +/// such as hair color being affixed to hair. +#define PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES "supplemental_features" diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index d3d0ef751e7ea..258cf4b307479 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -122,13 +122,14 @@ #define INIT_ORDER_RESEARCH 75 #define INIT_ORDER_ORBITS 74 //Other things use the orbital map, so it needs to be made early on. #define INIT_ORDER_STATION 73 //This is high priority because it manipulates a lot of the subsystems that will initialize after it. +#define INIT_ORDER_QUIRKS 72 #define INIT_ORDER_JOBS 71 //Must initialize before events for holidays #define INIT_ORDER_EVENTS 70 -#define INIT_ORDER_QUIRKS 60 #define INIT_ORDER_AI_MOVEMENT 56 //We need the movement setup #define INIT_ORDER_AI_CONTROLLERS 55 //So the controller can get the ref #define INIT_ORDER_TICKER 55 #define INIT_ORDER_MAPPING 50 +#define INIT_ORDER_EARLY_ASSETS 48 #define INIT_ORDER_TIMETRACK 47 #define INIT_ORDER_NETWORKS 45 #define INIT_ORDER_ECONOMY 40 @@ -141,6 +142,7 @@ #define INIT_ORDER_DEFAULT 0 #define INIT_ORDER_AIR -1 #define INIT_ORDER_PERSISTENCE -2 //before assets because some assets take data from SSPersistence +#define INIT_ORDER_PERSISTENT_PAINTINGS -3 // Assets relies on this #define INIT_ORDER_ASSETS -4 #define INIT_ORDER_ICON_SMOOTHING -5 #define INIT_ORDER_OVERLAY -6 diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index ddf2d8a2770e4..a0926be3f8987 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -417,3 +417,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_SUIT_SENSORS "suit_sensors" ///Mob is tracked by nanites, and on glob suit sensors list #define TRAIT_NANITE_SENSORS "nanite_sensors" + +/// This human wants to see the color of their glasses, for some reason +#define TRAIT_SEE_GLASS_COLORS "see_glass_colors" diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index da852464a8ff2..cb1b66773e0f8 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -676,11 +676,12 @@ else L1[key] = other_value -/proc/assoc_list_strip_value(list/input) - var/list/ret = list() +/// Turns an associative list into a flat list of keys +/proc/assoc_to_keys(list/input) + var/list/keys = list() for(var/key in input) - ret += key - return ret + keys += key + return keys /proc/compare_list(list/l,list/d) if(!islist(l) || !islist(d)) diff --git a/code/__HELPERS/admin.dm b/code/__HELPERS/admin.dm new file mode 100644 index 0000000000000..94f61ef0152fd --- /dev/null +++ b/code/__HELPERS/admin.dm @@ -0,0 +1,3 @@ +/// Returns if the given client is an admin, REGARDLESS of if they're deadminned or not. +/proc/is_admin(client/client) + return !isnull(GLOB.admin_datums[client.ckey]) || !isnull(GLOB.deadmins[client.ckey]) diff --git a/code/__HELPERS/colors.dm b/code/__HELPERS/colors.dm index 182082daf295f..aee61fbea4d01 100644 --- a/code/__HELPERS/colors.dm +++ b/code/__HELPERS/colors.dm @@ -45,3 +45,23 @@ animate(C, color = animate_color, time = flash_time) #define RANDOM_COLOUR (rgb(rand(0,255),rand(0,255),rand(0,255))) + +/// Given a color in the format of "#RRGGBB", will return if the color +/// is dark. +/proc/is_color_dark(color, threshold = 0.25) + var/list/rgb = hex2rgb(color) + var/list/hsl = rgb2hsl(rgb[1], rgb[2], rgb[3]) + return hsl[3] < threshold + +/// Given a 3 character color (no hash), converts it into #RRGGBB (with hash) +/proc/expand_three_digit_color(color) + if (length_char(color) != 3) + CRASH("Invalid 3 digit color: [color]") + + var/final_color = "#" + + for (var/digit = 1 to 3) + final_color += copytext(color, digit, digit + 1) + final_color += copytext(color, digit, digit + 1) + + return final_color diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index e641b32d548fe..825703bac0ce7 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -515,7 +515,7 @@ var/mob/M = C if(M.client) C = M.client - if(!C || (!(C.prefs.toggles2 & PREFTOGGLE_2_WINDOW_FLASHING) && !ignorepref)) + if(!C || (!C.prefs.read_preference(/datum/preference/toggle/window_flashing) && !ignorepref)) return winset(C, "mainwindow", "flash=5") diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index be36e552db14c..fa5f1bb8ca947 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -28,7 +28,6 @@ init_sprite_accessory_subtypes(/datum/sprite_accessory/spines, GLOB.spines_list) init_sprite_accessory_subtypes(/datum/sprite_accessory/spines_animated, GLOB.animated_spines_list) init_sprite_accessory_subtypes(/datum/sprite_accessory/legs, GLOB.legs_list) - init_sprite_accessory_subtypes(/datum/sprite_accessory/wings, GLOB.r_wings_list,roundstart = TRUE) init_sprite_accessory_subtypes(/datum/sprite_accessory/caps, GLOB.caps_list) init_sprite_accessory_subtypes(/datum/sprite_accessory/moth_wings, GLOB.moth_wings_list) init_sprite_accessory_subtypes(/datum/sprite_accessory/moth_wings, GLOB.moth_wings_roundstart_list, roundstart = TRUE) diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index b2e7c4f8164d6..acfb42d875269 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -216,7 +216,6 @@ world #define TO_HEX_DIGIT(n) ascii2text((n&15) + ((n&15)<10 ? 48 : 87)) //Dummy mob reserve slots -#define DUMMY_HUMAN_SLOT_PREFERENCES "dummy_preference_preview" #define DUMMY_HUMAN_SLOT_ADMIN "admintools" // Multiply all alpha values by this float diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 28fd44f3a61cb..5f21a35629607 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -185,6 +185,22 @@ GLOBAL_LIST_INIT(skin_tones, sort_list(list( "african2" ))) +GLOBAL_LIST_INIT(skin_tone_names, list( + "african1" = "Medium brown", + "african2" = "Dark brown", + "albino" = "Albino", + "arab" = "Light brown", + "asian1" = "Ivory", + "asian2" = "Beige", + "caucasian1" = "Porcelain", + "caucasian2" = "Light peach", + "caucasian3" = "Peach", + "indian" = "Brown", + "latino" = "Light beige", + "mediterranean" = "Olive", +)) + +/// An assoc list of species IDs to type paths GLOBAL_LIST_EMPTY(species_list) /proc/age2agedescription(age) @@ -682,7 +698,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) return . //// Generalised helper proc for letting mobs rename themselves. Used to be clname() and ainame() -/mob/proc/apply_pref_name(role, client/C) +/mob/proc/apply_pref_name(preference_type, client/C) if(!C) C = client var/oldname = real_name @@ -693,20 +709,11 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) var/banned = C ? is_banned_from(C.ckey, "Appearance") : null while(loop && safety < 5) - if(C?.prefs.active_character.custom_names[role] && !safety && !banned) - newname = C.prefs.active_character.custom_names[role] + if(!safety && !banned) + newname = C?.prefs?.read_preference(preference_type) else - switch(role) - if("human") - newname = random_unique_name(gender) - if("clown") - newname = pick(GLOB.clown_names) - if("mime") - newname = pick(GLOB.mime_names) - if("ai") - newname = pick(GLOB.ai_names) - else - return FALSE + var/datum/preference/preference = GLOB.preference_entries[preference_type] + newname = preference.create_informed_default_value(C.prefs) for(var/mob/living/M in GLOB.player_list) if(M == src) diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index b012dc4f73c63..01a209f60536a 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -292,6 +292,7 @@ CHECK_TICK SSdbcore.SetRoundEnd() + //Collects persistence features if(mode.allow_persistence_save) SSpersistence.CollectData() diff --git a/code/__HELPERS/sanitize_values.dm b/code/__HELPERS/sanitize_values.dm index a08352531ef1a..9c311beeed781 100644 --- a/code/__HELPERS/sanitize_values.dm +++ b/code/__HELPERS/sanitize_values.dm @@ -96,3 +96,7 @@ HSL[3] = min(HSL[3],0.4) var/list/RGB = hsl2rgb(arglist(HSL)) return "#[num2hex(RGB[1],2)][num2hex(RGB[2],2)][num2hex(RGB[3],2)]" + +/// Makes sure the input color is text with a # at the start followed by 6 hexadecimal characters. Examples: "#ff1234", "#A38321", COLOR_GREEN_GRAY +/proc/sanitize_color(color) + return findtext(color, GLOB.is_color) ? color : GLOB.normal_ooc_colour diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm index b21f8aa7774d4..ba0d27e1e3f22 100644 --- a/code/_globalvars/lists/flavor_misc.dm +++ b/code/_globalvars/lists/flavor_misc.dm @@ -40,7 +40,6 @@ GLOBAL_LIST_EMPTY(animated_tails_list_human) GLOBAL_LIST_EMPTY(ears_list) GLOBAL_LIST_EMPTY(wings_list) GLOBAL_LIST_EMPTY(wings_open_list) -GLOBAL_LIST_EMPTY(r_wings_list) GLOBAL_LIST_EMPTY(moth_wings_list) GLOBAL_LIST_EMPTY(moth_wings_roundstart_list)//this lacks the blacklisted wings such as burned, clockwork and angel GLOBAL_LIST_EMPTY(moth_antennae_list) @@ -149,18 +148,26 @@ GLOBAL_LIST_INIT(ai_core_display_screens, sort_list(list( "Weird" ))) -/proc/resolve_ai_icon(input) +/// A form of resolve_ai_icon that is guaranteed to never sleep. +/// Not always accurate, but always synchronous. +/proc/resolve_ai_icon_sync(input) + SHOULD_NOT_SLEEP(TRUE) + if(!input || !(input in GLOB.ai_core_display_screens)) return "ai" else if(input == "Random") input = pick(GLOB.ai_core_display_screens - "Random") - if(input == "Portrait") - var/datum/portrait_picker/tgui = new(usr)//create the datum - tgui.ui_interact(usr)//datum has a tgui component, here we open the window - return "ai-portrait" //just take this until they decide return "ai-[lowertext(input)]" +/proc/resolve_ai_icon(input) + if (input == "Portrait") + var/datum/portrait_picker/tgui = new(usr)//create the datum + tgui.ui_interact(usr)//datum has a tgui component, here we open the window + return "ai-portrait" //just take this until they decide + + return resolve_ai_icon_sync(input) + GLOBAL_LIST_INIT(security_depts_prefs, sort_list(list( SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, diff --git a/code/_globalvars/lists/names.dm b/code/_globalvars/lists/names.dm index 6260aeb53efce..94eb15fef4379 100644 --- a/code/_globalvars/lists/names.dm +++ b/code/_globalvars/lists/names.dm @@ -35,23 +35,3 @@ GLOBAL_LIST_INIT(adjectives, world.file2list("strings/names/adjectives.txt")) GLOBAL_LIST_INIT(dream_strings, world.file2list("strings/dreamstrings.txt")) //loaded on startup because of " //would include in rsc if ' was used - -/* -List of configurable names in preferences and their metadata -"id" = list( - "pref_name" = "name", //pref label - "qdesc" = "name", //popup question text - "allow_numbers" = FALSE, // numbers allowed in the name - "group" = "whatever", // group (these will be grouped together on pref ui ,order still follows the list so they need to be concurrent to be grouped) - "allow_null" = FALSE // if empty name is entered it's replaced with default value - ), -*/ -GLOBAL_LIST_INIT(preferences_custom_names, list( - "ai" = list("pref_name" = "AI", "qdesc" = "ai name", "allow_numbers" = TRUE , "group" = "silicons", "allow_null" = FALSE), - "clown" = list("pref_name" = "Clown" , "qdesc" = "clown name", "allow_numbers" = FALSE , "group" = "fun", "allow_null" = FALSE), - "cyborg" = list("pref_name" = "Cyborg", "qdesc" = "cyborg name (Leave empty to use default naming scheme)", "allow_numbers" = TRUE , "group" = "silicons", "allow_null" = TRUE), - "deity" = list("pref_name" = "Chaplain deity", "qdesc" = "deity", "allow_numbers" = TRUE , "group" = "chaplain", "allow_null" = FALSE), - "human" = list("pref_name" = "Backup Human", "qdesc" = "backup human name, used in the event you are assigned a command role as another species", "allow_numbers" = FALSE , "group" = "backup_human", "allow_null" = FALSE), - "mime" = list("pref_name" = "Mime", "qdesc" = "mime name" , "allow_numbers" = FALSE , "group" = "fun", "allow_null" = FALSE), - "religion" = list("pref_name" = "Chaplain religion", "qdesc" = "religion" , "allow_numbers" = TRUE , "group" = "chaplain", "allow_null" = FALSE), -)) diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index cd99d734c3663..44cd6d9a0122f 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -16,7 +16,7 @@ GLOBAL_LIST_EMPTY_TYPED(singularities, /datum/component/singularity) //li GLOBAL_LIST_EMPTY(uploads_list) //list of all silicon uploads GLOBAL_LIST(chemical_reactions_list) //list of all /datum/chemical_reaction datums. Used during chemical reactions -GLOBAL_LIST(chemical_reagents_list) //list of all /datum/reagent datums indexed by reagent id. Used by chemistry stuff +GLOBAL_LIST_INIT(chemical_reagents_list, init_chemical_reagent_list()) //list of all /datum/reagent datums indexed by reagent id. Used by chemistry stuff GLOBAL_LIST_EMPTY(tech_list) //list of all /datum/tech datums indexed by id. GLOBAL_LIST_EMPTY(surgeries_list) //list of all surgeries by name, associated with their path. GLOBAL_LIST_EMPTY(crafting_recipes) //list of all table craft recipes diff --git a/code/_onclick/hud/action_button.dm b/code/_onclick/hud/action_button.dm index a83ab3de9aded..cdf91bb54289d 100644 --- a/code/_onclick/hud/action_button.dm +++ b/code/_onclick/hud/action_button.dm @@ -99,14 +99,15 @@ usr.client.prefs.action_buttons_screen_locs["[name]_[id]"] = locked ? moved : null return TRUE if(modifiers["alt"]) + var/buttons_locked = usr.client.prefs.read_preference(/datum/preference/toggle/buttons_locked) for(var/V in usr.actions) var/datum/action/A = V var/atom/movable/screen/movable/action_button/B = A.button B.moved = FALSE if(B.id && usr.client) usr.client.prefs.action_buttons_screen_locs["[B.name]_[B.id]"] = null - B.locked = usr.client.prefs.toggles2 & PREFTOGGLE_2_LOCKED_BUTTONS - locked = usr.client.prefs.toggles2 & PREFTOGGLE_2_LOCKED_BUTTONS + B.locked = buttons_locked + locked = buttons_locked moved = FALSE if(id && usr.client) usr.client.prefs.action_buttons_screen_locs["[name]_[id]"] = null diff --git a/code/_onclick/hud/ghost.dm b/code/_onclick/hud/ghost.dm index 30a0c8bb9ff86..230de1afcd050 100644 --- a/code/_onclick/hud/ghost.dm +++ b/code/_onclick/hud/ghost.dm @@ -89,10 +89,10 @@ if(!.) return var/mob/screenmob = viewmob || mymob - if(!(screenmob.client.prefs.toggles2 & PREFTOGGLE_2_GHOST_HUD)) - screenmob.client.screen -= static_inventory - else + if(screenmob.client.prefs.read_preference(/datum/preference/toggle/ghost_hud)) screenmob.client.screen += static_inventory + else + screenmob.client.screen -= static_inventory //We should only see observed mob alerts. /datum/hud/ghost/reorganize_alerts(mob/viewmob) diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index 1090d26ed0a32..5a3b7a888ea52 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -55,6 +55,18 @@ GLOBAL_LIST_INIT(available_ui_styles, list( var/list/atom/movable/plane_master_controller/plane_master_controllers = list() var/list/team_finder_arrows = list() + /// Whether or not screentips are enabled. + /// This is updated by the preference for cheaper reads than would be + /// had with a proc call, especially on one of the hottest procs in the + /// game (MouseEntered). + var/screentips_enabled = TRUE + + /// The color to use for the screentips. + /// This is updated by the preference for cheaper reads than would be + /// had with a proc call, especially on one of the hottest procs in the + /// game (MouseEntered). + var/screentip_color + var/atom/movable/screen/movable/action_button/hide_toggle/hide_actions_toggle var/action_buttons_hidden = FALSE @@ -70,12 +82,12 @@ GLOBAL_LIST_INIT(available_ui_styles, list( if (!ui_style) // will fall back to the default if any of these are null - ui_style = ui_style2icon(owner.client && owner.client.prefs && owner.client.prefs.UI_style) + ui_style = ui_style2icon(owner.client?.prefs?.read_preference(/datum/preference/choiced/ui_style)) hide_actions_toggle = new hide_actions_toggle.InitialiseIcon(src) if(mymob.client) - hide_actions_toggle.locked = mymob.client.prefs.toggles2 & PREFTOGGLE_2_LOCKED_BUTTONS + hide_actions_toggle.locked = mymob.client.prefs.read_preference(/datum/preference/toggle/buttons_locked) hand_slots = list() diff --git a/code/_onclick/hud/parallax.dm b/code/_onclick/hud/parallax.dm index 571e092355451..55d09176f40f9 100755 --- a/code/_onclick/hud/parallax.dm +++ b/code/_onclick/hud/parallax.dm @@ -57,10 +57,10 @@ var/mob/screenmob = viewmob || mymob var/client/C = screenmob.client if(C.prefs) - var/pref = C.prefs.parallax + var/pref = C.prefs.read_preference(/datum/preference/choiced/parallax) if (isnull(pref)) pref = PARALLAX_HIGH - switch(C.prefs.parallax) + switch(pref) if (PARALLAX_INSANE) C.parallax_layers_max = 5 return TRUE diff --git a/code/_onclick/hud/rendering/plane_master.dm b/code/_onclick/hud/rendering/plane_master.dm index dd3dc8d3aabbc..b2527bfa837d2 100644 --- a/code/_onclick/hud/rendering/plane_master.dm +++ b/code/_onclick/hud/rendering/plane_master.dm @@ -65,7 +65,7 @@ /atom/movable/screen/plane_master/game_world/backdrop(mob/mymob) . = ..() remove_filter("AO") - if(istype(mymob) && (mymob.client?.prefs?.toggles2 & PREFTOGGLE_2_AMBIENT_OCCLUSION)) + if(istype(mymob) && mymob.client?.prefs?.read_preference(/datum/preference/toggle/ambient_occlusion)) add_filter("AO", 1, drop_shadow_filter(x = 0, y = -2, size = 4, color = "#04080FAA")) remove_filter("eye_blur") if(istype(mymob) && mymob.eye_blurry) @@ -184,7 +184,7 @@ /atom/movable/screen/plane_master/runechat/backdrop(mob/mymob) . = ..() remove_filter("AO") - if(istype(mymob) && (mymob.client?.prefs?.toggles2 & PREFTOGGLE_2_AMBIENT_OCCLUSION)) + if(istype(mymob) && mymob.client?.prefs?.read_preference(/datum/preference/toggle/ambient_occlusion)) add_filter("AO", 1, drop_shadow_filter(x = 0, y = -2, size = 4, color = "#04080FAA")) /atom/movable/screen/plane_master/gravpulse diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm index fc1b8fadf8616..6a9edddcd9ae3 100644 --- a/code/_onclick/observer.dm +++ b/code/_onclick/observer.dm @@ -54,7 +54,7 @@ return TRUE else if(IsAdminGhost(user)) attack_ai(user) - else if(user.client.prefs.toggles2 & PREFTOGGLE_2_GHOST_INQUISITIVENESS) + else if(user.client.prefs.read_preference(/datum/preference/toggle/inquisitive_ghost)) user.examinate(src) return FALSE diff --git a/code/controllers/master.dm b/code/controllers/master.dm index b129561f1c973..ef3f17bd99a75 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -59,6 +59,10 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/current_runlevel //for scheduling different subsystems for different stages of the round var/sleep_offline_after_initializations = TRUE + /// During initialization, will be the instanced subsytem that is currently initializing. + /// Outside of initialization, returns null. + var/current_initializing_subsystem = null + var/static/restart_clear = 0 var/static/restart_timeout = 0 var/static/restart_count = 0 @@ -212,8 +216,10 @@ GLOBAL_REAL(Master, /datum/controller/master) = new for (var/datum/controller/subsystem/SS in subsystems) if (SS.flags & SS_NO_INIT || SS.initialized) //Don't init SSs with the correspondig flag or if they already are initialzized continue + current_initializing_subsystem = SS SS.Initialize(REALTIMEOFDAY) CHECK_TICK + current_initializing_subsystem = null current_ticklimit = TICK_LIMIT_RUNNING var/time = (REALTIMEOFDAY - start_timeofday) / 10 diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm index 1c3aa443ccb06..cfbcfc428dec5 100644 --- a/code/controllers/subsystem/atoms.dm +++ b/code/controllers/subsystem/atoms.dm @@ -215,6 +215,14 @@ SUBSYSTEM_DEF(atoms) if(fails & BAD_INIT_SLEPT) . += "- Slept during Initialize()\n" +/// Prepares an atom to be deleted once the atoms SS is initialized. +/datum/controller/subsystem/atoms/proc/prepare_deletion(atom/target) + if (initialized == INITIALIZATION_INNEW_REGULAR) + // Atoms SS has already completed, just kill it now. + qdel(target) + else + queued_deletions += WEAKREF(target) + /datum/controller/subsystem/atoms/Shutdown() var/initlog = InitLog() if(initlog) diff --git a/code/controllers/subsystem/early_assets.dm b/code/controllers/subsystem/early_assets.dm new file mode 100644 index 0000000000000..db1ffb13333ba --- /dev/null +++ b/code/controllers/subsystem/early_assets.dm @@ -0,0 +1,24 @@ +/// Initializes any assets that need to be loaded ASAP. +/// This houses preference menu assets, since they can be loaded at any time, +/// most dangerously before the atoms SS initializes. +/// Thus, we want it to fail consistently in CI as if it would've if a player +/// opened it up early. +SUBSYSTEM_DEF(early_assets) + name = "Early Assets" + init_order = INIT_ORDER_EARLY_ASSETS + flags = SS_NO_FIRE + +/datum/controller/subsystem/early_assets/Initialize(start_timeofday) + for (var/datum/asset/asset_type as anything in subtypesof(/datum/asset)) + if (initial(asset_type._abstract) == asset_type) + continue + + if (!initial(asset_type.early)) + continue + + if (!get_asset_datum(asset_type)) + stack_trace("Could not initialize early asset [asset_type]!") + + CHECK_TICK + + return ..() diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index b313a9105c7d8..e88344a4099ec 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -36,7 +36,6 @@ SUBSYSTEM_DEF(job) SetupOccupations() if(CONFIG_GET(flag/load_jobs_from_txt)) LoadJobs() - set_overflow_role(CONFIG_GET(string/overflow_job)) spare_id_safe_code = "[rand(0,9)][rand(0,9)][rand(0,9)][rand(0,9)][rand(0,9)]" @@ -435,26 +434,34 @@ SUBSYSTEM_DEF(job) //We couldn't find a job from prefs for this guy. /datum/controller/subsystem/job/proc/HandleUnassigned(mob/dead/new_player/player) + var/jobless_role = player.client.prefs.read_preference(/datum/preference/choiced/jobless_role) + if(PopcapReached() && !IS_PATRON(player.ckey)) RejectPlayer(player) - else if(player.client.prefs.active_character.joblessrole == BEOVERFLOW) - var/allowed_to_be_a_loser = !is_banned_from(player.ckey, SSjob.overflow_role) - if(QDELETED(player) || !allowed_to_be_a_loser) - RejectPlayer(player) - else - if(!AssignRole(player, SSjob.overflow_role)) + return + + switch (jobless_role) + if (BEOVERFLOW) + var/datum/job/overflow_role_datum = GetJobType(overflow_role) + var/allowed_to_be_a_loser = !is_banned_from(player.ckey, overflow_role_datum.title) + if(QDELETED(player) || !allowed_to_be_a_loser) RejectPlayer(player) - else if(player.client.prefs.active_character.joblessrole == BERANDOMJOB) - if(!GiveRandomJob(player)) + else + if(!AssignRole(player, overflow_role_datum)) + RejectPlayer(player) + if (BERANDOMJOB) + if(!GiveRandomJob(player)) + RejectPlayer(player) + if (RETURNTOLOBBY) RejectPlayer(player) - else if(player.client.prefs.active_character.joblessrole == RETURNTOLOBBY) - RejectPlayer(player) - else //Something gone wrong if we got here. - var/message = "DO: [player] fell through handling unassigned" - JobDebug(message) - log_game(message) - message_admins(message) - RejectPlayer(player) + else //Something gone wrong if we got here. + var/message = "DO: [player] fell through handling unassigned" + JobDebug(message) + log_game(message) + message_admins(message) + RejectPlayer(player) + + //Gives the player the stuff he should have with his rank /datum/controller/subsystem/job/proc/EquipRank(mob/M, rank, joined_late = FALSE) var/mob/dead/new_player/newplayer diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 975992d894953..2c839ec78c133 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -343,7 +343,7 @@ GLOBAL_LIST_EMPTY(the_station_areas) var/pmv = CONFIG_GET(flag/preference_map_voting) if(pmv) for (var/client/c in GLOB.clients) - var/vote = c.prefs.preferred_map + var/vote = c.prefs.read_preference(/datum/preference/choiced/preferred_map) if (!vote) if (global.config.defaultmap) mapvotes[global.config.defaultmap.map_name] += 1 diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm index c368e4d97d91b..1ca0c4da965f1 100644 --- a/code/controllers/subsystem/vote.dm +++ b/code/controllers/subsystem/vote.dm @@ -68,8 +68,8 @@ SUBSYSTEM_DEF(vote) else if(mode == "map") for (var/non_voter_ckey in non_voters) var/client/C = non_voters[non_voter_ckey] - if(C.prefs.preferred_map) - var/preferred_map = C.prefs.preferred_map + var/preferred_map = C.prefs.read_preference(/datum/preference/choiced/preferred_map) + if(preferred_map) choices[preferred_map] += 1 greatest_votes = max(greatest_votes, choices[preferred_map]) else if(global.config.defaultmap) diff --git a/code/datums/action.dm b/code/datums/action.dm index 44c6ffcdd482c..29f9f89030848 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -68,7 +68,7 @@ M.actions += src if(M.client) M.client.screen += button - button.locked = (M.client.prefs.toggles2 & PREFTOGGLE_2_LOCKED_BUTTONS) || button.id ? M.client.prefs.action_buttons_screen_locs["[name]_[button.id]"] : FALSE //even if it's not defaultly locked we should remember we locked it before + button.locked = M.client.prefs.read_preference(/datum/preference/toggle/buttons_locked) || button.id ? M.client.prefs.action_buttons_screen_locs["[name]_[button.id]"] : FALSE //even if it's not defaultly locked we should remember we locked it before button.moved = button.id ? M.client.prefs.action_buttons_screen_locs["[name]_[button.id]"] : FALSE var/obj/effect/proc_holder/spell/spell_proc_holder = button.linked_action.target if(istype(spell_proc_holder) && spell_proc_holder.text_overlay) diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index 4930749e8f762..0f7a4c14eac13 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -174,8 +174,15 @@ friend_talk(message) +<<<<<<< HEAD /mob/camera/imaginary_friend/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode) to_chat(src, compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode)) +======= +/mob/camera/imaginary_friend/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list()) + if (client?.prefs.read_preference(/datum/preference/toggle/enable_runechat) && (client.prefs.read_preference(/datum/preference/toggle/enable_runechat_non_mobs) || ismob(speaker))) + create_chat_message(speaker, message_language, raw_message, spans) + to_chat(src, compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mods)) +>>>>>>> 5a4c87a9fc3 (tgui Preferences Menu + total rewrite of the preferences backend (#61313)) /mob/camera/imaginary_friend/proc/friend_talk(message) message = treat_message_min(trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))) diff --git a/code/datums/mind.dm b/code/datums/mind.dm index f3ebf8c47b262..13c78d754f7ea 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -307,6 +307,7 @@ var/obj/item/uplink_loc var/implant = FALSE +<<<<<<< HEAD if(traitor_mob.client?.prefs) switch(traitor_mob.client.prefs.active_character.uplink_spawn_loc) if(UPLINK_PDA) @@ -330,9 +331,26 @@ if(!uplink_loc) uplink_loc = P if(UPLINK_PEN) +======= + var/uplink_spawn_location = traitor_mob.client?.prefs?.read_preference(/datum/preference/choiced/uplink_location) + switch (uplink_spawn_location) + if(UPLINK_PDA) + uplink_loc = PDA + if(!uplink_loc) + uplink_loc = R + if(!uplink_loc) uplink_loc = P - if(UPLINK_IMPLANT) - implant = TRUE + if(UPLINK_RADIO) + uplink_loc = R + if(!uplink_loc) + uplink_loc = PDA + if(!uplink_loc) +>>>>>>> 5a4c87a9fc3 (tgui Preferences Menu + total rewrite of the preferences backend (#61313)) + uplink_loc = P + if(UPLINK_PEN) + uplink_loc = P + if(UPLINK_IMPLANT) + implant = TRUE if(!uplink_loc) // We've looked everywhere, let's just implant you implant = TRUE diff --git a/code/datums/view.dm b/code/datums/view.dm index d82d4e4866bbf..6053297541f87 100644 --- a/code/datums/view.dm +++ b/code/datums/view.dm @@ -25,11 +25,11 @@ /datum/viewData/proc/assertFormat()//T-Pose winset(chief, "mapwindow.map", "zoom=0") -/datum/viewData/proc/resetFormat()//Cuck - winset(chief, "mapwindow.map", "zoom=[chief.prefs.pixel_size]") +/datum/viewData/proc/resetFormat() + winset(chief, "mapwindow.map", "zoom=[chief.prefs.read_preference(/datum/preference/numeric/pixel_size)]") /datum/viewData/proc/setZoomMode() - winset(chief, "mapwindow.map", "zoom-mode=[chief.prefs.scaling_method]") + winset(chief, "mapwindow.map", "zoom-mode=[chief.prefs.read_preference(/datum/preference/choiced/scaling_method)]") /datum/viewData/proc/isZooming() return (width || height) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm index f69f0ba6f59b5..7ad4d8fa73668 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm @@ -19,7 +19,6 @@ role_preference_key = role_preference )) candidates.Remove(P) - continue /datum/dynamic_ruleset/latejoin/ready(forced = 0) if (forced) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index 512f00ae76a2f..fe487b97e5c55 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -459,6 +459,9 @@ /datum/dynamic_ruleset/roundstart/nuclear/clown_ops name = "Clown Ops" antag_datum = /datum/antagonist/nukeop/clownop + antag_flag = ROLE_CLOWN_OPERATIVE + antag_preference = ROLE_OPERATIVE + antag_flag_override = ROLE_OPERATIVE antag_leader_datum = /datum/antagonist/nukeop/leader/clownop requirements = list(101,101,101,101,101,101,101,101,101,101) diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index 0b6ee5041fe62..6ff3aa5f5135a 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -785,7 +785,7 @@ What a mess.*/ active1.fields["age"] = t1 if("species") if(istype(active1, /datum/data/record)) - var/t1 = input("Select a species", "Species Selection") as null|anything in GLOB.roundstart_races + var/t1 = input("Select a species", "Species Selection") as null|anything in get_selectable_species() if(!canUseSecurityRecordsConsole(usr, t1, a1)) return active1.fields["species"] = t1 @@ -1022,7 +1022,7 @@ What a mess.*/ if(6) R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") if(7) - R.fields["species"] = pick(GLOB.roundstart_races) + R.fields["species"] = pick(get_selectable_species()) if(8) var/datum/data/record/G = pick(GLOB.data_core.general) R.fields["photo_front"] = G.fields["photo_front"] diff --git a/code/game/machinery/telecomms/broadcasting.dm b/code/game/machinery/telecomms/broadcasting.dm index edc885eda86dd..4bc8193778bee 100644 --- a/code/game/machinery/telecomms/broadcasting.dm +++ b/code/game/machinery/telecomms/broadcasting.dm @@ -171,11 +171,6 @@ // From the list of radios, find all mobs who can hear those. var/list/receive = get_mobs_in_radio_ranges(radios) - // Cut out mobs with clients who are admins and have radio chatter disabled. - for(var/mob/R in receive) - if (R.client && R.client.holder && !(R.client.prefs.chat_toggles & CHAT_RADIO)) - receive -= R - // Add observers who have ghost radio enabled. for(var/mob/dead/observer/M in GLOB.player_list) if(M.client && (M.client.prefs.chat_toggles & CHAT_GHOSTRADIO)) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index c27565377b149..58270da4d7236 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1044,7 +1044,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) openToolTip(user,src,params,title = name,content = "[desc]
    Force: [force_string]",theme = "") /obj/item/MouseEntered(location, control, params) - if((item_flags & PICKED_UP || item_flags & IN_STORAGE) && (usr.client.prefs.toggles2 & PREFTOGGLE_2_ENABLE_TIPS) && !QDELETED(src)) + if((item_flags & PICKED_UP || item_flags & IN_STORAGE) && usr.client.prefs.read_preference(/datum/preference/toggle/enable_tooltips) && !QDELETED(src)) var/timedelay = usr.client.prefs.tip_delay/100 var/user = usr tip_timer = addtimer(CALLBACK(src, PROC_REF(openTip), location, control, params, user), timedelay, TIMER_STOPPABLE)//timer takes delay in deciseconds, but the pref is in milliseconds. dividing by 100 converts it. @@ -1066,9 +1066,8 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) /obj/item/proc/apply_outline(colour = null) if(!(item_flags & PICKED_UP || item_flags & IN_STORAGE) || QDELETED(src) || isobserver(usr)) return - if(usr.client) - if(!(usr.client.prefs.toggles & PREFTOGGLE_OUTLINE_ENABLED)) - return + if(!usr.client?.prefs?.read_preference(/datum/preference/toggle/item_outlines)) + return if(!colour) if(usr.client) colour = usr.client.prefs.outline_color diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index f0b0a96560c70..84031e4e6548c 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -68,13 +68,6 @@ GLOBAL_PROTECT(admin_verbs_admin) /client/proc/toggle_combo_hud, // toggle display of the combination pizza antag and taco sci/med/eng hud /client/proc/toggle_AI_interact, /*toggle admin ability to interact with machines as an AI*/ /datum/admins/proc/open_shuttlepanel, /* Opens shuttle manipulator UI */ - /client/proc/deadchat, - /client/proc/toggleprayers, - /client/proc/toggle_prayer_sound, - /client/proc/colorasay, - /client/proc/resetasaycolor, - /client/proc/toggleadminhelpsound, - /client/proc/toggleadminalertsound, /client/proc/respawn_character, /datum/admins/proc/open_borgopanel, /client/proc/fix_say, diff --git a/code/modules/admin/poll_management.dm b/code/modules/admin/poll_management.dm index 88bd9a31774ba..593833ac84aed 100644 --- a/code/modules/admin/poll_management.dm +++ b/code/modules/admin/poll_management.dm @@ -328,7 +328,7 @@ SELECT p.text, pv.rating, COUNT(*) output += "
    " var/datum/browser/panel = new(usr, "pmpanel", "Poll Management Panel", 780, 640) panel.add_stylesheet("admin_panelscss", 'html/admin/admin_panels.css') - if(usr.client.prefs.toggles2 & PREFTOGGLE_2_FANCY_TGUI) //some browsers (IE8) have trouble with unsupported css3 elements that break the panel's functionality, so we won't load those if a user is in no frills tgui mode since that's for similar compatability support + if(usr.client.prefs.read_preference(/datum/preference/toggle/tgui_fancy)) //some browsers (IE8) have trouble with unsupported css3 elements that break the panel's functionality, so we won't load those if a user is in no frills tgui mode since that's for similar compatability support panel.add_stylesheet("admin_panelscss3", 'html/admin/admin_panels_css3.css') panel.set_content(jointext(output, "")) panel.open() @@ -638,7 +638,7 @@ SELECT p.text, pv.rating, COUNT(*) panel_height = 320 var/datum/browser/panel = new(usr, "popanel", "Poll Option Panel", 370, panel_height) panel.add_stylesheet("admin_panelscss", 'html/admin/admin_panels.css') - if(usr.client.prefs.toggles2 & PREFTOGGLE_2_FANCY_TGUI) //some browsers (IE8) have trouble with unsupported css3 elements that break the panel's functionality, so we won't load those if a user is in no frills tgui mode since that's for similar compatability support + if(usr.client.prefs.read_preference(/datum/preference/toggle/tgui_fancy)) //some browsers (IE8) have trouble with unsupported css3 elements that break the panel's functionality, so we won't load those if a user is in no frills tgui mode since that's for similar compatability support panel.add_stylesheet("admin_panelscss3", 'html/admin/admin_panels_css3.css') panel.set_content(jointext(output, "")) panel.open() diff --git a/code/modules/admin/sql_ban_system.dm b/code/modules/admin/sql_ban_system.dm index 219df92204ada..ad50ef9e0bbba 100644 --- a/code/modules/admin/sql_ban_system.dm +++ b/code/modules/admin/sql_ban_system.dm @@ -148,7 +148,8 @@ var/datum/browser/panel = new(usr, "banpanel", "Banning Panel", 910, panel_height) panel.add_stylesheet("admin_panelscss", 'html/admin/admin_panels.css') panel.add_stylesheet("banpanelcss", 'html/admin/banpanel.css') - if(usr.client.prefs.toggles2 & PREFTOGGLE_2_FANCY_TGUI) //some browsers (IE8) have trouble with unsupported css3 elements and DOM methods that break the panel's functionality, so we won't load those if a user is in no frills tgui mode since that's for similar compatability support + var/tgui_fancy = usr.client.prefs.read_preference(/datum/preference/toggle/tgui_fancy) + if(tgui_fancy) //some browsers (IE8) have trouble with unsupported css3 elements and DOM methods that break the panel's functionality, so we won't load those if a user is in no frills tgui mode since that's for similar compatability support panel.add_stylesheet("admin_panelscss3", 'html/admin/admin_panels_css3.css') panel.add_script("banpaneljs", 'html/admin/banpanel.js') var/list/output = list("
    [HrefTokenFormField()]") diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index 19c4ec47db8e9..aca69c2dd5cc0 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -15,7 +15,8 @@ mob.log_talk(msg, LOG_ASAY) msg = keywords_lookup(msg) - var/custom_asay_color = (CONFIG_GET(flag/allow_admin_asaycolor) && prefs.asaycolor) ? "" : "" + var/asay_color = prefs.read_preference(/datum/preference/color/asay_color) + var/custom_asay_color = (CONFIG_GET(flag/allow_admin_asaycolor) && asay_color) ? "" : "" msg = "ADMIN: [key_name(usr, 1)] [ADMIN_FLW(mob)]: [custom_asay_color][msg][custom_asay_color ? "":null]" to_chat(GLOB.admins, msg, allow_linkify = TRUE, type = MESSAGE_TYPE_ADMINCHAT) diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index 4bdfd8cbec2de..8733a5bb031b3 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -30,6 +30,8 @@ GLOBAL_LIST(admin_antag_list) var/antagpanel_category = "Uncategorized" //Antagpanel will display these together, REQUIRED var/show_name_in_check_antagonists = FALSE //Will append antagonist name in admin listings - use for categories that share more than one antag type var/show_to_ghosts = FALSE // Should this antagonist be shown as antag to ghosts? Shouldn't be used for stealthy antagonists like traitors + /// The typepath for the outfit to show in the preview for the preferences menu. + var/preview_outfit /datum/antagonist/proc/show_tips(fileid) if(!owner || !owner.current || !owner.current.client) @@ -262,6 +264,42 @@ GLOBAL_LIST(admin_antag_list) /datum/antagonist/proc/get_admin_commands() . = list() +/// Creates an icon from the preview outfit. +/// Custom implementors of `get_preview_icon` should use this, as the +/// result of `get_preview_icon` is expected to be the completed version. +/datum/antagonist/proc/render_preview_outfit(datum/outfit/outfit, mob/living/carbon/human/dummy) + dummy = dummy || new /mob/living/carbon/human/dummy/consistent + dummy.equipOutfit(outfit, visualsOnly = TRUE) + COMPILE_OVERLAYS(dummy) + var/icon = getFlatIcon(dummy) + + // We don't want to qdel the dummy right away, since its items haven't initialized yet. + SSatoms.prepare_deletion(dummy) + + return icon + +/// Given an icon, will crop it to be consistent of those in the preferences menu. +/// Not necessary, and in fact will look bad if it's anything other than a human. +/datum/antagonist/proc/finish_preview_icon(icon/icon) + // Zoom in on the top of the head and the chest + // I have no idea how to do this dynamically. + icon.Scale(115, 115) + + // This is probably better as a Crop, but I cannot figure it out. + icon.Shift(WEST, 8) + icon.Shift(SOUTH, 30) + + icon.Crop(1, 1, ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE) + + return icon + +/// Returns the icon to show on the preferences menu. +/datum/antagonist/proc/get_preview_icon() + if (isnull(preview_outfit)) + return null + + return finish_preview_icon(render_preview_outfit(preview_outfit)) + /datum/antagonist/Topic(href,href_list) if(!check_rights(R_ADMIN)) return diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm index 433f9117bdc14..bb81cc5c40425 100644 --- a/code/modules/antagonists/abductor/abductor.dm +++ b/code/modules/antagonists/abductor/abductor.dm @@ -13,6 +13,26 @@ var/landmark_type var/greet_text +/datum/antagonist/abductor/get_preview_icon() + var/mob/living/carbon/human/dummy/consistent/scientist = new + var/mob/living/carbon/human/dummy/consistent/agent = new + + scientist.set_species(/datum/species/abductor) + agent.set_species(/datum/species/abductor) + + var/icon/scientist_icon = render_preview_outfit(/datum/outfit/abductor/scientist, scientist) + scientist_icon.Shift(WEST, 8) + + var/icon/agent_icon = render_preview_outfit(/datum/outfit/abductor/agent, agent) + agent_icon.Shift(EAST, 8) + + var/icon/final_icon = scientist_icon + final_icon.Blend(agent_icon, ICON_OVERLAY) + + qdel(scientist) + qdel(agent) + + return finish_preview_icon(final_icon) /datum/antagonist/abductor/agent name = "Abductor Agent" diff --git a/code/modules/antagonists/blob/blob.dm b/code/modules/antagonists/blob/blob.dm index f8cc423d127f5..57c6b91058fe9 100644 --- a/code/modules/antagonists/blob/blob.dm +++ b/code/modules/antagonists/blob/blob.dm @@ -32,6 +32,16 @@ create_objectives() . = ..() +/datum/antagonist/blob/get_preview_icon() + var/datum/blobstrain/reagent/reactive_spines/reactive_spines = /datum/blobstrain/reagent/reactive_spines + + var/icon/icon = icon('icons/mob/blob.dmi', "blob_core") + icon.Blend(initial(reactive_spines.color), ICON_MULTIPLY) + icon.Blend(icon('icons/mob/blob.dmi', "blob_core_overlay"), ICON_OVERLAY) + icon.Scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE) + + return icon + /datum/antagonist/blob/proc/create_objectives() if(!give_objectives) return diff --git a/code/modules/antagonists/brother/brother.dm b/code/modules/antagonists/brother/brother.dm index 78513285dd071..ed178f764f745 100644 --- a/code/modules/antagonists/brother/brother.dm +++ b/code/modules/antagonists/brother/brother.dm @@ -35,6 +35,34 @@ /datum/antagonist/brother/antag_panel_data() return "Conspirators : [get_brother_names()]" +/datum/antagonist/brother/get_preview_icon() + var/mob/living/carbon/human/dummy/consistent/brother1 = new + var/mob/living/carbon/human/dummy/consistent/brother2 = new + + brother1.dna.features["ethcolor"] = GLOB.color_list_ethereal["Faint Red"] + brother1.set_species(/datum/species/ethereal) + + brother2.dna.features["moth_antennae"] = "Plain" + brother2.dna.features["moth_markings"] = "None" + brother2.dna.features["moth_wings"] = "Plain" + brother2.set_species(/datum/species/moth) + + var/icon/brother1_icon = render_preview_outfit(/datum/outfit/job/quartermaster, brother1) + brother1_icon.Blend(icon('icons/effects/blood.dmi', "maskblood"), ICON_OVERLAY) + brother1_icon.Shift(WEST, 8) + + var/icon/brother2_icon = render_preview_outfit(/datum/outfit/job/scientist, brother2) + brother2_icon.Blend(icon('icons/effects/blood.dmi', "uniformblood"), ICON_OVERLAY) + brother2_icon.Shift(EAST, 8) + + var/icon/final_icon = brother1_icon + final_icon.Blend(brother2_icon, ICON_OVERLAY) + + qdel(brother1) + qdel(brother2) + + return finish_preview_icon(final_icon) + /datum/antagonist/brother/proc/get_brother_names() var/list/brothers = team.members - owner var/brother_text = "" diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm index c8b5db362d87a..554118363ce62 100644 --- a/code/modules/antagonists/changeling/changeling.dm +++ b/code/modules/antagonists/changeling/changeling.dm @@ -607,8 +607,23 @@ return parts.Join("
    ") +<<<<<<< HEAD /datum/antagonist/changeling/antag_listing_name() return ..() + "([changelingID])" /datum/antagonist/changeling/xenobio/antag_listing_name() return ..() + "(Xenobio)" + +/datum/antagonist/changeling/get_preview_icon() + var/icon/final_icon = render_preview_outfit(/datum/outfit/changeling) + var/icon/split_icon = render_preview_outfit(/datum/outfit/job/engineer) + + final_icon.Shift(WEST, world.icon_size / 2) + final_icon.Shift(EAST, world.icon_size / 2) + + split_icon.Shift(EAST, world.icon_size / 2) + split_icon.Shift(WEST, world.icon_size / 2) + + final_icon.Blend(split_icon, ICON_OVERLAY) + + return finish_preview_icon(final_icon) diff --git a/code/modules/antagonists/creep/creep.dm b/code/modules/antagonists/creep/creep.dm index d295623d42f55..b002677a5d994 100644 --- a/code/modules/antagonists/creep/creep.dm +++ b/code/modules/antagonists/creep/creep.dm @@ -7,6 +7,7 @@ roundend_category = "obsessed" count_against_dynamic_roll_chance = FALSE silent = TRUE //not actually silent, because greet will be called by the trauma anyway. + preview_outfit = /datum/outfit/obsessed var/datum/brain_trauma/special/obsessed/trauma /datum/antagonist/obsessed/admin_add(datum/mind/new_owner,mob/admin) @@ -48,7 +49,41 @@ var/mob/living/M = mob_override || owner.current update_obsession_icons_removed(M) -/datum/antagonist/obsessed/proc/forge_objectives(var/datum/mind/obsessionmind) +/datum/antagonist/obsessed/get_preview_icon() + var/mob/living/carbon/human/dummy/consistent/victim_dummy = new + victim_dummy.hair_color = "b96" // Brown + victim_dummy.hairstyle = "Messy" + victim_dummy.update_hair() + + var/icon/obsessed_icon = render_preview_outfit(preview_outfit) + obsessed_icon.Blend(icon('icons/effects/blood.dmi', "uniformblood"), ICON_OVERLAY) + + var/icon/final_icon = finish_preview_icon(obsessed_icon) + + final_icon.Blend( + icon('icons/ui_icons/antags/obsessed.dmi', "obsession"), + ICON_OVERLAY, + ANTAGONIST_PREVIEW_ICON_SIZE - 30, + 20, + ) + + return final_icon + +/datum/outfit/obsessed + name = "Obsessed (Preview only)" + + uniform = /obj/item/clothing/under/misc/overalls + gloves = /obj/item/clothing/gloves/color/latex + mask = /obj/item/clothing/mask/surgical + neck = /obj/item/camera + suit = /obj/item/clothing/suit/apron + +/datum/outfit/obsessed/post_equip(mob/living/carbon/human/H) + for(var/obj/item/carried_item in H.get_equipped_items(TRUE)) + carried_item.add_mob_blood(H)//Oh yes, there will be blood... + H.regenerate_icons() + +/datum/antagonist/obsessed/proc/forge_objectives(datum/mind/obsessionmind) var/list/objectives_left = list("spendtime", "polaroid", "hug") var/datum/objective/assassinate/obsessed/kill = new kill.owner = owner diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm index f8cab78fdf3ac..ec98d8ae0bda9 100644 --- a/code/modules/antagonists/cult/cult.dm +++ b/code/modules/antagonists/cult/cult.dm @@ -5,7 +5,7 @@ roundend_category = "cultists" antagpanel_category = "Cult" antag_moodlet = /datum/mood_event/cult - var/datum/action/innate/cult/comm/communion = new + preview_outfit = /datum/outfit/cultist var/datum/action/innate/cult/mastervote/vote = new var/datum/action/innate/cult/blood_magic/magic = new banning_key = BAN_ROLE_CULTIST @@ -68,6 +68,24 @@ if(cult_team.blood_target && cult_team.blood_target_image && current.client) current.client.images += cult_team.blood_target_image +/datum/antagonist/cult/get_preview_icon() + var/icon/icon = render_preview_outfit(preview_outfit) + + // The longsword is 64x64, but getFlatIcon crunches to 32x32. + // So I'm just going to add it in post, screw it. + + // Center the dude, because item icon states start from the center. + // This makes the image 64x64. + icon.Crop(-15, -15, 48, 48) + + var/obj/item/melee/cultblade/longsword = new + icon.Blend(icon(longsword.lefthand_file, longsword.inhand_icon_state), ICON_OVERLAY) + qdel(longsword) + + // Move the guy back to the bottom left, 32x32. + icon.Crop(17, 17, 48, 48) + + return finish_preview_icon(icon) /datum/antagonist/cult/proc/equip_cultist(metal=TRUE) var/mob/living/carbon/C = owner.current @@ -451,3 +469,19 @@ /datum/team/cult/is_gamemode_hero() return SSticker.mode.name == "cult" + +/datum/outfit/cultist + name = "Cultist (Preview only)" + + uniform = /obj/item/clothing/under/color/black + suit = /obj/item/clothing/suit/hooded/cultrobes/alt + shoes = /obj/item/clothing/shoes/cult/alt + r_hand = /obj/item/melee/blood_magic/stun + +/datum/outfit/cultist/post_equip(mob/living/carbon/human/H, visualsOnly) + H.eye_color = BLOODCULT_EYE + H.update_body() + + var/obj/item/clothing/suit/hooded/hooded = locate() in H + hooded.MakeHood() // This is usually created on Initialize, but we run before atoms + hooded.ToggleHood() diff --git a/code/modules/antagonists/gang/gang.dm b/code/modules/antagonists/gang/gang.dm index a36a0b98f9b9e..fe4ec21e2c356 100644 --- a/code/modules/antagonists/gang/gang.dm +++ b/code/modules/antagonists/gang/gang.dm @@ -4,6 +4,7 @@ can_coexist_with_others = FALSE banning_key = BAN_ROLE_GANG antagpanel_category = "Gang" + preview_outfit = /datum/outfit/gangster var/hud_type = "gangster" var/message_name = "Gangster" var/datum/team/gang/gang @@ -268,6 +269,11 @@ name = "Gang Lieutenant" message_name = "Lieutenant" +/datum/outfit/gangster + name = "Gangster (Preview only)" + uniform = /obj/item/clothing/under/suit/henchmen + back = /obj/item/storage/backpack/henchmen + #define INFLUENCE_SWAG 2 #define INFLUENCE_TERRITORY 0 #define INFLUENCE_BASE 20 diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm index d7d4bb08e7ce8..273a9bef77fa5 100644 --- a/code/modules/antagonists/nukeop/nukeop.dm +++ b/code/modules/antagonists/nukeop/nukeop.dm @@ -10,6 +10,10 @@ var/always_new_team = FALSE //If not assigned a team by default ops will try to join existing ones, set this to TRUE to always create new team. var/send_to_spawnpoint = TRUE //Should the user be moved to default spawnpoint. var/nukeop_outfit = /datum/outfit/syndicate + preview_outfit = /datum/outfit/nuclear_operative_elite + + /// In the preview icon, the nukies who are behind the leader + var/preview_outfit_behind = /datum/outfit/nuclear_operative /datum/antagonist/nukeop/proc/update_synd_icons_added(mob/living/M) var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS] @@ -20,9 +24,6 @@ var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS] opshud.leave_hud(M) set_antag_hud(M, null) - -/datum/antagonist/nukeop/apply_innate_effects(mob/living/mob_override) - var/mob/living/M = mob_override || owner.current update_synd_icons_added(M) ADD_TRAIT(owner, TRAIT_DISK_VERIFIER, NUKEOP_TRAIT) owner.remove_all_quirks() @@ -169,6 +170,40 @@ else to_chat(admin, "No valid nuke found!") +/datum/antagonist/nukeop/get_preview_icon() + if (!preview_outfit) + return null + + var/icon/final_icon = render_preview_outfit(preview_outfit) + + if (!isnull(preview_outfit_behind)) + var/icon/teammate = render_preview_outfit(preview_outfit_behind) + teammate.Blend(rgb(128, 128, 128, 128), ICON_MULTIPLY) + + final_icon.Blend(teammate, ICON_OVERLAY, -world.icon_size / 4, 0) + final_icon.Blend(teammate, ICON_OVERLAY, world.icon_size / 4, 0) + + return finish_preview_icon(final_icon) + +/datum/outfit/nuclear_operative + name = "Nuclear Operative (Preview only)" + + suit = /obj/item/clothing/suit/space/hardsuit/syndi + head = /obj/item/clothing/head/helmet/space/hardsuit/syndi + +/datum/outfit/nuclear_operative_elite + name = "Nuclear Operative (Elite, Preview only)" + + suit = /obj/item/clothing/suit/space/hardsuit/syndi/elite + head = /obj/item/clothing/head/helmet/space/hardsuit/syndi/elite + l_hand = /obj/item/modular_computer/tablet/nukeops + r_hand = /obj/item/shield/energy + +/datum/outfit/nuclear_operative_elite/post_equip(mob/living/carbon/human/H, visualsOnly) + var/obj/item/shield/energy/shield = locate() in H.held_items + shield.icon_state = "[shield.base_icon_state]1" + H.update_inv_hands() + /datum/antagonist/nukeop/leader name = "Nuclear Operative Leader" nukeop_outfit = /datum/outfit/syndicate/leader @@ -238,6 +273,8 @@ always_new_team = TRUE send_to_spawnpoint = FALSE //Handled by event nukeop_outfit = /datum/outfit/syndicate/full + preview_outfit = /datum/outfit/nuclear_operative + preview_outfit_behind = null /datum/antagonist/nukeop/lone/assign_nuke() if(nuke_team && !nuke_team.tracked_nuke) diff --git a/code/modules/antagonists/revenant/revenant_antag.dm b/code/modules/antagonists/revenant/revenant_antag.dm index 261d6973e47e6..7cbe08f09c2e4 100644 --- a/code/modules/antagonists/revenant/revenant_antag.dm +++ b/code/modules/antagonists/revenant/revenant_antag.dm @@ -23,3 +23,6 @@ /datum/antagonist/revenant/on_gain() forge_objectives() . = ..() + +/datum/antagonist/revenant/get_preview_icon() + return finish_preview_icon(icon('icons/mob/mob.dmi', "revenant_idle")) diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm index c2e5ffa4b2dc7..17b13edbcfb70 100644 --- a/code/modules/antagonists/revolution/revolution.dm +++ b/code/modules/antagonists/revolution/revolution.dm @@ -177,6 +177,37 @@ revhud.leave_hud(M) set_antag_hud(M, null) +/datum/antagonist/rev/head/get_preview_icon() + var/icon/final_icon = render_preview_outfit(preview_outfit) + + final_icon.Blend(make_assistant_icon("Business Hair"), ICON_UNDERLAY, -8, 0) + final_icon.Blend(make_assistant_icon("CIA"), ICON_UNDERLAY, 8, 0) + + // Apply the rev head HUD, but scale up the preview icon a bit beforehand. + // Otherwise, the R gets cut off. + final_icon.Scale(64, 64) + + var/icon/rev_head_icon = icon('icons/mob/hud.dmi', "rev_head") + rev_head_icon.Scale(48, 48) + rev_head_icon.Crop(1, 1, 64, 64) + rev_head_icon.Shift(EAST, 10) + rev_head_icon.Shift(NORTH, 16) + final_icon.Blend(rev_head_icon, ICON_OVERLAY) + + return finish_preview_icon(final_icon) + +/datum/antagonist/rev/head/proc/make_assistant_icon(hairstyle) + var/mob/living/carbon/human/dummy/consistent/assistant = new + assistant.hairstyle = hairstyle + assistant.update_hair() + + var/icon/assistant_icon = render_preview_outfit(/datum/outfit/job/assistant/consistent, assistant) + assistant_icon.ChangeOpacity(0.5) + + qdel(assistant) + + return assistant_icon + /datum/antagonist/rev/proc/can_be_converted(mob/living/candidate) if(!candidate.mind) return FALSE @@ -551,5 +582,14 @@ /datum/team/revolution/is_gamemode_hero() return SSticker.mode.name == "revolution" +/datum/outfit/revolutionary + name = "Revolutionary (Preview only)" + + uniform = /obj/item/clothing/under/costume/soviet + head = /obj/item/clothing/head/ushanka + gloves = /obj/item/clothing/gloves/color/black + l_hand = /obj/item/spear + r_hand = /obj/item/assembly/flash + #undef DECONVERTER_STATION_WIN #undef DECONVERTER_REVS_WIN diff --git a/code/modules/antagonists/space_dragon/space_dragon.dm b/code/modules/antagonists/space_dragon/space_dragon.dm index c1237056d0ed4..baa76dc237b08 100644 --- a/code/modules/antagonists/space_dragon/space_dragon.dm +++ b/code/modules/antagonists/space_dragon/space_dragon.dm @@ -184,6 +184,17 @@ qdel(rift) rift_list.Cut() +/datum/antagonist/space_dragon/get_preview_icon() + var/icon/icon = icon('icons/mob/spacedragon.dmi', "spacedragon") + + icon.Blend(COLOR_STRONG_VIOLET, ICON_MULTIPLY) + icon.Blend(icon('icons/mob/spacedragon.dmi', "overlay_base"), ICON_OVERLAY) + + icon.Crop(10, 9, 54, 53) + icon.Scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE) + + return icon + /datum/objective/summon_carp var/datum/antagonist/space_dragon/dragon explanation_text = "Summon 3 rifts in order to flood the station with carp. Your possible rift locations are: (ERROR)." diff --git a/code/modules/antagonists/swarmer/swarmer.dm b/code/modules/antagonists/swarmer/swarmer.dm index 0f404a6587399..3835db8ce3a1b 100644 --- a/code/modules/antagonists/swarmer/swarmer.dm +++ b/code/modules/antagonists/swarmer/swarmer.dm @@ -831,3 +831,8 @@ parts += "The swarm has failed." return "
    [parts.Join("
    ")]
    " + +/datum/antagonist/swarmer/get_preview_icon() + var/icon/swarmer_icon = icon('icons/mob/swarmer.dmi', "swarmer") + swarmer_icon.Shift(NORTH, 8) + return finish_preview_icon(swarmer_icon) diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 5c0f78f442a41..5a64de2961375 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -428,3 +428,19 @@ /datum/antagonist/traitor/is_gamemode_hero() return SSticker.mode.name == "traitor" + +/datum/outfit/traitor + name = "Traitor (Preview only)" + + uniform = /obj/item/clothing/under/color/grey + suit = /obj/item/clothing/suit/hooded/ablative + gloves = /obj/item/clothing/gloves/color/yellow + mask = /obj/item/clothing/mask/gas + l_hand = /obj/item/melee/energy/sword + r_hand = /obj/item/gun/energy/kinetic_accelerator/crossbow + +/datum/outfit/traitor/post_equip(mob/living/carbon/human/H, visualsOnly) + var/obj/item/melee/energy/sword/sword = locate() in H.held_items + sword.icon_state = "e_sword_on_red" + sword.worn_icon_state = "e_sword_on_red" + H.update_inv_hands() diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm index 63b41a4f57964..5b909e327bf44 100644 --- a/code/modules/antagonists/wizard/wizard.dm +++ b/code/modules/antagonists/wizard/wizard.dm @@ -5,6 +5,7 @@ banning_key = BAN_ROLE_WIZARD antag_moodlet = /datum/mood_event/focused hijack_speed = 0.5 + preview_outfit = /datum/outfit/wizard var/strip = TRUE //strip before equipping var/allow_rename = TRUE var/hud_version = "wizard" diff --git a/code/modules/antagonists/xeno/xeno.dm b/code/modules/antagonists/xeno/xeno.dm index 70aeaabc42354..6b42559808fac 100644 --- a/code/modules/antagonists/xeno/xeno.dm +++ b/code/modules/antagonists/xeno/xeno.dm @@ -51,6 +51,8 @@ if(owner.antag_hud_icon_state == "xenomorph") set_antag_hud(owner.current, null) +/datum/antagonist/xeno/get_preview_icon() + return finish_preview_icon(icon('icons/mob/alien.dmi', "alienh")) //XENO /mob/living/carbon/alien/mind_initialize() diff --git a/code/modules/asset_cache/asset_list.dm b/code/modules/asset_cache/asset_list.dm index ddd0acfc54737..31c5955411754 100644 --- a/code/modules/asset_cache/asset_list.dm +++ b/code/modules/asset_cache/asset_list.dm @@ -25,6 +25,9 @@ GLOBAL_LIST_EMPTY(asset_datums) /// config can, of course, be disabled. var/cross_round_cachable = FALSE + /// Whether or not this asset should be loaded in the "early assets" SS + var/early = FALSE + /datum/asset/New() GLOB.asset_datums[type] = src register() @@ -508,5 +511,31 @@ GLOBAL_LIST_EMPTY(asset_datums) /datum/asset/simple/namespaced/proc/get_htmlloader(filename) return url2htmlloader(SSassets.transport.get_asset_url(filename, assets[filename])) +/// A subtype to generate a JSON file from a list +/datum/asset/json + _abstract = /datum/asset/json + /// The filename, will be suffixed with ".json" + var/name + +/datum/asset/json/send(client) + return SSassets.transport.send_assets(client, "data/[name].json") + +/datum/asset/json/get_url_mappings() + return list( + "[name].json" = SSassets.transport.get_asset_url("data/[name].json"), + ) + +/datum/asset/json/register() + var/filename = "data/[name].json" + fdel(filename) + text2file(json_encode(generate()), filename) + SSassets.transport.register_asset(filename, fcopy_rsc(filename)) + fdel(filename) + +/// Returns the data that will be JSON encoded +/datum/asset/json/proc/generate() + SHOULD_CALL_PARENT(FALSE) + CRASH("generate() not implemented for [type]!") + #undef ASSET_CROSS_ROUND_CACHE_DIRECTORY diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm index ed6c73bac4077..f663dce2ba395 100644 --- a/code/modules/asset_cache/asset_list_items.dm +++ b/code/modules/asset_cache/asset_list_items.dm @@ -505,10 +505,9 @@ assets = list() /datum/asset/simple/portraits/New() - if(!SSpersistence.paintings || !SSpersistence.paintings[tab] || !length(SSpersistence.paintings[tab])) + if(!length(SSpersistent_paintings.paintings[tab])) return - for(var/p in SSpersistence.paintings[tab]) - var/list/portrait = p + for(var/list/portrait as anything in SSpersistent_paintings.paintings[tab]) var/png = "data/paintings/[tab]/[portrait["md5"]].png" if(fexists(png)) var/asset_name = "[tab]_[portrait["md5"]]" diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index f02bafb04ee13..7ade6f31f3fe4 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -121,3 +121,6 @@ /// If the client is currently under the restrictions of the interview system var/interviewee = FALSE + + /// Whether or not this client has standard hotkeys enabled + var/hotkeys = TRUE diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index c9df9515dc772..d5a22bd4ae15a 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -122,13 +122,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( hsrc = mentor_datum if("usr") hsrc = mob - if("prefs") - if (inprefs) - return - inprefs = TRUE - . = prefs.process_link(usr,href_list) - inprefs = FALSE - return if("vars") return view_var_Topic(href,href_list,hsrc) @@ -261,7 +254,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( GLOB.preferences_datums[ckey] = prefs prefs.last_ip = address //these are gonna be used for banning prefs.last_id = computer_id //these are gonna be used for banning - fps = prefs.clientfps prefs.handle_donator_items() @@ -414,7 +406,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( add_admin_verbs() to_chat(src, get_message_output("memo")) adminGreet() - add_verbs_from_config() var/cached_player_age = set_client_age_from_db(tdata) //we have to cache this because other shit may change it and we need it's current value now down below. @@ -955,7 +946,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( to_chat(src, "Your previous click was ignored because you've done too many in a second") return - if (prefs.toggles2 & PREFTOGGLE_2_HOTKEYS) + if (hotkeys) // If hotkey mode is enabled, then clicking the map will automatically // unfocus the text bar. This removes the red color from the text bar // so that the visual focus indicator matches reality. @@ -1042,7 +1033,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( if (isliving(mob)) var/mob/living/M = mob M.update_damage_hud() - if (prefs.toggles2 & PREFTOGGLE_2_AUTO_FIT_VIEWPORT) + if (prefs.read_preference(/datum/preference/toggle/auto_fit_viewport)) addtimer(CALLBACK(src,.verb/fit_viewport,10)) //Delayed to avoid wingets from Login calls. /client/proc/generate_clickcatcher() diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 195be67722bc6..86f7957dbb4d5 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -18,90 +18,103 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/datum/character_save/active_character //game-preferences - var/lastchangelog = "" //Saved changlog filesize to detect if there was a change - var/ooccolor = "#c43b23" - var/asaycolor = "#ff4500" //This won't change the color for current admins, only incoming ones. - var/tip_delay = 500 //tip delay in milliseconds + var/lastchangelog = "" //Saved changlog filesize to detect if there was a change //Antag preferences - var/list/be_special = list() //Special role selection - var/tmp/old_be_special = 0 //Bitflag version of be_special, used to update old savefiles and nothing more - //If it's 0, that's good, if it's anything but 0, the owner of this prefs file's antag choices were, - //autocorrected this round, not that you'd need to check that. + var/list/be_special = list() //Special role selection - var/UI_style = null - var/outline_color = COLOR_BLUE_GRAY + /// Custom keybindings. Map of keybind names to keyboard inputs. + /// For example, by default would have "swap_hands" -> list("X") + var/list/key_bindings = list() - ///Whether we want balloon alerts displayed alone, with chat or not displayed at all - var/see_balloon_alerts = BALLOON_ALERT_ALWAYS + /// Cached list of keybindings, mapping keys to actions. + /// For example, by default would have "X" -> list("swap_hands") + var/list/key_bindings_by_key = list() var/toggles = TOGGLES_DEFAULT var/toggles2 = TOGGLES_2_DEFAULT var/db_flags var/chat_toggles = TOGGLES_DEFAULT_CHAT var/ghost_form = "ghost" - var/ghost_orbit = GHOST_ORBIT_CIRCLE - var/ghost_accs = GHOST_ACCS_DEFAULT_OPTION - var/ghost_others = GHOST_OTHERS_DEFAULT_OPTION - var/preferred_map = null - var/pda_theme = THEME_NTOS - var/pda_color = "#808000" - // Custom Keybindings - var/list/key_bindings = null + //character preferences + var/slot_randomized //keeps track of round-to-round randomization of the character slot, prevents overwriting - // 0 = character settings, 1 = game preferences - var/current_tab = 0 + var/list/randomise = list() + + //Quirk list + var/list/all_quirks = list() + + //Job preferences 2.0 - indexed by job title , no key or value implies never + var/list/job_preferences = list() + + /// The current window, PREFERENCE_TAB_* in [`code/__DEFINES/preferences.dm`] + var/current_window = PREFERENCE_TAB_CHARACTER_PREFERENCES var/unlock_content = 0 var/list/ignoring = list() - var/clientfps = 40 - var/updated_fps = 0 + var/list/exp = list() + + var/action_buttons_screen_locs = list() - var/parallax + ///Someone thought we were nice! We get a little heart in OOC until we join the server past the below time (we can keep it until the end of the round otherwise) + var/hearted + ///If we have a hearted commendations, we honor it every time the player loads preferences until this time has been passed + var/hearted_until + /// If we have persistent scars enabled + var/persistent_scars = TRUE + ///What outfit typepaths we've favorited in the SelectEquipment menu + var/list/favorite_outfits = list() - ///What size should pixels be displayed as? 0 is strech to fit - var/pixel_size = 0 - ///What scaling method should we use? - var/scaling_method = "normal" + /// A preview of the current character + var/atom/movable/screen/character_preview_view/character_preview_view - var/list/exp = list() - var/job_exempt = 0 + /// A list of instantiated middleware + var/list/datum/preference_middleware/middleware = list() - //Loadout stuff - var/list/purchased_gear = list() - var/gear_tab = "General" + /// The savefile relating to core preferences, PREFERENCE_PLAYER + var/savefile/game_savefile - var/action_buttons_screen_locs = list() + /// The savefile relating to character preferences, PREFERENCE_CHARACTER + var/savefile/character_savefile - var/pai_name = "" - var/pai_description = "" - var/pai_comment = "" + /// A list of keys that have been updated since the last save. + var/list/recently_updated_keys = list() -/datum/preferences/proc/set_max_character_slots(newmax) - max_usable_slots = min(TRUE_MAX_SAVE_SLOTS, newmax) // Make sure they dont go over - check_usable_slots() + /// A cache of preference entries to values. + /// Used to avoid expensive READ_FILE every time a preference is retrieved. + var/value_cache = list() + + /// If set to TRUE, will update character_profiles on the next ui_data tick. + var/tainted_character_profiles = FALSE + +/datum/preferences/Destroy(force, ...) + QDEL_NULL(character_preview_view) + QDEL_LIST(middleware) + value_cache = null + return ..() /datum/preferences/New(client/C) parent = C - character_saves.len = TRUE_MAX_SAVE_SLOTS - for(var/i in 1 to TRUE_MAX_SAVE_SLOTS) - var/datum/character_save/CS = new() - CS.slot_number = i - character_saves[i] = CS + for (var/middleware_type in subtypesof(/datum/preference_middleware)) + middleware += new middleware_type(src) - UI_style = GLOB.available_ui_styles[1] if(istype(C)) - if(!IS_GUEST_KEY(C.key)) - unlock_content = C.IsByondMember() + if(!IsGuestKey(C.key)) + load_path(C.ckey) + unlock_content = !!C.IsByondMember() if(unlock_content) - set_max_character_slots(8) - else if(!length(key_bindings)) // Guests need default keybinds - key_bindings = deep_copy_list(GLOB.keybinding_list_by_key) - var/loaded_preferences_successfully = load_from_database() + max_save_slots = 8 + + // give them default keybinds and update their movement keys + key_bindings = deepCopyList(GLOB.default_hotkeys) + key_bindings_by_key = get_key_bindings_by_key(key_bindings) + randomise = get_default_randomization() + + var/loaded_preferences_successfully = load_preferences() if(loaded_preferences_successfully) if("6030fe461e610e2be3a2c3e75c06067e" in purchased_gear) //MD5 hash of, "extra character slot" set_max_character_slots(max_usable_slots + 1) @@ -114,1147 +127,339 @@ GLOBAL_LIST_EMPTY(preferences_datums) return //we couldn't load character data so just randomize the character appearance + name - active_character = character_saves[1] - var/fallback_default_species = CONFIG_GET(string/fallback_default_species) - if(!active_character.pref_species && fallback_default_species != "random") - var/datum/species/spath = GLOB.species_list[fallback_default_species || "human"] - active_character.pref_species = new spath - active_character.randomise() //let's create a random character then - rather than a fat, bald and naked man. - active_character.real_name = active_character.pref_species.random_name(active_character.gender, TRUE) - if(!loaded_preferences_successfully) - save_preferences() - active_character.save(C) //let's save this new random character so it doesn't keep generating new ones. - return - -#define APPEARANCE_CATEGORY_COLUMN "
    " -#define MAX_MUTANT_ROWS 4 - -/datum/preferences/proc/ShowChoices(mob/user) - if(!user || !user.client) - return - active_character.update_preview_icon(user.client) - var/list/dat = list(TOOLTIP_CSS_SETUP, "
    ") - - dat += "Character Settings" - dat += "Antagonist Preferences" - dat += "Game Preferences" - var/shop_name = "[CONFIG_GET(string/metacurrency_name)] Shop" - dat += "[shop_name]" - dat += "OOC Preferences" - - dat += "
    " - - dat += "
    " - - switch(current_tab) - if (0) // Character Settings# - dat += "
    " - var/name - var/unspaced_slots = 0 - for(var/datum/character_save/CS as anything in character_saves) - unspaced_slots++ - if(unspaced_slots > 4) - dat += "
    " - unspaced_slots = 0 - name = CS.real_name - if(!name) - name = "Character [CS.slot_number]" - if(CS.slot_locked) - dat += "[name] (Locked) " - else - dat += "[name] " - dat += "
    " - - dat += "

    Occupation Choices

    " - dat += "Set Occupation Preferences
    " - if(CONFIG_GET(flag/roundstart_traits)) - dat += "

    Quirk Setup

    " - dat += "Configure Quirks
    " - dat += "
    Current Quirks: [length(active_character.all_quirks) ? active_character.all_quirks.Join(", ") : "None"]
    " - dat += "

    Identity

    " - dat += "" - - dat += "
    " - if(is_banned_from(user.ckey, "Appearance")) - dat += "You are banned from using custom names and appearances. You can continue to adjust your characters, but you will be randomised once you join the game.
    " - dat += "Random Name " - dat += "Always Random Name: [active_character.be_random_name ? "Yes" : "No"]
    " - - dat += "[TOOLTIP_CONFIG_CALLER("Name:", 400, "preferences.naming_policy")] " - dat += "[active_character.real_name]
    " - - if(!(AGENDER in active_character.pref_species.species_traits)) - var/dispGender - if(active_character.gender == MALE) - dispGender = "Male" - else if(active_character.gender == FEMALE) - dispGender = "Female" - else - dispGender = "Other" - dat += "Gender: [dispGender]
    " - if(active_character.gender == PLURAL || active_character.gender == NEUTER) - dat += "Body Model:[active_character.features["body_model"] == MALE ? "Masculine" : "Feminine"]
    " - dat += "Age: [active_character.age]
    " - - dat += "Special Names:
    " - var/old_group - for(var/custom_name_id in GLOB.preferences_custom_names) - var/namedata = GLOB.preferences_custom_names[custom_name_id] - if(!old_group) - old_group = namedata["group"] - else if(old_group != namedata["group"]) - old_group = namedata["group"] - dat += "
    " - dat += "[namedata["pref_name"]]: [active_character.custom_names[custom_name_id]] " - dat += "

    " - - dat += "Custom Job Preferences:
    " - dat += "Preferred AI Core Display: [active_character.preferred_ai_core_display]
    " - dat += "Preferred Security Department: [active_character.preferred_security_department]
    " - - dat += "

    Body

    " - dat += "Random Body " - dat += "Always Random Body: [active_character.be_random_body ? "Yes" : "No"]
    " - - dat += "" - - var/use_skintones = active_character.pref_species.use_skintones - if(use_skintones) - - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

    Skin Tone

    " - - dat += "[active_character.skin_tone]
    " - - var/mutant_colors - if((MUTCOLORS in active_character.pref_species.species_traits) || (MUTCOLORS_PARTSONLY in active_character.pref_species.species_traits)) - - if(!use_skintones) - dat += APPEARANCE_CATEGORY_COLUMN + randomise_appearance_prefs() //let's create a random character then - rather than a fat, bald and naked man. - dat += "

    Mutant Color

    " + C?.set_macros() - dat += "   Change
    " - - mutant_colors = TRUE - - if(istype(active_character.pref_species, /datum/species/ethereal)) //not the best thing to do tbf but I dont know whats better. - - if(!use_skintones) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

    Ethereal Color

    " - - dat += "   Change
    " - - if(istype(active_character.pref_species, /datum/species/plasmaman)) - - if(!use_skintones) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

    Envirohelmet Type

    " - - dat += "[active_character.helmet_style]
    " - - if((EYECOLOR in active_character.pref_species.species_traits) && !(NOEYESPRITES in active_character.pref_species.species_traits)) - - if(!use_skintones && !mutant_colors) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

    Eye Color

    " - - dat += "   Change
    " - - dat += "" - else if(use_skintones || mutant_colors) - dat += "" - - if(HAIR in active_character.pref_species.species_traits) - - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

    Hair Style

    " - - dat += "[active_character.hair_style]
    " - dat += "<>
    " - dat += "   Change
    " - - dat += "

    Gradient Style

    " - - dat += "[active_character.gradient_style]
    " - dat += "<>
    " - dat += "   Change
    " - - dat += "

    Facial Hair Style

    " - - dat += "[active_character.facial_hair_style]
    " - dat += "<>
    " - dat += "   Change
    " - - dat += "" - - //Mutant stuff - var/mutant_category = 0 - - if("tail_lizard" in active_character.pref_species.default_features) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

    Tail

    " - - dat += "[active_character.features["tail_lizard"]]
    " - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - - if("snout" in active_character.pref_species.default_features) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

    Snout

    " - - dat += "[active_character.features["snout"]]
    " + if(!loaded_preferences_successfully) + save_preferences() + save_character() //let's save this new random character so it doesn't keep generating new ones. - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 +/datum/preferences/ui_interact(mob/user, datum/tgui/ui) + // If you leave and come back, re-register the character preview + if (!isnull(character_preview_view) && !(character_preview_view in user.client?.screen)) + user.client?.register_map_obj(character_preview_view) - if("horns" in active_character.pref_species.default_features) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PreferencesMenu") + ui.set_autoupdate(FALSE) + ui.open() - dat += "

    Horns

    " + // HACK: Without this the character starts out really tiny because of some BYOND bug. + // You can fix it by changing a preference, so let's just forcably update the body to emulate this. + addtimer(CALLBACK(character_preview_view, /atom/movable/screen/character_preview_view/proc/update_body), 1 SECONDS) - dat += "[active_character.features["horns"]]
    " +/datum/preferences/ui_state(mob/user) + return GLOB.always_state - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 +// Without this, a hacker would be able to edit other people's preferences if +// they had the ref to Topic to. +/datum/preferences/ui_status(mob/user, datum/ui_state/state) + return user.client == parent ? UI_INTERACTIVE : UI_CLOSE - if("frills" in active_character.pref_species.default_features) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN +/datum/preferences/ui_data(mob/user) + var/list/data = list() - dat += "

    Frills

    " + if (isnull(character_preview_view)) + character_preview_view = create_character_preview_view(user) + else if (character_preview_view.client != parent) + // The client re-logged, and doing this when they log back in doesn't seem to properly + // carry emissives. + character_preview_view.register_to_client(parent) - dat += "[active_character.features["frills"]]
    " + if (tainted_character_profiles) + data["character_profiles"] = create_character_profiles() + tainted_character_profiles = FALSE - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 + data["character_preferences"] = compile_character_preferences(user) - if("spines" in active_character.pref_species.default_features) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN + data["active_slot"] = default_slot - dat += "

    Spines

    " + for (var/datum/preference_middleware/preference_middleware as anything in middleware) + data += preference_middleware.get_ui_data(user) - dat += "[active_character.features["spines"]]
    " + return data - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 +/datum/preferences/ui_static_data(mob/user) + var/list/data = list() - if("body_markings" in active_character.pref_species.default_features) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN + data["character_profiles"] = create_character_profiles() - dat += "

    Body Markings

    " + data["character_preview_view"] = character_preview_view.assigned_map + data["overflow_role"] = SSjob.GetJobType(SSjob.overflow_role).title + data["window"] = current_window - dat += "[active_character.features["body_markings"]]
    " + data["content_unlocked"] = unlock_content - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 + for (var/datum/preference_middleware/preference_middleware as anything in middleware) + data += preference_middleware.get_ui_static_data(user) - if("legs" in active_character.pref_species.default_features) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN + return data - dat += "

    Legs

    " +/datum/preferences/ui_assets(mob/user) + var/list/assets = list( + get_asset_datum(/datum/asset/spritesheet/preferences), + get_asset_datum(/datum/asset/json/preferences), + ) - dat += "[active_character.features["legs"]]
    " + for (var/datum/preference_middleware/preference_middleware as anything in middleware) + assets += preference_middleware.get_ui_assets() - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 + return assets - if("moth_wings" in active_character.pref_species.default_features) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN +/datum/preferences/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if (.) + return - dat += "

    Moth wings

    " + switch (action) + if ("change_slot") + // Save existing character + save_character() - dat += "[active_character.features["moth_wings"]]
    " + // SAFETY: `load_character` performs sanitization the slot number + if (!load_character(params["slot"])) + tainted_character_profiles = TRUE + randomise_appearance_prefs() + save_character() - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 + for (var/datum/preference_middleware/preference_middleware as anything in middleware) + preference_middleware.on_new_character(usr) - if("moth_antennae" in active_character.pref_species.default_features) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN + character_preview_view.update_body() - dat += "

    Moth antennae

    " + return TRUE + if ("rotate") + character_preview_view.dir = turn(character_preview_view.dir, -90) - dat += "[active_character.features["moth_antennae"]]
    " + return TRUE + if ("set_preference") + var/requested_preference_key = params["preference"] + var/value = params["value"] - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 + for (var/datum/preference_middleware/preference_middleware as anything in middleware) + if (preference_middleware.pre_set_preference(usr, requested_preference_key, value)) + return TRUE - if("moth_markings" in active_character.pref_species.default_features) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN + var/datum/preference/requested_preference = GLOB.preference_entries_by_key[requested_preference_key] + if (isnull(requested_preference)) + return FALSE - dat += "

    Moth markings

    " + // SAFETY: `update_preference` performs validation checks + if (!update_preference(requested_preference, value)) + return FALSE - dat += "[active_character.features["moth_markings"]]
    " + if (istype(requested_preference, /datum/preference/name)) + tainted_character_profiles = TRUE - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 + return TRUE + if ("set_color_preference") + var/requested_preference_key = params["preference"] - if("ipc_screen" in active_character.pref_species.mutant_bodyparts) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN + var/datum/preference/requested_preference = GLOB.preference_entries_by_key[requested_preference_key] + if (isnull(requested_preference)) + return FALSE - dat += "

    Screen Style

    " + if (!istype(requested_preference, /datum/preference/color) \ + && !istype(requested_preference, /datum/preference/color_legacy) \ + ) + return FALSE - dat += "[active_character.features["ipc_screen"]]
    " + var/default_value = read_preference(requested_preference.type) + if (istype(requested_preference, /datum/preference/color_legacy)) + default_value = expand_three_digit_color(default_value) - dat += "   Change
    " + // Yielding + var/new_color = input( + usr, + "Select new color", + null, + default_value || COLOR_WHITE, + ) as color | null - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 + if (!new_color) + return FALSE - if("ipc_antenna" in active_character.pref_species.mutant_bodyparts) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN + if (!update_preference(requested_preference, new_color)) + return FALSE - dat += "

    Antenna Style

    " + return TRUE - dat += "[active_character.features["ipc_antenna"]]
    " + for (var/datum/preference_middleware/preference_middleware as anything in middleware) + var/delegation = preference_middleware.action_delegations[action] + if (!isnull(delegation)) + return call(preference_middleware, delegation)(params, usr) - dat += "   Change
    " + return FALSE - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 +/datum/preferences/ui_close(mob/user) + save_character() + save_preferences() + QDEL_NULL(character_preview_view) - if("ipc_chassis" in active_character.pref_species.mutant_bodyparts) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN +/datum/preferences/Topic(href, list/href_list) + . = ..() + if (.) + return - dat += "

    Chassis Style

    " + if (href_list["open_keybindings"]) + current_window = PREFERENCE_TAB_KEYBINDINGS + update_static_data(usr) + ui_interact(usr) + return TRUE - dat += "[active_character.features["ipc_chassis"]]
    " +/datum/preferences/proc/create_character_preview_view(mob/user) + character_preview_view = new(null, src, user.client) + character_preview_view.update_body() + character_preview_view.register_to_client(user.client) - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 + return character_preview_view - if("tail_human" in active_character.pref_species.default_features) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN +/datum/preferences/proc/compile_character_preferences(mob/user) + var/list/preferences = list() - dat += "

    Tail

    " + for (var/datum/preference/preference as anything in get_preferences_in_priority_order()) + if (!preference.is_accessible(src)) + continue - dat += "[active_character.features["tail_human"]]
    " + LAZYINITLIST(preferences[preference.category]) - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 + var/value = read_preference(preference.type) + var/data = preference.compile_ui_data(user, value) - if("insect_type" in active_character.pref_species.mutant_bodyparts) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN + preferences[preference.category][preference.savefile_key] = data - dat += "

    Insect Type

    " + 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)) + continue - dat += "[active_character.features["insect_type"]]
    " + for (var/category in append_character_preferences) + if (category in preferences) + preferences[category] += append_character_preferences[category] + else + preferences[category] = append_character_preferences[category] - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 + return preferences - if("apid_antenna" in active_character.pref_species.mutant_bodyparts) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN +// This is necessary because you can open the set preferences menu before +// the atoms SS is done loading. +INITIALIZE_IMMEDIATE(/atom/movable/screen/character_preview_view) - dat += "

    Antenna Style

    " +/// A preview of a character for use in the preferences menu +/atom/movable/screen/character_preview_view + name = "character_preview" + del_on_map_removal = FALSE + layer = GAME_PLANE + plane = GAME_PLANE - dat += "[active_character.features["apid_antenna"]]
    " + /// The body that is displayed + var/mob/living/carbon/human/dummy/body - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 + /// The preferences this refers to + var/datum/preferences/preferences - if("apid_stripes" in active_character.pref_species.mutant_bodyparts) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN + var/list/plane_masters = list() - dat += "

    Stripe Pattern

    " + /// The client that is watching this view + var/client/client - dat += "[active_character.features["apid_stripes"]]
    " +/atom/movable/screen/character_preview_view/Initialize(mapload, datum/preferences/preferences, client/client) + . = ..() - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 + assigned_map = "character_preview_[REF(src)]" + set_position(1, 1) - if("apid_headstripes" in active_character.pref_species.mutant_bodyparts) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN + src.preferences = preferences - dat += "

    Headstripe Pattern

    " +/atom/movable/screen/character_preview_view/Destroy() + QDEL_NULL(body) - dat += "[active_character.features["apid_headstripes"]]
    " + for (var/plane_master in plane_masters) + client?.screen -= plane_master + qdel(plane_master) - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 + client?.clear_map(assigned_map) + client?.screen -= src - if("ears" in active_character.pref_species.default_features) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN + preferences?.character_preview_view = null - dat += "

    Ears

    " + client = null + plane_masters = null + preferences = null - dat += "[active_character.features["ears"]]
    " + return ..() - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 +/// Updates the currently displayed body +/atom/movable/screen/character_preview_view/proc/update_body() + if (isnull(body)) + create_body() + else + body.wipe_state() + appearance = preferences.render_new_preview_appearance(body) - if("body_size" in active_character.pref_species.default_features) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN +/atom/movable/screen/character_preview_view/proc/create_body() + QDEL_NULL(body) - dat += "

    Size

    " + body = new - dat += "[active_character.features["body_size"]]
    " + // Without this, it doesn't show up in the menu + body.appearance_flags &= ~KEEP_TOGETHER - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 +/// Registers the relevant map objects to a client +/atom/movable/screen/character_preview_view/proc/register_to_client(client/client) + QDEL_LIST(plane_masters) - if(CONFIG_GET(flag/join_with_mutant_humans)) + src.client = client - if("wings" in active_character.pref_species.default_features && GLOB.r_wings_list.len >1) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN + if (!client) + return - dat += "

    Wings

    " + for (var/plane_master_type in subtypesof(/atom/movable/screen/plane_master)) + var/atom/movable/screen/plane_master/plane_master = new plane_master_type + plane_master.screen_loc = "[assigned_map]:CENTER" + client?.screen |= plane_master - dat += "[active_character.features["wings"]]
    " + plane_masters += plane_master - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 + client?.register_map_obj(src) - if(mutant_category) - dat += "" - mutant_category = 0 - dat += "
    " - - dat += "Species:
    [active_character.pref_species.name]
    " - - dat += "Underwear:
    [active_character.underwear]
    " - dat += "Underwear Color:
        Change
    " - dat += "Undershirt:
    [active_character.undershirt]
    " - dat += "Socks:
    [active_character.socks]
    " - dat += "Backpack:
    [active_character.backbag]
    " - dat += "Jumpsuit:
    [active_character.jumpsuit_style]
    " - dat += "Uplink Spawn Location:
    [active_character.uplink_spawn_loc == UPLINK_IMPLANT ? UPLINK_IMPLANT_WITH_PRICE : active_character.uplink_spawn_loc]
    " +/datum/preferences/proc/create_character_profiles() + var/list/profiles = list() + var/savefile/savefile = new(path) + for (var/index in 1 to max_save_slots) + // It won't be updated in the savefile yet, so just read the name directly + if (index == default_slot) + profiles += read_preference(/datum/preference/name/real_name) + continue - if (1) // Game Preferences - dat += "" - // left box - dat += "" // left box closed - - // right box - dat += "" - // right box closed - - dat += "" - dat += "" - dat += "
    " - dat += "

    General Settings

    " - dat += "UI Style: [UI_style]
    " - dat += "Outline: [toggles & PREFTOGGLE_OUTLINE_ENABLED ? "Enabled" : "Disabled"]
    " - dat += "Outline Color:     Change
    " - dat += "Show Runechat Chat Bubbles: [toggles & PREFTOGGLE_RUNECHAT_GLOBAL ? "Enabled" : "Disabled"]
    " - dat += "See Runechat for non-mobs: [toggles & PREFTOGGLE_RUNECHAT_NONMOBS ? "Enabled" : "Disabled"]
    " - dat += "See Runechat emotes: [toggles & PREFTOGGLE_RUNECHAT_EMOTES ? "Enabled" : "Disabled"]
    " - dat += "See Balloon alerts: [see_balloon_alerts]" - dat += "
    " - dat += "Action Buttons: [(toggles2 & PREFTOGGLE_2_LOCKED_BUTTONS) ? "Locked In Place" : "Unlocked"]
    " - dat += "Hotkey Mode: [(toggles2 & PREFTOGGLE_2_HOTKEYS) ? "Hotkeys" : "Default"]
    " - dat += "
    " - dat += "PDA Theme: [theme_name_for_id(pda_theme)]
    " - dat += "PDA Classic Color:     Change
    " - dat += "
    " - dat += "Crew Objectives: [(toggles2 & PREFTOGGLE_2_CREW_OBJECTIVES) ? "Yes" : "No"]
    " - dat += "
    " - dat += "Ghost Ears: [(chat_toggles & CHAT_GHOSTEARS) ? "All Speech" : "Nearest Creatures"]
    " - dat += "Ghost Radio: [(chat_toggles & CHAT_GHOSTRADIO) ? "All Messages":"No Messages"]
    " - dat += "Ghost Sight: [(chat_toggles & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearest Creatures"]
    " - dat += "Ghost Whispers: [(chat_toggles & CHAT_GHOSTWHISPER) ? "All Speech" : "Nearest Creatures"]
    " - dat += "Ghost PDA: [(chat_toggles & CHAT_GHOSTPDA) ? "All Messages" : "Nearest Creatures"]
    " - dat += "Ghost Law Changes: [(chat_toggles & CHAT_GHOSTLAWS) ? "All Law Changes" : "No Law Changes"]
    " - dat += "Ghost (F) Chat toggle: [(chat_toggles & CHAT_GHOSTFOLLOWMINDLESS) ? "All mobs" : "Only mobs with mind"]
    " + savefile.cd = "/character[index]" - if(unlock_content) - dat += "Ghost Form: [ghost_form]
    " - dat += "Ghost Orbit: [ghost_orbit]
    " - - var/button_name = "If you see this something went wrong." - switch(ghost_accs) - if(GHOST_ACCS_FULL) - button_name = GHOST_ACCS_FULL_NAME - if(GHOST_ACCS_DIR) - button_name = GHOST_ACCS_DIR_NAME - if(GHOST_ACCS_NONE) - button_name = GHOST_ACCS_NONE_NAME - - dat += "Ghost Accessories: [button_name]
    " - - switch(ghost_others) - if(GHOST_OTHERS_THEIR_SETTING) - button_name = GHOST_OTHERS_THEIR_SETTING_NAME - if(GHOST_OTHERS_DEFAULT_SPRITE) - button_name = GHOST_OTHERS_DEFAULT_SPRITE_NAME - if(GHOST_OTHERS_SIMPLE) - button_name = GHOST_OTHERS_SIMPLE_NAME - - dat += "Ghosts of Others: [button_name]
    " - dat += "
    " - - dat += "Income Updates: [(chat_toggles & CHAT_BANKCARD) ? "Allowed" : "Muted"]
    " - dat += "
    " - dat += "

    TGUI Settings

    " - dat += "Monitor Lock: [(toggles2 & PREFTOGGLE_2_LOCKED_TGUI) ? "Primary" : "All"]
    " - dat += "Window Style: [(toggles2 & PREFTOGGLE_2_FANCY_TGUI) ? "Fancy (Borderless)" : "System Window"]
    " - dat += "
    " - dat += "

    TGUI Input

    " - dat += "Input Engine: [(toggles2 & PREFTOGGLE_2_TGUI_INPUT) ? "TGUI" : "Classic"]
    " - dat += "Button Size: [(toggles2 & PREFTOGGLE_2_BIG_BUTTONS) ? "Large" : "Small"]
    " - dat += "Button Location: [(toggles2 & PREFTOGGLE_2_SWITCHED_BUTTONS) ? "OK - Cancel" : "Cancel - OK"]
    " - dat += "
    " - dat += "

    TGUI Say

    " - dat += "Say Engine: [(toggles2 & PREFTOGGLE_2_TGUI_SAY) ? "TGUI" : "Classic"]
    " - dat += "Say Theme: [(toggles2 & PREFTOGGLE_2_SAY_LIGHT_THEME) ? "Light" : "Dark"]
    " - dat += "Radio Prefixes: [(toggles2 & PREFTOGGLE_2_SAY_SHOW_PREFIX) ? "Show" : "Hidden"]
    " - - dat += "

    Graphics Settings

    " - dat += "FPS: [clientfps]
    " - - dat += "Parallax (Fancy Space): " - switch (parallax) - if (PARALLAX_LOW) - dat += "Low" - if (PARALLAX_MED) - dat += "Medium" - if (PARALLAX_INSANE) - dat += "Insane" - if (PARALLAX_DISABLE) - dat += "Disabled" - else - dat += "High" - dat += "
    " - - dat += "Ambient Occlusion: [toggles2 & PREFTOGGLE_2_AMBIENT_OCCLUSION ? "Enabled" : "Disabled"]
    " - dat += "Fit Viewport: [toggles2 & PREFTOGGLE_2_AUTO_FIT_VIEWPORT ? "Auto" : "Manual"]
    " - - button_name = pixel_size - dat += "Pixel Scaling: [(button_name) ? "Pixel Perfect [button_name]x" : "Stretch to fit"]
    " - - switch(scaling_method) - if(SCALING_METHOD_NORMAL) - button_name = "Nearest Neighbor" - if(SCALING_METHOD_DISTORT) - button_name = "Point Sampling" - if(SCALING_METHOD_BLUR) - button_name = "Bilinear" - dat += "Scaling Method: [button_name]
    " - - if (CONFIG_GET(flag/maprotation)) - var/p_map = preferred_map - if (!p_map) - p_map = "Default" - if (config.defaultmap) - p_map += " ([config.defaultmap.map_name])" - else - if (p_map in config.maplist) - var/datum/map_config/VM = config.maplist[p_map] - if (!VM) - p_map += " (No longer exists)" - else - p_map = VM.map_name - else - p_map += " (No longer exists)" - if(CONFIG_GET(flag/preference_map_voting)) - dat += "Preferred Map: [p_map]
    " - - dat += "
    Customize Keybinds
    " - - if(4) // antagonist preferences window - dat += "" - // - dat += "" - // left box closed - - // - // -------------------------------------------- - // Midround antagonists + ghostspawn roles - dat += "" - // right box closed - - dat += "
    " - // -------------------------------------------- - // warning pannel - var/banned = is_banned_from(user.ckey, BAN_ROLE_ALL_ANTAGONISTS) - if(banned) - dat += "

    Notification

    " - dat += "You are banned from all antagonist type roles.
    " - - // -------------------------------------------- - // Antagonist roles - dat += "

    Antagonists

    " - for (var/typepath in GLOB.role_preference_entries) - var/datum/role_preference/pref = GLOB.role_preference_entries[typepath] - if(pref.category != ROLE_PREFERENCE_CATEGORY_ANAGONIST) - continue - dat += "[pref.name]: [role_preference_enabled(parent, typepath) ? "Enabled" : "Disabled"]
    " - dat += "

    Midrounds (Living)

    " - for (var/typepath in GLOB.role_preference_entries) - var/datum/role_preference/pref = GLOB.role_preference_entries[typepath] - if(pref.category != ROLE_PREFERENCE_CATEGORY_MIDROUND_LIVING) - continue - dat += "[pref.name]: [role_preference_enabled(parent, typepath) ? "Enabled" : "Disabled"]
    " - dat += "
    " - dat += "

    Midrounds (Ghost)

    " - for (var/typepath in GLOB.role_preference_entries) - var/datum/role_preference/pref = GLOB.role_preference_entries[typepath] - if(pref.category != ROLE_PREFERENCE_CATEGORY_MIDROUND_GHOST) - continue - dat += "[pref.name]: [role_preference_enabled(parent, typepath) ? "Enabled" : "Disabled"]
    " - /*dat += "

    Ghost Polls

    " - for (var/typepath in GLOB.role_preference_entries) - var/datum/role_preference/pref = GLOB.role_preference_entries[typepath] - if(pref.category != ROLE_PREFERENCE_CATEGORY_GHOST_ROLES) - continue - dat += "[pref.name]: [role_preference_enabled(parent, typepath) ? "Enabled" : "Disabled"]
    "*/ - dat += "
    " - - if(2) //Loadout - var/list/type_blacklist = list() - if(length(active_character.equipped_gear)) - for(var/i in 1 to length(active_character.equipped_gear)) - var/datum/gear/G = GLOB.gear_datums[active_character.equipped_gear[i]] - if(G) - if(G.subtype_path in type_blacklist) - continue - type_blacklist += G.subtype_path - else - active_character.equipped_gear.Cut(i,i+1) - - dat += "
    " - var/name - var/unspaced_slots = 0 - for(var/datum/character_save/CS as anything in character_saves) - unspaced_slots++ - if(unspaced_slots > 4) - dat += "
    " - unspaced_slots = 0 - name = CS.real_name - if(!name) - name = "Character [CS.slot_number]" - if(CS.slot_locked) - dat += "[name] (Locked) " - else - dat += "[name] " - dat += "
    " - - var/fcolor = "#3366CC" - var/metabalance = user.client.get_metabalance_db() - dat += "" - dat += "" - dat += "" - - var/datum/loadout_category/LC = GLOB.loadout_categories[gear_tab] - dat += "" - dat += "" - dat += "" - - dat += "" - dat += "" - if(LC.category != "Donator") - dat += "" - dat += "" - dat += "" - dat += "" - for(var/gear_id in LC.gear) - var/datum/gear/G = LC.gear[gear_id] - var/ticked = (G.id in active_character.equipped_gear) - - if(active_character.jumpsuit_style == PREF_SKIRT && !isnull(G.skirt_display_name)) - dat += "" - else - dat += "Equip" - else - dat += "[donator ? "Donator" : "Purchase"]" - dat += "" - else - dat += "" - dat += "
    Current balance: [metabalance] [CONFIG_GET(string/metacurrency_name)]s. \[Clear Loadout\]
    " - - var/firstcat = 1 - for(var/category in GLOB.loadout_categories) - if(category == "Donator" && (!LAZYLEN(GLOB.patrons) || !CONFIG_GET(flag/donator_items))) - continue - if(firstcat) - firstcat = 0 - else - dat += " |" - if(category == gear_tab) - dat += " [category] " - else - dat += " [category] " - dat += "

    [LC.category]


    NameCostRestricted JobsDescription

    [G.skirt_display_name]\n" - else - dat += "
    [G.display_name]\n" - var/donator = G.sort_category == "Donator" // purchase box and cost coloumns doesn't appear on donator items - if(G.id in purchased_gear) - if(G.sort_category == "OOC") - dat += "Purchased.[donator ? "" : "[G.cost]"]" - - if(G.allowed_roles) - dat += "" - for(var/role in G.allowed_roles) - dat += role + ", " - dat += "" - if(active_character.jumpsuit_style == PREF_SKIRT && !isnull(G.skirt_path)) - dat += "[G.skirt_description]
    [G.description]
    " - - if(3) //OOC Preferences - dat += "" - - if(user.client.holder) - dat +="" - dat += "
    " - dat += "

    OOC Settings

    " - dat += "Window Flashing: [(toggles2 & PREFTOGGLE_2_WINDOW_FLASHING) ? "Enabled":"Disabled"]
    " - dat += "
    " - dat += "Play Admin MIDIs: [(toggles & PREFTOGGLE_SOUND_MIDI) ? "Enabled":"Disabled"]
    " - dat += "Play Lobby Music: [(toggles & PREFTOGGLE_SOUND_LOBBY) ? "Enabled":"Disabled"]
    " - dat += "Play Game Soundtrack: [(toggles2 & PREFTOGGLE_2_SOUNDTRACK) ? "Enabled":"Disabled"]
    " - dat += "See Pull Requests: [(chat_toggles & CHAT_PULLR) ? "Enabled":"Disabled"]
    " - dat += "
    " - - - if(user.client) - if(unlock_content) - dat += "BYOND Membership Publicity: [(toggles & PREFTOGGLE_MEMBER_PUBLIC) ? "Public" : "Hidden"]
    " - - if(unlock_content || check_rights_for(user.client, R_ADMIN)) - dat += "OOC Color:     Change
    " - - dat += "
    " - - dat += "

    Admin Settings

    " - - dat += "Adminhelp Sounds: [(toggles & PREFTOGGLE_SOUND_ADMINHELP)?"Enabled":"Disabled"]
    " - dat += "Admin Alert Sounds: [(toggles & PREFTOGGLE_2_SOUND_ADMINALERT)?"Enabled":"Disabled"]
    " - dat += "Prayer Sounds: [(toggles & PREFTOGGLE_SOUND_PRAYERS)?"Enabled":"Disabled"]
    " - dat += "Announce Login: [(toggles & PREFTOGGLE_ANNOUNCE_LOGIN)?"Enabled":"Disabled"]
    " - dat += "
    " - dat += "Combo HUD Lighting: [(toggles & PREFTOGGLE_COMBOHUD_LIGHTING)?"Full-bright":"No Change"]
    " - dat += "
    " - dat += "Hide Dead Chat: [(chat_toggles & CHAT_DEAD)?"Shown":"Hidden"]
    " - dat += "Hide Radio Messages: [(chat_toggles & CHAT_RADIO)?"Shown":"Hidden"]
    " - dat += "Hide Prayers: [(chat_toggles & CHAT_PRAYER)?"Shown":"Hidden"]
    " - if(CONFIG_GET(flag/allow_admin_asaycolor)) - dat += "
    " - dat += "ASAY Color:     Change
    " - - //deadmin - dat += "

    Deadmin While Playing

    " - if(CONFIG_GET(flag/auto_deadmin_players)) - dat += "Always Deadmin: FORCED
    " - else - dat += "Always Deadmin: [(toggles & PREFTOGGLE_DEADMIN_ALWAYS)?"Enabled":"Disabled"]
    " - if(!(toggles & PREFTOGGLE_DEADMIN_ALWAYS)) - dat += "
    " - if(!CONFIG_GET(flag/auto_deadmin_antagonists)) - dat += "As Antag: [(toggles & PREFTOGGLE_DEADMIN_ANTAGONIST)?"Deadmin":"Keep Admin"]
    " - else - dat += "As Antag: FORCED
    " - - if(!CONFIG_GET(flag/auto_deadmin_heads)) - dat += "As Command: [(toggles & PREFTOGGLE_DEADMIN_POSITION_HEAD)?"Deadmin":"Keep Admin"]
    " - else - dat += "As Command: FORCED
    " - - if(!CONFIG_GET(flag/auto_deadmin_security)) - dat += "As Security: [(toggles & PREFTOGGLE_DEADMIN_POSITION_SECURITY)?"Deadmin":"Keep Admin"]
    " - else - dat += "As Security: FORCED
    " - - if(!CONFIG_GET(flag/auto_deadmin_silicons)) - dat += "As Silicon: [(toggles & PREFTOGGLE_DEADMIN_POSITION_SILICON)?"Deadmin":"Keep Admin"]
    " - else - dat += "As Silicon: FORCED
    " - - dat += "
    " - - dat += "
    " - - if(!IS_GUEST_KEY(user.key)) - dat += "Undo " - dat += "Save Setup " - - dat += "Reset Setup" - dat += "
    " - - winshow(user, "preferences_window", TRUE) - var/datum/browser/popup = new(user, "preferences_browser", "
    Character Setup
    ", 640, 830) - popup.set_content(dat.Join()) - popup.open(FALSE) - onclose(user, "preferences_window", src) - -#undef APPEARANCE_CATEGORY_COLUMN -#undef MAX_MUTANT_ROWS - -/datum/preferences/proc/SetChoices(mob/user, limit = 16, list/splitJobs = list(JOB_NAME_CLOWN, JOB_NAME_RESEARCHDIRECTOR), widthPerColumn = 295, height = 620) - if(!SSjob) - return + var/name + READ_FILE(savefile["real_name"], name) - //limit - The amount of jobs allowed per column. Defaults to 17 to make it look nice. - //splitJobs - Allows you split the table by job. You can make different tables for each department by including their heads. Defaults to CE to make it look nice. - //widthPerColumn - Screen's width for every column. - //height - Screen's height. + if (isnull(name)) + profiles += null + continue - var/width = widthPerColumn + profiles += name - var/HTML = "
    " - if(SSjob.occupations.len <= 0) - HTML += "The job SSticker is not yet finished creating jobs, please try again later" - HTML += "
    Done

    " // Easier to press up here. + return profiles - else - HTML += "Choose occupation chances
    " - HTML += "
    Left-click to raise an occupation preference, right-click to lower it.
    " - HTML += "
    Done

    " // Easier to press up here. - HTML += "" - HTML += "
    " // Table within a table for alignment, also allows you to easily add more colomns. - HTML += "" - var/index = -1 - - //The job before the current job. I only use this to get the previous jobs color when I'm filling in blank rows. - var/datum/job/lastJob - - var/datum/job/overflow = SSjob.GetJob(SSjob.overflow_role) - - for(var/datum/job/job in sort_list(SSjob.occupations, GLOBAL_PROC_REF(cmp_job_display_asc))) - if(job.gimmick) //Gimmick jobs run off of a single pref - continue - index += 1 - if((index >= limit) || (job.title in splitJobs)) - width += widthPerColumn - if((index < limit) && (lastJob != null)) - //If the cells were broken up by a job in the splitJob list then it will fill in the rest of the cells with - //the last job's selection color. Creating a rather nice effect. - for(var/i = 0, i < (limit - index), i += 1) - HTML += "" - HTML += "
      
    " - index = 0 - - HTML += "" - continue - var/required_playtime_remaining = job.required_playtime_remaining(user.client) - if(required_playtime_remaining) - HTML += "[rank]" - continue - if(!job.player_old_enough(user.client)) - var/available_in_days = job.available_in_days(user.client) - HTML += "[rank]" - continue - if((active_character.job_preferences[overflow] == JP_LOW) && (rank != SSjob.overflow_role) && !is_banned_from(user.ckey, SSjob.overflow_role)) - HTML += "[rank]" - continue - if((rank in GLOB.command_positions) || (rank == JOB_NAME_AI))//Bold head jobs - HTML += "[rank]" - else - HTML += "[rank]" - - HTML += "" - continue - - HTML += "[prefLevelLabel]" - HTML += "" - - for(var/i = 1, i < (limit - index), i += 1) // Finish the column so it is even - HTML += "" - - HTML += "
    " - var/rank = job.title - lastJob = job - if(is_banned_from(user.ckey, rank)) - HTML += "[rank] BANNED
    \[ [get_exp_format(required_playtime_remaining)] as [job.get_exp_req_type()] \]
    \[IN [(available_in_days)] DAYS\]
    " - - var/prefLevelLabel = "ERROR" - var/prefLevelColor = "pink" - var/prefUpperLevel = -1 // level to assign on left click - var/prefLowerLevel = -1 // level to assign on right click - - switch(active_character.job_preferences[job.title]) - if(JP_HIGH) - prefLevelLabel = "High" - prefLevelColor = "slateblue" - prefUpperLevel = 4 - prefLowerLevel = 2 - if(JP_MEDIUM) - prefLevelLabel = "Medium" - prefLevelColor = "green" - prefUpperLevel = 1 - prefLowerLevel = 3 - if(JP_LOW) - prefLevelLabel = "Low" - prefLevelColor = "orange" - prefUpperLevel = 2 - prefLowerLevel = 4 - else - prefLevelLabel = "NEVER" - prefLevelColor = "red" - prefUpperLevel = 3 - prefLowerLevel = 1 - - HTML += "" - - if(rank == SSjob.overflow_role)//Overflow is special - if(active_character.job_preferences[overflow.title] == JP_LOW) - HTML += "Yes" - else - HTML += "No" - HTML += "
      
    " - HTML += "
    " - - var/message = "Be an [SSjob.overflow_role] if preferences unavailable" - if(active_character.joblessrole == BERANDOMJOB) - message = "Get random job if preferences unavailable" - else if(active_character.joblessrole == RETURNTOLOBBY) - message = "Return to lobby if preferences unavailable" - HTML += "

    [message]
    " - HTML += "
    Reset Preferences
    " - - var/datum/browser/popup = new(user, "mob_occupation", "
    Occupation Preferences
    ", width, height) - popup.set_window_options("can_close=0") - popup.set_content(HTML) - popup.open(FALSE) - - -/datum/preferences/proc/ShowKeybindings(mob/user) - // Create an inverted list of keybindings -> key - var/list/user_binds = list() - for(var/key in key_bindings) - for(var/kb_name in key_bindings[key]) - user_binds[kb_name] = key - - var/list/kb_categories = list() - // Group keybinds by category - for (var/name in GLOB.keybindings_by_name) - var/datum/keybinding/kb = GLOB.keybindings_by_name[name] - if (!(kb.category in kb_categories)) - kb_categories[kb.category] = list() - kb_categories[kb.category] += list(kb) - - var/HTML = "" - - for (var/category in kb_categories) - HTML += "

    [category]

    " - for (var/i in kb_categories[category]) - var/datum/keybinding/kb = i - var/bound_key = user_binds[kb.name] - bound_key = (bound_key) ? bound_key : "Unbound" - - HTML += " [bound_key] Default: ( [kb.key] )" - HTML += "
    " - - HTML += "

    " - HTML += "Close" - HTML += "Reset to default" - HTML += "" - - winshow(user, "keybindings", TRUE) - var/datum/browser/popup = new(user, "keybindings", "
    Keybindings
    ", 500, 900) - popup.set_content(HTML) - popup.open(FALSE) - onclose(user, "keybindings", src) - - -/datum/preferences/proc/CaptureKeybinding(mob/user, datum/keybinding/kb, var/old_key) - var/HTML = {" -
    Keybinding: [kb.full_name]
    [kb.description]

    Press any key to change
    Press ESC to clear
    - - "} - winshow(user, "capturekeypress", TRUE) - var/datum/browser/popup = new(user, "capturekeypress", "
    Keybindings
    ", 350, 300) - popup.set_content(HTML) - popup.open(FALSE) - onclose(user, "capturekeypress", src) - - -/datum/preferences/proc/SetJobPreferenceLevel(datum/job/job, level) +/datum/preferences/proc/set_job_preference_level(datum/job/job, level) if (!job) return FALSE - if (level == JP_HIGH) // to high - //Set all other high to medium - for(var/j in active_character.job_preferences) - if(active_character.job_preferences[j] == JP_HIGH) - active_character.job_preferences[j] = JP_MEDIUM - //technically break here - - active_character.job_preferences[job.title] = level - return TRUE - - - - -/datum/preferences/proc/UpdateJobPreference(mob/user, role, desiredLvl) - if(!SSjob || SSjob.occupations.len <= 0) - return - var/datum/job/job = SSjob.GetJob(role) - - if(!job) - user << browse(null, "window=mob_occupation") - ShowChoices(user) - return - - if (!isnum_safe(desiredLvl)) - to_chat(user, "UpdateJobPreference - desired level was not a number. Please notify coders!") - ShowChoices(user) - return - - var/jpval = null - switch(desiredLvl) - if(3) - jpval = JP_LOW - if(2) - jpval = JP_MEDIUM - if(1) - jpval = JP_HIGH - - if(role == SSjob.overflow_role) - if(active_character.job_preferences[job.title] == JP_LOW) - jpval = null - else - jpval = JP_LOW - - SetJobPreferenceLevel(job, jpval) - SetChoices(user) + if (level == JP_HIGH) + for(var/other_job in job_preferences) + if(job_preferences[other_job] == JP_HIGH) + job_preferences[other_job] = JP_MEDIUM - return 1 - - -/datum/preferences/proc/ResetJobs() - active_character.job_preferences = list() - -/datum/preferences/proc/SetQuirks(mob/user) - if(!SSquirks) - to_chat(user, "The quirk subsystem is still initializing! Try again in a minute.") - return + job_preferences[job.title] = level - var/list/dat = list() - if(!SSquirks.quirks.len) - dat += "The quirk subsystem hasn't finished initializing, please hold..." - dat += "
    Done

    " - else - dat += "
    Choose quirk setup

    " - dat += "
    Left-click to add or remove quirks. You need negative quirks to have positive ones.
    \ - Quirks are applied at roundstart and cannot normally be removed.
    " - dat += "
    Done
    " - dat += "
    " - dat += "
    Current quirks: [length(active_character.all_quirks) ? active_character.all_quirks.Join(", ") : "None"]
    " - dat += "
    [GetPositiveQuirkCount()] / [MAX_QUIRKS] max positive quirks
    \ - Quirk balance remaining: [GetQuirkBalance()]

    " - for(var/V in SSquirks.quirks) - var/datum/quirk/T = SSquirks.quirks[V] - var/quirk_name = initial(T.name) - var/has_quirk - var/quirk_cost = initial(T.value) * -1 - var/lock_reason = "This trait is unavailable." - var/quirk_conflict = FALSE - for(var/_V in active_character.all_quirks) - if(_V == quirk_name) - has_quirk = TRUE - if(initial(T.mood_quirk) && CONFIG_GET(flag/disable_human_mood)) - lock_reason = "Mood is disabled." - quirk_conflict = TRUE - if(has_quirk) - if(quirk_conflict) - active_character.all_quirks -= quirk_name - has_quirk = FALSE - else - quirk_cost *= -1 //invert it back, since we'd be regaining this amount - if(quirk_cost > 0) - quirk_cost = "+[quirk_cost]" - var/font_color = "#AAAAFF" - if(initial(T.value) != 0) - font_color = initial(T.value) > 0 ? "#AAFFAA" : "#FFAAAA" - if(quirk_conflict) - dat += "[quirk_name] - [initial(T.desc)] \ - LOCKED: [lock_reason]
    " - else - if(has_quirk) - dat += "[has_quirk ? "Remove" : "Take"] ([quirk_cost] pts.) \ - [quirk_name] - [initial(T.desc)]
    " - else - dat += "[has_quirk ? "Remove" : "Take"] ([quirk_cost] pts.) \ - [quirk_name] - [initial(T.desc)]
    " - dat += "
    Reset Quirks
    " - - var/datum/browser/popup = new(user, "mob_occupation", "
    Quirk Preferences
    ", 900, 600) //no reason not to reuse the occupation window, as it's cleaner that way - popup.set_window_options("can_close=0") - popup.set_content(dat.Join()) - popup.open(FALSE) + return TRUE /datum/preferences/proc/GetQuirkBalance() var/bal = 0 @@ -1269,906 +474,61 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(SSquirks.quirk_points[q] > 0) .++ -/datum/preferences/Topic(href, href_list, hsrc) //yeah, gotta do this I guess.. - . = ..() - if(href_list["close"]) - var/client/C = usr.client - if(C) - C.clear_character_previews() - -/datum/preferences/proc/process_link(mob/user, list/href_list) - if(href_list["bancheck"]) - var/list/ban_details = is_banned_from_with_details(user.ckey, user.client.address, user.client.computer_id, href_list["bancheck"]) - var/admin = FALSE - if(GLOB.admin_datums[user.ckey] || GLOB.deadmins[user.ckey]) - admin = TRUE - for(var/i in ban_details) - if(admin && !text2num(i["applies_to_admins"])) - continue - ban_details = i - break //we only want to get the most recent ban's details - if(ban_details && ban_details.len) - var/expires = "This is a permanent ban." - if(ban_details["expiration_time"]) - expires = " The ban is for [DisplayTimeText(text2num(ban_details["duration"]) MINUTES)] and expires on [ban_details["expiration_time"]] (server time)." - to_chat(user, "You, or another user of this computer or connection ([ban_details["key"]]) is banned from playing [href_list["bancheck"]].
    The ban reason is: [ban_details["reason"]]
    This ban (BanID #[ban_details["id"]]) was applied by [ban_details["admin_key"]] on [ban_details["bantime"]] during round ID [ban_details["round_id"]].
    [expires]
    ") - return - if(href_list["preference"] == "job") - switch(href_list["task"]) - if("close") - user << browse(null, "window=mob_occupation") - ShowChoices(user) - if("reset") - ResetJobs() - SetChoices(user) - if("random") - switch(active_character.joblessrole) - if(RETURNTOLOBBY) - if(is_banned_from(user.ckey, SSjob.overflow_role)) - active_character.joblessrole = BERANDOMJOB - else - active_character.joblessrole = BEOVERFLOW - if(BEOVERFLOW) - active_character.joblessrole = BERANDOMJOB - if(BERANDOMJOB) - active_character.joblessrole = RETURNTOLOBBY - SetChoices(user) - if("setJobLevel") - UpdateJobPreference(user, href_list["text"], text2num(href_list["level"])) - else - SetChoices(user) - return 1 - - else if(href_list["preference"] == "trait") - switch(href_list["task"]) - if("close") - user << browse(null, "window=mob_occupation") - ShowChoices(user) - if("update") - var/quirk = href_list["trait"] - if(!SSquirks.quirks[quirk]) - return - for(var/V in SSquirks.quirk_blacklist) //V is a list - var/list/L = V - for(var/Q in active_character.all_quirks) - if((quirk in L) && (Q in L) && !(Q == quirk)) //two quirks have lined up in the list of the list of quirks that conflict with each other, so return (see quirks.dm for more details) - to_chat(user, "[quirk] is incompatible with [Q].") - return - var/value = SSquirks.quirk_points[quirk] - var/balance = GetQuirkBalance() - if(quirk in active_character.all_quirks) - if(balance + value < 0) - to_chat(user, "Refunding this would cause you to go below your balance!") - return - active_character.all_quirks -= quirk - else - var/is_positive_quirk = SSquirks.quirk_points[quirk] > 0 - if(is_positive_quirk && GetPositiveQuirkCount() >= MAX_QUIRKS) - to_chat(user, "You can't have more than [MAX_QUIRKS] positive quirks!") - return - if(balance - value < 0) - to_chat(user, "You don't have enough balance to gain this quirk!") - return - active_character.all_quirks += quirk - SetQuirks(user) - if("reset") - active_character.all_quirks = list() - SetQuirks(user) - else - SetQuirks(user) - return TRUE +/datum/preferences/proc/validate_quirks() + if(GetQuirkBalance() < 0) + all_quirks = list() - if(href_list["preference"] == "gear") - if(href_list["purchase_gear"]) - var/datum/gear/TG = GLOB.gear_datums[href_list["purchase_gear"]] - if(TG.sort_category == "Donator") - if(CONFIG_GET(flag/donator_items) && alert(parent, "This item is only accessible to our patrons. Would you like to subscribe?", "Patron Locked", "Yes", "No") == "Yes") - parent.donate() - else if(TG.cost <= user.client.get_metabalance_db()) - purchased_gear += TG.id - TG.purchase(user.client) - user.client.inc_metabalance((TG.cost * -1), TRUE, "Purchased [TG.display_name].") - save_preferences() - else - to_chat(user, "You don't have enough [CONFIG_GET(string/metacurrency_name)]s to purchase \the [TG.display_name]!") - if(href_list["toggle_gear"]) - var/datum/gear/TG = GLOB.gear_datums[href_list["toggle_gear"]] - if(TG.id in active_character.equipped_gear) - active_character.equipped_gear -= TG.id - else - var/list/type_blacklist = list() - var/list/slot_blacklist = list() - for(var/gear_id in active_character.equipped_gear) - var/datum/gear/G = GLOB.gear_datums[gear_id] - if(istype(G)) - if(!(G.subtype_path in type_blacklist)) - type_blacklist += G.subtype_path - if(!(G.slot in slot_blacklist)) - slot_blacklist += G.slot - if((TG.id in purchased_gear)) - if(!(TG.subtype_path in type_blacklist) || !(TG.slot in slot_blacklist)) - active_character.equipped_gear += TG.id - else - to_chat(user, "Can't equip [TG.display_name]. It conflicts with an already-equipped item.") - else - log_href_exploit(user) - active_character.save(user.client) - - else if(href_list["select_category"]) - gear_tab = href_list["select_category"] - else if(href_list["clear_loadout"]) - active_character.equipped_gear.Cut() - active_character.save(user.client) - - ShowChoices(user) - return +/// Sanitizes the preferences, applies the randomization prefs, and then applies the preference to the human mob. +/datum/preferences/proc/safe_transfer_prefs_to(mob/living/carbon/human/character, icon_updates = TRUE, is_antag = FALSE) + apply_character_randomization_prefs(is_antag) + apply_prefs_to(character, icon_updates) - switch(href_list["task"]) - if("random") - switch(href_list["preference"]) - if("name") - active_character.real_name = active_character.pref_species.random_name(active_character.gender, 1) - if("age") - active_character.age = rand(AGE_MIN, AGE_MAX) - if("hair_color") - active_character.hair_color = random_short_color() - if("hair_style") - active_character.hair_style = random_hair_style(active_character.gender) - if("facial") - active_character.facial_hair_color = random_short_color() - if("facial_hair_style") - active_character.facial_hair_style = random_facial_hair_style(active_character.gender) - if("underwear") - active_character.underwear = random_underwear(active_character.gender) - if("underwear_color") - active_character.underwear_color = random_short_color() - if("undershirt") - active_character.undershirt = random_undershirt(active_character.gender) - if("socks") - active_character.socks = random_socks() - if(BODY_ZONE_PRECISE_EYES) - active_character.eye_color = random_eye_color() - if("s_tone") - active_character.skin_tone = random_skin_tone() - if("bag") - active_character.backbag = pick(GLOB.backbaglist) - if("all") - active_character.randomise() - - if("input") - - if(href_list["preference"] in GLOB.preferences_custom_names) - ask_for_custom_name(user,href_list["preference"]) - - if(href_list["preference"] in active_character.pref_species.forced_features) - alert("You cannot change that bodypart for your selected species!") - active_character.features[href_list["preference"]] = active_character.pref_species.forced_features[href_list["preference"]] - return - - switch(href_list["preference"]) - if("ghostform") - if(unlock_content) - var/new_form = input(user, "Thanks for supporting BYOND - Choose your ghostly form:","Thanks for supporting BYOND",null) as null|anything in GLOB.ghost_forms - if(new_form) - ghost_form = new_form - if("ghostorbit") - if(unlock_content) - var/new_orbit = input(user, "Thanks for supporting BYOND - Choose your ghostly orbit:","Thanks for supporting BYOND", null) as null|anything in GLOB.ghost_orbits - if(new_orbit) - ghost_orbit = new_orbit - - if("ghostaccs") - var/new_ghost_accs = alert("Do you want your ghost to show full accessories where possible, hide accessories but still use the directional sprites where possible, or also ignore the directions and stick to the default sprites?",,GHOST_ACCS_FULL_NAME, GHOST_ACCS_DIR_NAME, GHOST_ACCS_NONE_NAME) - switch(new_ghost_accs) - if(GHOST_ACCS_FULL_NAME) - ghost_accs = GHOST_ACCS_FULL - if(GHOST_ACCS_DIR_NAME) - ghost_accs = GHOST_ACCS_DIR - if(GHOST_ACCS_NONE_NAME) - ghost_accs = GHOST_ACCS_NONE - - if("ghostothers") - var/new_ghost_others = alert("Do you want the ghosts of others to show up as their own setting, as their default sprites or always as the default white ghost?",,GHOST_OTHERS_THEIR_SETTING_NAME, GHOST_OTHERS_DEFAULT_SPRITE_NAME, GHOST_OTHERS_SIMPLE_NAME) - switch(new_ghost_others) - if(GHOST_OTHERS_THEIR_SETTING_NAME) - ghost_others = GHOST_OTHERS_THEIR_SETTING - if(GHOST_OTHERS_DEFAULT_SPRITE_NAME) - ghost_others = GHOST_OTHERS_DEFAULT_SPRITE - if(GHOST_OTHERS_SIMPLE_NAME) - ghost_others = GHOST_OTHERS_SIMPLE - - if("name") - var/new_name = reject_bad_name( input(user, "Choose your character's name:", "Character Preference") as text|null , active_character.pref_species.allow_numbers_in_name) - if(new_name) - active_character.real_name = new_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") - - if("age") - var/new_age = input(user, "Choose your character's age:\n([AGE_MIN]-[AGE_MAX])", "Character Preference") as num|null - if(new_age) - active_character.age = max(min( round(text2num(new_age)), AGE_MAX),AGE_MIN) - - if("hair_color") - var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference","#" + active_character.hair_color) as color|null - if(new_hair) - active_character.hair_color = sanitize_hexcolor(new_hair) - - if("hair_style") - var/new_hair_style = tgui_input_list(user, "Choose your character's hair style:", "Character Preference", GLOB.hair_styles_list, active_character.hair_style) - if(new_hair_style) - active_character.hair_style = new_hair_style - - if("gradient_style") - var/new_gradient_style - new_gradient_style = input(user, "Choose your character's hair gradient style:", "Character Preference") as null|anything in GLOB.hair_gradients_list - if(new_gradient_style) - active_character.gradient_style = new_gradient_style - - if("gradient_color") - var/new_hair_gradient = input(user, "Choose your character's hair gradient colour:", "Character Preference", "#" + active_character.gradient_color) as color|null - if(new_hair_gradient) - active_character.gradient_color = sanitize_hexcolor(new_hair_gradient) - - if("next_hair_style") - active_character.hair_style = next_list_item(active_character.hair_style, GLOB.hair_styles_list) - - if("previous_hair_style") - active_character.hair_style = previous_list_item(active_character.hair_style, GLOB.hair_styles_list) - - if("next_gradient_style") - active_character.gradient_style = next_list_item(active_character.gradient_style, GLOB.hair_gradients_list) - - if("previous_gradient_style") - active_character.gradient_style = previous_list_item(active_character.gradient_style, GLOB.hair_gradients_list) - - if("facial") - var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference","#" + active_character.facial_hair_color) as color|null - if(new_facial) - active_character.facial_hair_color = sanitize_hexcolor(new_facial) - - if("facial_hair_style") - var/new_facial_hair_style = tgui_input_list(user, "Choose your character's facial-hair style:", "Character Preference", GLOB.facial_hair_styles_list, active_character.facial_hair_style) - if(new_facial_hair_style) - active_character.facial_hair_style = new_facial_hair_style - - if("next_facehair_style") - active_character.facial_hair_style = next_list_item(active_character.facial_hair_style, GLOB.facial_hair_styles_list) - - if("previous_facehair_style") - active_character.facial_hair_style = previous_list_item(active_character.facial_hair_style, GLOB.facial_hair_styles_list) - - if("underwear") - var/new_underwear = tgui_input_list(user, "Choose your character's underwear:", "Character Preference", GLOB.underwear_list, active_character.underwear) - if(new_underwear) - active_character.underwear = new_underwear - - if("underwear_color") - var/new_underwear_color = input(user, "Choose your character's underwear color:", "Character Preference","#"+active_character.underwear_color) as color|null - if(new_underwear_color) - active_character.underwear_color = sanitize_hexcolor(new_underwear_color) - - if("undershirt") - var/new_undershirt = tgui_input_list(user, "Choose your character's undershirt:", "Character Preference", GLOB.undershirt_list, active_character.undershirt) - if(new_undershirt) - active_character.undershirt = new_undershirt - - if("socks") - var/new_socks - new_socks = tgui_input_list(user, "Choose your character's socks:", "Character Preference", GLOB.socks_list, active_character.socks) - if(new_socks) - active_character.socks = new_socks - - if("eyes") - var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference","#"+active_character.eye_color) as color|null - if(new_eyes) - active_character.eye_color = sanitize_hexcolor(new_eyes) - - if("body_size") - var/new_size = input(user, "Choose your character's height:", "Character Preference") as null|anything in GLOB.body_sizes - if(new_size) - active_character.features["body_size"] = new_size - - if("species") - - var/result = input(user, "Select a species", "Species Selection") as null|anything in GLOB.roundstart_races - - if(result) - var/new_species_type = GLOB.species_list[result] - var/datum/species/new_species = new new_species_type() - - if (!CONFIG_GET(keyed_list/paywall_races)[new_species.id] || IS_PATRON(parent.ckey) || parent.holder) - active_character.pref_species = new_species - //Now that we changed our species, we must verify that the mutant colour is still allowed. - var/temp_hsv = RGBtoHSV(active_character.features["mcolor"]) - if(active_character.features["mcolor"] == "#000" || (!(MUTCOLORS_PARTSONLY in active_character.pref_species.species_traits) && ReadHSV(temp_hsv)[3] < ReadHSV("#7F7F7F")[3])) - active_character.features["mcolor"] = active_character.pref_species.default_color - //Set our forced bodyparts - for(var/forced_part in active_character.pref_species.forced_features) - //Get the forced type - var/forced_type = active_character.pref_species.forced_features[forced_part] - //Apply the forced bodypart. - active_character.features[forced_part] = forced_type - else - if(alert(parent, "This species is only accessible to our patrons. Would you like to subscribe?", "Patron Locked", "Yes", "No") == "Yes") - parent.donate() - - - if("mutant_color") - var/new_mutantcolor = input(user, "Choose your character's alien/mutant color:", "Character Preference","#"+active_character.features["mcolor"]) as color|null - if(new_mutantcolor) - var/temp_hsv = RGBtoHSV(new_mutantcolor) - if(new_mutantcolor == "#000000") - active_character.features["mcolor"] = active_character.pref_species.default_color - else if((MUTCOLORS_PARTSONLY in active_character.pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#7F7F7F")[3]) // mutantcolors must be bright, but only if they affect the skin - active_character.features["mcolor"] = sanitize_hexcolor(new_mutantcolor) - else - to_chat(user, "Invalid color. Your color is not bright enough.") - - if("color_ethereal") - var/new_etherealcolor = input(user, "Choose your ethereal color", "Character Preference") as null|anything in GLOB.color_list_ethereal - if(new_etherealcolor) - active_character.features["ethcolor"] = GLOB.color_list_ethereal[new_etherealcolor] - - if("helmet_style") - var/style = input(user, "Choose your helmet style", "Character Preference") as null|anything in list(HELMET_DEFAULT, HELMET_MK2, HELMET_PROTECTIVE) - if(style) - active_character.helmet_style = style - - if("tail_lizard") - var/new_tail - new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.tails_list_lizard - if(new_tail) - active_character.features["tail_lizard"] = new_tail - - if("tail_human") - var/new_tail - new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.tails_list_human - if(new_tail) - active_character.features["tail_human"] = new_tail - - if("snout") - var/new_snout - new_snout = input(user, "Choose your character's snout:", "Character Preference") as null|anything in GLOB.snouts_list - if(new_snout) - active_character.features["snout"] = new_snout - - if("horns") - var/new_horns - new_horns = input(user, "Choose your character's horns:", "Character Preference") as null|anything in GLOB.horns_list - if(new_horns) - active_character.features["horns"] = new_horns - - if("ears") - var/new_ears - new_ears = input(user, "Choose your character's ears:", "Character Preference") as null|anything in GLOB.ears_list - if(new_ears) - active_character.features["ears"] = new_ears - - if("wings") - var/new_wings - new_wings = input(user, "Choose your character's wings:", "Character Preference") as null|anything in GLOB.r_wings_list - if(new_wings) - active_character.features["wings"] = new_wings - - if("frills") - var/new_frills - new_frills = input(user, "Choose your character's frills:", "Character Preference") as null|anything in GLOB.frills_list - if(new_frills) - active_character.features["frills"] = new_frills - - if("spines") - var/new_spines - new_spines = input(user, "Choose your character's spines:", "Character Preference") as null|anything in GLOB.spines_list - if(new_spines) - active_character.features["spines"] = new_spines - - if("body_markings") - var/new_body_markings - new_body_markings = input(user, "Choose your character's body markings:", "Character Preference") as null|anything in GLOB.body_markings_list - if(new_body_markings) - active_character.features["body_markings"] = new_body_markings - - if("legs") - var/new_legs - new_legs = input(user, "Choose your character's legs:", "Character Preference") as null|anything in GLOB.legs_list - if(new_legs) - active_character.features["legs"] = new_legs - - if("moth_wings") - var/new_moth_wings - new_moth_wings = input(user, "Choose your character's wings:", "Character Preference") as null|anything in GLOB.moth_wings_roundstart_list - if(new_moth_wings) - active_character.features["moth_wings"] = new_moth_wings - - if("moth_antennae") - var/new_moth_antennae - new_moth_antennae = input(user, "Choose your character's antennae:", "Character Preference") as null|anything in GLOB.moth_antennae_roundstart_list - if(new_moth_antennae) - active_character.features["moth_antennae"] = new_moth_antennae - - if("moth_markings") - var/new_moth_markings - new_moth_markings = input(user, "Choose your character's markings:", "Character Preference") as null|anything in GLOB.moth_markings_roundstart_list - if(new_moth_markings) - active_character.features["moth_markings"] = new_moth_markings - - if("ipc_screen") - var/new_ipc_screen - - new_ipc_screen = input(user, "Choose your character's screen:", "Character Preference") as null|anything in GLOB.ipc_screens_list - - if(new_ipc_screen) - active_character.features["ipc_screen"] = new_ipc_screen - - if("ipc_antenna") - var/new_ipc_antenna - - new_ipc_antenna = input(user, "Choose your character's antenna:", "Character Preference") as null|anything in GLOB.ipc_antennas_list - - if(new_ipc_antenna) - active_character.features["ipc_antenna"] = new_ipc_antenna - - if("ipc_chassis") - var/new_ipc_chassis - - new_ipc_chassis = input(user, "Choose your character's chassis:", "Character Preference") as null|anything in GLOB.ipc_chassis_list - - if(new_ipc_chassis) - active_character.features["ipc_chassis"] = new_ipc_chassis - - if("insect_type") - var/new_insect_type - - new_insect_type = input(user, "Choose your character's species:", "Character Preference") as null|anything in GLOB.insect_type_list - - if(new_insect_type) - active_character.features["insect_type"] = new_insect_type - - if("apid_antenna") - var/new_apid_antenna - - new_apid_antenna = input(user, "Choose your apid antennae:", "Character Preference") as null|anything in GLOB.apid_antenna_list - - if(new_apid_antenna) - active_character.features["apid_antenna"] = new_apid_antenna - - if("apid_stripes") - var/new_apid_stripes - - new_apid_stripes = input(user, "Choose your apid stripes:", "Character Preference") as null|anything in GLOB.apid_stripes_list - - if(new_apid_stripes) - active_character.features["apid_stripes"] = new_apid_stripes - - if("apid_headstripes") - var/new_apid_headstripes - - new_apid_headstripes = input(user, "Choose your apid headstripes:", "Character Preference") as null|anything in GLOB.apid_headstripes_list - - if(new_apid_headstripes) - active_character.features["apid_headstripes"] = new_apid_headstripes - - if("s_tone") - var/new_s_tone = input(user, "Choose your character's skin-tone:", "Character Preference") as null|anything in GLOB.skin_tones - if(new_s_tone) - active_character.skin_tone = new_s_tone - - if("ooccolor") - var/new_ooccolor = input(user, "Choose your OOC colour:", "Game Preference",ooccolor) as color|null - if(new_ooccolor) - ooccolor = new_ooccolor - - if("asaycolor") - var/new_asaycolor = input(user, "Choose your ASAY color:", "Game Preference",asaycolor) as color|null - if(new_asaycolor) - asaycolor = sanitize_ooccolor(new_asaycolor) - - if("bag") - var/new_backbag = input(user, "Choose your character's style of bag:", "Character Preference") as null|anything in GLOB.backbaglist - if(new_backbag) - active_character.backbag = new_backbag - - if("suit") - if(active_character.jumpsuit_style == PREF_SUIT) - active_character.jumpsuit_style = PREF_SKIRT - else - active_character.jumpsuit_style = PREF_SUIT - - if("uplink_loc") - var/new_loc = input(user, "Choose your character's traitor uplink spawn location:", "Character Preference") as null|anything in GLOB.uplink_spawn_loc_list - if(new_loc) - // This is done to prevent affecting saves - active_character.uplink_spawn_loc = new_loc == UPLINK_IMPLANT_WITH_PRICE ? UPLINK_IMPLANT : new_loc - - if("ai_core_icon") - var/ai_core_icon = input(user, "Choose your preferred AI core display screen:", "AI Core Display Screen Selection") as null|anything in GLOB.ai_core_display_screens - "Portrait" - if(ai_core_icon) - active_character.preferred_ai_core_display = ai_core_icon - - if("sec_dept") - var/department = input(user, "Choose your preferred security department:", "Security Departments") as null|anything in GLOB.security_depts_prefs - if(department) - active_character.preferred_security_department = department - - if ("preferred_map") - var/maplist = list() - var/default = "Default" - if (config.defaultmap) - default += " ([config.defaultmap.map_name])" - for (var/M in config.maplist) - var/datum/map_config/VM = config.maplist[M] - if(!VM.votable || SSmapping.config.map_name == VM.map_name) //current map will be excluded from the vote - continue - var/friendlyname = "[VM.map_name] " - if (VM.voteweight <= 0) - friendlyname += " (disabled)" - maplist[friendlyname] = VM.map_name - maplist[default] = null - var/pickedmap = input(user, "Choose your preferred map. This will be used to help weight random map selection.", "Character Preference") as null|anything in sort_list(maplist) - if (pickedmap) - preferred_map = maplist[pickedmap] - - if ("clientfps") - var/desiredfps = input(user, "Choose your desired fps. (0 = synced with server tick rate (currently:[world.fps]))", "Character Preference", clientfps) as null|num - if (!isnull(desiredfps)) - clientfps = desiredfps - parent.fps = desiredfps - if("ui") - var/pickedui = input(user, "Choose your UI style.", "Character Preference", UI_style) as null|anything in GLOB.available_ui_styles - if(pickedui) - UI_style = pickedui - if (parent && parent.mob && parent.mob.hud_used) - parent.mob.hud_used.update_ui_style(ui_style2icon(UI_style)) - if("pda_theme") - var/pickedPDAStyle = input(user, "Choose your default PDA theme.", "Character Preference", pda_theme) as null|anything in GLOB.ntos_device_themes_default - if(pickedPDAStyle) - pda_theme = GLOB.ntos_device_themes_default[pickedPDAStyle] - if("pda_color") - var/pickedPDAColor = input(user, "Choose your default Thinktronic Classic theme background color.", "Character Preference", pda_color) as color|null - if(pickedPDAColor) - pda_color = pickedPDAColor - if ("see_balloon_alerts") - var/pickedstyle = input(user, "Choose how you want balloon alerts displayed", "Balloon alert preference", BALLOON_ALERT_ALWAYS) as null|anything in list(BALLOON_ALERT_ALWAYS, BALLOON_ALERT_WITH_CHAT, BALLOON_ALERT_NEVER) - if (!isnull(pickedstyle)) - see_balloon_alerts = pickedstyle - - else - switch(href_list["preference"]) - if("publicity") - if(unlock_content) - toggles ^= PREFTOGGLE_MEMBER_PUBLIC - if("gender") - var/list/friendlyGenders = list("Male" = "male", "Female" = "female", "Other" = "plural") - var/pickedGender = input(user, "Choose your gender.", "Character Preference", active_character.gender) as null|anything in friendlyGenders - if(pickedGender && friendlyGenders[pickedGender] != active_character.gender) - switch(friendlyGenders[pickedGender]) - if("plural") - active_character.features["body_model"] = pick(MALE, FEMALE) - else - active_character.features["body_model"] = friendlyGenders[pickedGender] - active_character.gender = friendlyGenders[pickedGender] - if("body_model") - active_character.features["body_model"] = active_character.features["body_model"] == MALE ? FEMALE : MALE - if("hotkeys") - toggles2 ^= PREFTOGGLE_2_HOTKEYS - if(toggles2 & PREFTOGGLE_2_HOTKEYS) - winset(user, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED] mainwindow.macro=default") - else - winset(user, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED] mainwindow.macro=old_default") - if("action_buttons") - toggles2 ^= PREFTOGGLE_2_LOCKED_BUTTONS - if("tgui_fancy") - toggles2 ^= PREFTOGGLE_2_FANCY_TGUI - if("outline_enabled") - toggles ^= PREFTOGGLE_OUTLINE_ENABLED - if("outline_color") - var/pickedOutlineColor = input(user, "Choose your outline color.", "General Preference", outline_color) as color|null - if(pickedOutlineColor) - outline_color = pickedOutlineColor - if("tgui_lock") - toggles2 ^= PREFTOGGLE_2_LOCKED_TGUI - if("winflash") - toggles2 ^= PREFTOGGLE_2_WINDOW_FLASHING - if("crewobj") - toggles2 ^= PREFTOGGLE_2_WINDOW_FLASHING - - //here lies the badmins - if("hear_adminhelps") - user.client.toggleadminhelpsound() - if("hear_adminalertsounds") - user.client.toggleadminalertsound() - if("hear_prayers") - user.client.toggle_prayer_sound() - if("announce_login") - user.client.toggleannouncelogin() - if("combohud_lighting") - toggles ^= PREFTOGGLE_COMBOHUD_LIGHTING - if("toggle_dead_chat") - user.client.deadchat() - if("toggle_radio_chatter") - user.client.toggle_hear_radio() - if("toggle_prayers") - user.client.toggleprayers() - if("toggle_deadmin_always") - toggles ^= PREFTOGGLE_DEADMIN_ALWAYS - if("toggle_deadmin_antag") - toggles ^= PREFTOGGLE_DEADMIN_ANTAGONIST - if("toggle_deadmin_head") - toggles ^= PREFTOGGLE_DEADMIN_POSITION_HEAD - if("toggle_deadmin_security") - toggles ^= PREFTOGGLE_DEADMIN_POSITION_SECURITY - if("toggle_deadmin_silicon") - toggles ^= PREFTOGGLE_DEADMIN_POSITION_SILICON - - - if("be_special") - var/be_special_type = href_list["be_special_type"] - var/current = be_special["[be_special_type]"] - if(isnum(current)) - be_special["[be_special_type]"] = !current - else // not set, we assume it's on, so turn it off. - be_special["[be_special_type]"] = FALSE - - if("name") - active_character.be_random_name = !active_character.be_random_name - - if("all") - active_character.be_random_body = !active_character.be_random_body - - if("hear_midis") - toggles ^= PREFTOGGLE_SOUND_MIDI - - if("lobby_music") - toggles ^= PREFTOGGLE_SOUND_LOBBY - if((toggles & PREFTOGGLE_SOUND_LOBBY) && user.client && isnewplayer(user)) - user.client.playtitlemusic() - else - user.stop_sound_channel(CHANNEL_LOBBYMUSIC) - - if("soundtrack") - toggles2 ^= PREFTOGGLE_2_SOUNDTRACK - if((toggles2 & PREFTOGGLE_2_SOUNDTRACK)) - user.play_current_soundtrack() - else - user.stop_sound_channel(CHANNEL_SOUNDTRACK) - - if("ghost_ears") - chat_toggles ^= CHAT_GHOSTEARS - - if("ghost_sight") - chat_toggles ^= CHAT_GHOSTSIGHT - - if("ghost_whispers") - chat_toggles ^= CHAT_GHOSTWHISPER - - if("ghost_radio") - chat_toggles ^= CHAT_GHOSTRADIO - - if("ghost_pda") - chat_toggles ^= CHAT_GHOSTPDA - - if("ghost_laws") - chat_toggles ^= CHAT_GHOSTLAWS - - if("ghost_follow") - chat_toggles ^= CHAT_GHOSTFOLLOWMINDLESS - - if("income_pings") - chat_toggles ^= CHAT_BANKCARD - - if("pull_requests") - chat_toggles ^= CHAT_PULLR - - if("tgui_input") - toggles2 ^= PREFTOGGLE_2_TGUI_INPUT - - if("tgui_big_buttons") - toggles2 ^= PREFTOGGLE_2_BIG_BUTTONS - - if("tgui_switched_buttons") - toggles2 ^= PREFTOGGLE_2_SWITCHED_BUTTONS - - if("tgui_say") - toggles2 ^= PREFTOGGLE_2_TGUI_SAY - if(parent) - if(parent.tgui_say) - parent.tgui_say.close() - parent.set_macros() - - if("tgui_say_light") - toggles2 ^= PREFTOGGLE_2_SAY_LIGHT_THEME - if(parent && parent.tgui_say) // change the theme - parent.tgui_say.load() - - if("tgui_say_radio_prefix") - toggles2 ^= PREFTOGGLE_2_SAY_SHOW_PREFIX - if(parent && parent.tgui_say) // update the UI - parent.tgui_say.load() - - if("parallaxup") - parallax = WRAP(parallax + 1, PARALLAX_INSANE, PARALLAX_DISABLE + 1) - if (parent && parent.mob && parent.mob.hud_used) - parent.mob.hud_used.update_parallax_pref(parent.mob) - - if("parallaxdown") - parallax = WRAP(parallax - 1, PARALLAX_INSANE, PARALLAX_DISABLE + 1) - if (parent && parent.mob && parent.mob.hud_used) - parent.mob.hud_used.update_parallax_pref(parent.mob) - - if("ambientocclusion") - toggles2 ^= PREFTOGGLE_2_AMBIENT_OCCLUSION - if(parent && parent.screen && parent.screen.len) - var/atom/movable/screen/plane_master/game_world/PM = locate(/atom/movable/screen/plane_master/game_world) in parent.screen - PM.backdrop(parent.mob) - - if("auto_fit_viewport") - toggles2 ^= PREFTOGGLE_2_AUTO_FIT_VIEWPORT - if((toggles2 & PREFTOGGLE_2_AUTO_FIT_VIEWPORT) && parent) - parent.fit_viewport() - - if("pixel_size") - switch(pixel_size) - if(PIXEL_SCALING_AUTO) - pixel_size = PIXEL_SCALING_1X - if(PIXEL_SCALING_1X) - pixel_size = PIXEL_SCALING_1_2X - if(PIXEL_SCALING_1_2X) - pixel_size = PIXEL_SCALING_2X - if(PIXEL_SCALING_2X) - pixel_size = PIXEL_SCALING_3X - if(PIXEL_SCALING_3X) - pixel_size = PIXEL_SCALING_AUTO - user.client.view_size.resetToDefault(getScreenSize(user)) //Fix our viewport size so it doesn't reset on change - - if("scaling_method") - switch(scaling_method) - if(SCALING_METHOD_NORMAL) - scaling_method = SCALING_METHOD_DISTORT - if(SCALING_METHOD_DISTORT) - scaling_method = SCALING_METHOD_BLUR - if(SCALING_METHOD_BLUR) - scaling_method = SCALING_METHOD_NORMAL - user.client.view_size.setZoomMode() - - if("save") - save_preferences() - active_character.save(user.client) - - if("load") - load_from_database() - load_characters() - - if("changeslot") - var/numerical_slot = text2num(href_list["num"]) - var/datum/character_save/CS = character_saves[numerical_slot] - if(CS && !CS.slot_locked) - active_character = CS - default_slot = numerical_slot - // If its fresh, randomise & save it - if(!CS.from_db) - CS.randomise() - CS.save(user.client) - - if("tab") - if (href_list["tab"]) - current_tab = text2num(href_list["tab"]) - - if("keybindings_menu") - ShowKeybindings(user) - return - - if("keybindings_capture") - var/datum/keybinding/kb = GLOB.keybindings_by_name[href_list["keybinding"]] - var/old_key = href_list["old_key"] - CaptureKeybinding(user, kb, old_key) - return - - if("keybindings_set") - var/kb_name = href_list["keybinding"] - if(!kb_name) - user << browse(null, "window=capturekeypress") - ShowKeybindings(user) - return - - var/clear_key = text2num(href_list["clear_key"]) - var/old_key = href_list["old_key"] - - if(clear_key) - if(old_key != "Unbound") // if it was already set - key_bindings[old_key] -= kb_name - key_bindings["Unbound"] += list(kb_name) - save_preferences() - user << browse(null, "window=capturekeypress") - ShowKeybindings(user) - return - - var/key = href_list["key"] - var/numpad = text2num(href_list["numpad"]) - // TODO: Handle holding shift or alt down - var/AltMod = text2num(href_list["alt"]) ? "Alt-" : "" - var/CtrlMod = text2num(href_list["ctrl"]) ? "Ctrl-" : "" - var/ShiftMod = text2num(href_list["shift"]) ? "Shift-" : "" - // var/key_code = text2num(href_list["key_code"]) - - var/new_key = uppertext(key) - - // This is a mapping from JS keys to Byond - ref: https://keycode.info/ - var/list/_kbMap = list( - "INSERT" = "Insert", "HOME" = "Northwest", "PAGEUP" = "Northeast", - "DEL" = "Delete", "END" = "Southwest", "PAGEDOWN" = "Southeast", - "SPACEBAR" = "Space", "ALT" = "Alt", "SHIFT" = "Shift", "CONTROL" = "Ctrl" - ) - new_key = _kbMap[new_key] ? _kbMap[new_key] : new_key - - if (numpad) - new_key = "Numpad[new_key]" - - var/full_key = "[AltMod][CtrlMod][ShiftMod][new_key]" - if(old_key && (old_key in key_bindings)) - key_bindings[old_key] -= kb_name - key_bindings[full_key] += list(kb_name) - key_bindings[full_key] = sort_list(key_bindings[full_key]) - - save_preferences() - user << browse(null, "window=capturekeypress") - ShowKeybindings(user) - return - - if("keybindings_done") - user << browse(null, "window=keybindings") - - if("keybindings_reset") - key_bindings = deep_copy_list(GLOB.keybinding_list_by_key) - save_preferences() - ShowKeybindings(user) - return - - if("chat_on_map") - toggles ^= PREFTOGGLE_RUNECHAT_GLOBAL - if("see_chat_non_mob") - toggles ^= PREFTOGGLE_RUNECHAT_NONMOBS - if("see_rc_emotes") - toggles ^= PREFTOGGLE_RUNECHAT_EMOTES - - ShowChoices(user) - return 1 - -/datum/preferences/proc/ask_for_custom_name(mob/user,name_id) - var/namedata = GLOB.preferences_custom_names[name_id] - if(!namedata) - return +/// Applies the given preferences to a human mob. +/datum/preferences/proc/apply_prefs_to(mob/living/carbon/human/character, icon_updates = TRUE) + character.dna.features = list() - var/raw_name = capped_input(user, "Choose your character's [namedata["qdesc"]]:","Character Preference") - if(!raw_name) - if(namedata["allow_null"]) - active_character.custom_names[name_id] = get_default_name(name_id) - else - return - else - var/sanitized_name = reject_bad_name(raw_name,namedata["allow_numbers"]) - if(!sanitized_name) - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z,[namedata["allow_numbers"] ? ",0-9," : ""] -, ' and .") - return - else - active_character.custom_names[name_id] = sanitized_name + for (var/datum/preference/preference as anything in get_preferences_in_priority_order()) + if (preference.savefile_identifier != PREFERENCE_CHARACTER) + continue -/// Handles adding and removing donator items from clients -/datum/preferences/proc/handle_donator_items() - var/datum/loadout_category/DLC = GLOB.loadout_categories["Donator"] // stands for donator loadout category but the other def for DLC works too xD - if(!LAZYLEN(GLOB.patrons) || !CONFIG_GET(flag/donator_items)) // donator items are only accesibile by servers with a patreon - return - if(IS_PATRON(parent.ckey) || (parent in GLOB.admins)) - for(var/gear_id in DLC.gear) - var/datum/gear/AG = DLC.gear[gear_id] - if(AG.id in purchased_gear) - continue - purchased_gear += AG.id - AG.purchase(parent) - save_preferences() - else if(length(purchased_gear) || length(active_character.equipped_gear)) - for(var/gear_id in DLC.gear) - var/datum/gear/RG = DLC.gear[gear_id] - active_character.equipped_gear -= RG.id - purchased_gear -= RG.id - save_preferences() + preference.apply_to_human(character, read_preference(preference.type)) + + character.dna.real_name = character.real_name + + if(icon_updates) + character.update_body() + character.update_hair() + character.update_body_parts() + + +/// Returns whether the parent mob should have the random hardcore settings enabled. Assumes it has a mind. +/datum/preferences/proc/should_be_random_hardcore(datum/job/job, datum/mind/mind) + if(!read_preference(/datum/preference/toggle/random_hardcore)) + return FALSE + if(job.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND) //No command staff + return FALSE + for(var/datum/antagonist/antag as anything in mind.antag_datums) + if(antag.get_team()) //No team antags + return FALSE + return TRUE + +/// Inverts the key_bindings list such that it can be used for key_bindings_by_key +/datum/preferences/proc/get_key_bindings_by_key(list/key_bindings) + var/list/output = list() + + for (var/action in key_bindings) + for (var/key in key_bindings[action]) + LAZYADD(output[key], action) + + return output + +/// Returns the default `randomise` variable ouptut +/datum/preferences/proc/get_default_randomization() + var/list/default_randomization = list() + + for (var/preference_key in GLOB.preference_entries_by_key) + var/datum/preference/preference = GLOB.preference_entries_by_key[preference_key] + if (preference.is_randomizable() && preference.randomize_by_default) + default_randomization[preference_key] = RANDOM_ENABLED + + return default_randomization diff --git a/code/modules/client/preferences/README.md b/code/modules/client/preferences/README.md new file mode 100644 index 0000000000000..fa13228f11c02 --- /dev/null +++ b/code/modules/client/preferences/README.md @@ -0,0 +1,476 @@ +# Preferences (by Mothblocks) + +This does not contain all the information on specific values--you can find those as doc-comments in relevant paths, such as `/datum/preference`. Rather, this gives you an overview for creating *most* preferences, and getting your foot in the door to create more advanced ones. + +## Anatomy of a preference (A.K.A. how do I make one?) + +Most preferences consist of two parts: + +1. A `/datum/preference` type. +2. A tgui representation in a TypeScript file. + +Every `/datum/preference` requires these three values be set: +1. `category` - See [Categories](#Categories). +2. `savefile_key` - The value which will be saved in the savefile. This will also be the identifier for tgui. +3. `savefile_identifier` - Whether or not this is a character specific preference (`PREFERENCE_CHARACTER`) or one that affects the player (`PREFERENCE_PLAYER`). As an example: hair color is `PREFERENCE_CHARACTER` while your UI settings are `PREFERENCE_PLAYER`, since they do not change between characters. + +For the tgui representation, most preferences will create a `.tsx` file in `tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/`. If your preference is a character preference, make a new file in `character_preferences`. Otherwise, put it in `game_preferences`. The filename does not matter, and this file can hold multiple relevant preferences if you would like. + +From here, you will want to write code resembling: + +```ts +import { Feature } from "../base"; + +export const savefile_key_here: Feature = { + name: "Preference Name Here", + component: Component, + + // Necessary for game preferences, unused for others + category: "CATEGORY", + + // Optional, only shown in game preferences + description: "This preference will blow your mind!", +} +``` + +`T` and `Component` depend on the type of preference you're making. Here are all common examples... + +## Numeric preferences + +Examples include age and FPS. + +A numeric preference derives from `/datum/preference/numeric`. + +```dm +/datum/preference/numeric/legs + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "legs" + + minimum = 1 + maximum = 8 +``` + +You can optionally provide a `step` field. This value is 1 by default, meaning only integers are accepted. + +Your `.tsx` file would look like: + +```ts +import { Feature, FeatureNumberInput } from "../base"; + +export const legs: Feature = { + name: "Legs", + component: FeatureNumberInput, +} +``` + +## Toggle preferences + +Examples include enabling tooltips. + +```dm +/datum/preference/toggle/enable_breathing + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "enable_breathing" + + // Optional, TRUE by default + default_value = FALSE +``` + +Your `.tsx` file would look like: + +```ts +import { CheckboxInput, FeatureToggle } from "../base"; + +export const enable_breathing: Feature = { + name: "Enable breathing", + component: CheckboxInput, +} +``` + +## Choiced preferences +A choiced preference is one where the only options are in a distinct few amount of choices. Examples include skin tone, shirt, and UI style. + +To create one, derive from `/datum/preference/choiced`. + +```dm +/datum/preference/choiced/favorite_drink + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "favorite_drink" +``` + +Now we need to tell the game what the choices are. We do this by overriding `init_possible_values()`. This will return a list of possible options. + +```dm +/datum/preference/choiced/favorite_drink/init_possible_values() + return list( + "Milk", + "Cola", + "Water", + ) +``` + +Your `.tsx` file would then look like: + +```tsx +import { FeatureChoiced, FeatureDropdownInput } from "../base"; + +export const favorite_drink: FeatureChoiced = { + name: "Favorite drink", + component: FeatureDropdownInput, +}; +``` + +This will create a dropdown input for your preference. + +### Choiced preferences - Icons +Choiced preferences can generate icons. This is how the clothing/species preferences work, for instance. However, if we just want a basic dropdown input with icons, it would look like this: + +```dm +/datum/preference/choiced/favorite_drink + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "favorite_drink" + should_generate_icons = TRUE // NEW! This is necessary. + +// Instead of returning a flat list, this now returns an assoc list +// of values to icons. +/datum/preference/choiced/favorite_drink/init_possible_values() + return list( + "Milk" = icon('drinks.dmi', "milk"), + "Cola" = icon('drinks.dmi', "cola"), + "Water" = icon('drinks.dmi', "water"), + ) +``` + +Then, change your `.tsx` file to look like: + +```tsx +import { FeatureChoiced, FeatureIconnedDropdownInput } from "../base"; + +export const favorite_drink: FeatureChoiced = { + name: "Favorite drink", + component: FeatureIconnedDropdownInput, +}; +``` + +### Choiced preferences - Display names +Sometimes the values you want to save in code aren't the same as the ones you want to display. You can specify display names to change this. + +The only thing you will add is "compiled data". + +```dm +/datum/preference/choiced/favorite_drink/compile_constant_data() + var/list/data = ..() + + // An assoc list of values to display names + data[CHOICED_PREFERENCE_DISPLAY_NAMES] = list( + "Milk" = "Delicious Milk", + "Cola" = "Crisp Cola", + "Water" = "Plain Ol' Water", + ) + + return data +``` + +Your `.tsx` file does not change. The UI will figure it out for you! + +## Color preferences +These refer to colors, such as your OOC color. When read, these values will be given as 6 hex digits, *without* the pound sign. + +```dm +/datum/preference/color/eyeliner_color + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "eyeliner_color" +``` + +Your `.tsx` file would look like: + +```ts +import { FeatureColorInput, Feature } from "../base"; + +export const eyeliner_color: Feature = { + name: "Eyeliner color", + component: FeatureColorInput, +}; +``` + +## Name preferences +These refer to an alternative name. Examples include AI names and backup human names. + +These exist in `code/modules/client/preferences/names.dm`. + +These do not need a `.ts` file, and will be created in the UI automatically. + +```dm +/datum/preference/name/doctor + savefile_key = "doctor_name" + + // The name on the UI + explanation = "Doctor name" + + // This groups together with anything else with the same group + group = "medicine" + + // Optional, if specified the UI will show this name actively + // when the player is a medical doctor. + relevant_job = /datum/job/medical_doctor +``` + +## Making your preference do stuff + +There are a handful of procs preferences can use to act on their own: + +```dm +/// Apply this preference onto the given client. +/// Called when the savefile_identifier == PREFERENCE_PLAYER. +/datum/preference/proc/apply_to_client(client/client, value) + +/// Fired when the preference is updated. +/// Calls apply_to_client by default, but can be overridden. +/datum/preference/proc/apply_to_client_updated(client/client, value) + +/// Apply this preference onto the given human. +/// Must be overriden by subtypes. +/// Called when the savefile_identifier == PREFERENCE_CHARACTER. +/datum/preference/proc/apply_to_human(mob/living/carbon/human/target, value) +``` + +For example, `/datum/preference/numeric/age` contains: + +```dm +/datum/preference/numeric/age/apply_to_human(mob/living/carbon/human/target, value) + target.age = value +``` + +If your preference is `PREFERENCE_CHARACTER`, it MUST override `apply_to_human`, even if just to immediately `return`. + +You can also read preferences directly with `preferences.read_preference(/datum/preference/type/here)`, which will return the stored value. + +## Categories +Every preference needs to be in a `category`. These can be found in `code/__DEFINES/preferences.dm`. + +```dm +/// These will be shown in the character sidebar, but at the bottom. +#define PREFERENCE_CATEGORY_FEATURES "features" + +/// Any preferences that will show to the sides of the character in the setup menu. +#define PREFERENCE_CATEGORY_CLOTHING "clothing" + +/// Preferences that will be put into the 3rd list, and are not contextual. +#define PREFERENCE_CATEGORY_NON_CONTEXTUAL "non_contextual" + +/// Will be put under the game preferences window. +#define PREFERENCE_CATEGORY_GAME_PREFERENCES "game_preferences" + +/// These will show in the list to the right of the character preview. +#define PREFERENCE_CATEGORY_SECONDARY_FEATURES "secondary_features" + +/// These are preferences that are supplementary for main features, +/// such as hair color being affixed to hair. +#define PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES "supplemental_features" +``` + +![Preference categories for the main page](https://raw.githubusercontent.com/tgstation/documentation-assets/main/preferences/preference_categories.png) + +> SECONDARY_FEATURES or NON_CONTEXTUAL? + +Secondary features tend to be species specific. Non contextual features shouldn't change much from character to character. + +## Default values and randomization + +There are three procs to be aware of in regards to this topic: + +- `create_default_value()`. This is used when a value deserializes improperly or when a new character is created. +- `create_informed_default_value(datum/preferences/preferences)` - Used for more complicated default values, like how names require the gender. Will call `create_default_value()` by default. +- `create_random_value(datum/preferences/preferences)` - Explicitly used for random values, such as when a character is being randomized. + +`create_default_value()` in most preferences will create a random value. If this is a problem (like how default characters should always be human), you can override `create_default_value()`. By default (without overriding `create_random_value`), random values are just default values. + +## Advanced - Server data + +As previewed in [the display names implementation](#Choiced-preferences---Display-names), there exists a `compile_constant_data()` proc you can override. + +Compiled data is used wherever the server needs to give the client some value it can't figure out on its own. Skin tones use this to tell the client what colors they represent, for example. + +Compiled data is sent to the `serverData` field in the `FeatureValueProps`. + +## Advanced - Creating your own tgui component + +If you have good knowledge with tgui (especially TypeScript), you'll be able to create your own component to represent preferences. + +The `component` field in a feature accepts __any__ component that accepts `FeatureValueProps`. + +This will give you the fields: + +```ts +act: typeof sendAct, +featureId: string, +handleSetValue: (newValue: TSending) => void, +serverData: TServerData | undefined, +shrink?: boolean, +value: TReceiving, +``` + +`act` is the same as the one you get from `useBackend`. + +`featureId` is the savefile_key of the feature. + +`handleSetValue` is a function that, when called, will tell the server the new value, as well as changing the value immediately locally. + +`serverData` is the [server data](#Advanced---Server-data), if it has been fetched yet (and exists). + +`shrink` is whether or not the UI should appear smaller. This is only used for supplementary features. + +`value` is the current value, could be predicted (meaning that the value was changed locally, but has not yet reached the server). + +For a basic example of how this can look, observe `CheckboxInput`: + +```tsx +export const CheckboxInput = ( + props: FeatureValueProps +) => { + return ( { + props.handleSetValue(!props.value); + }} + />); +}; +``` + +## Advanced - Middleware +A `/datum/preference_middleware` is a way to inject your own data at specific points, as well as hijack actions. + +Middleware can hijack actions by specifying `action_delegations`: + +```dm +/datum/preference_middleware/congratulations + action_delegations = list( + "congratulate_me" = .proc/congratulate_me, + ) + +/datum/preference_middleware/congratulations/proc/congratulate_me(list/params, mob/user) + to_chat(user, span_notice("Wow, you did a great job learning about middleware!")) + + return TRUE +``` + +Middleware can inject its own data at several points, such as providing new UI assets, compiled data (used by middleware such as quirks to tell the client what quirks exist), etc. Look at `code/modules/client/preferences/middleware/_middleware.dm` for full information. + +--- + +## Jobs + +Every job must have an associated `.ts` file so that the UI knows where to place it. + +Create a file in `tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/`. + +This will specify the description and department of the job. Here is the details for the medical doctor: + +```ts +import { Job } from "../base"; +import { Service } from "../departments"; + +const Cook: Job = { + name: "Cook", + // If you need more room, use `multiline` + description: "Serve food, cook meat, keep the crew fed.", + department: Service, +}; + +export default Cook; +``` + +--- + +## Antagonists + +In order to make an antagonist selectable, you must do a few things: + +1. Your antagonist needs an icon. +2. Your antagonist must be in a Dynamic ruleset. The ruleset must specify the antagonist as its `antag_flag`. +3. Your antagonist needs a file in `tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/filename.ts`. This file name MUST be the `antag_flag` of your ruleset, with nothing but letters remaining (e.g. "Nuclear Operative" -> `nuclearoperative`). +4. Add it to `special_roles`. + +## Creating icons + +If you are satisfied with your icon just being a dude with some clothes, then you can specify `preview_outfit` in your `/datum/antagonist`. + +Space Ninja, for example, looks like: + +```dm +/datum/antagonist/ninja + preview_outift = /datum/outfit/ninja +``` + +However, if you want to get creative, you can override `/get_preview_icon()`. This proc should return an icon of size `ANTAGONIST_PREVIEW_ICON_SIZE`x`ANTAGONIST_PREVIEW_ICON_SIZE`. + +There are some helper procs you can use as well. `render_preview_outfit(outfit_type)` will take an outfit and give you an icon of someone wearing those clothes. `finish_preview_outfit` will, given an icon, resize it appropriately and zoom in on the head. Note that this will look bad on anything that isn't a human, so if you have a non-human antagonist (such as sentient disease), just run `icon.Scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE)`. + +For inspiration, here is changeling's: + +```dm +/datum/antagonist/changeling/get_preview_icon() + var/icon/final_icon = render_preview_outfit(/datum/outfit/changeling) + var/icon/split_icon = render_preview_outfit(/datum/outfit/job/engineer) + + final_icon.Shift(WEST, world.icon_size / 2) + final_icon.Shift(EAST, world.icon_size / 2) + + split_icon.Shift(EAST, world.icon_size / 2) + split_icon.Shift(WEST, world.icon_size / 2) + + final_icon.Blend(split_icon, ICON_OVERLAY) + + return finish_preview_icon(final_icon) +``` + +...which creates: + +![Changeling icon](https://raw.githubusercontent.com/tgstation/documentation-assets/main/preferences/changeling.png) + +## Creating the tgui representation + +In the `.ts` file you created earlier, you must now give the information of your antagonist. For reference, this is the changeling's: + +```ts +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +const Changeling: Antagonist = { + key: "changeling", // This must be the same as your filename + name: "Changeling", + description: [ + multiline` + A highly intelligent alien predator that is capable of altering their + shape to flawlessly resemble a human. + `, + + multiline` + Transform yourself or others into different identities, and buy from an + arsenal of biological weaponry with the DNA you collect. + `, + ], + category: Category.Roundstart, // Category.Roundstart, Category.Midround, or Category.Latejoin +}; + +export default Changeling; +``` + +## Readying the Dynamic ruleset + +You already need to create a Dynamic ruleset, so in order to get your antagonist recognized, you just need to specify `antag_flag`. This must be unique per ruleset. + +Two other values to note are `antag_flag_override` and `antag_preference`. + +`antag_flag_override` exists for cases where you want the banned antagonist to be separate from `antag_flag`. As an example: roundstart, midround, and latejoin traitors have separate `antag_flag`, but all have `antag_flag_override = ROLE_TRAITOR`. This is because admins want to ban a player from Traitor altogether, not specific rulesets. + +If `antag_preference` is set, it will refer to that preference instead of `antag_flag`. This is used for clown operatives, which we want to be on the same preference as standard nuke ops, but must specify a unique `antag_flag` for. + +## Updating special_roles + +In `code/__DEFINES/role_preferences.dm` (the same place you'll need to make your ROLE_\* defined), simply add your antagonist to the `special_roles` assoc list. The key is your ROLE, the value is the number of days since your first game in order to play as that antagonist. diff --git a/code/modules/client/preferences/_preference.dm b/code/modules/client/preferences/_preference.dm new file mode 100644 index 0000000000000..0f5de07a7dff3 --- /dev/null +++ b/code/modules/client/preferences/_preference.dm @@ -0,0 +1,534 @@ +// Priorities must be in order! +/// The default priority level +#define PREFERENCE_PRIORITY_DEFAULT 1 + +/// The priority at which species runs, needed for external organs to apply properly. +#define PREFERENCE_PRIORITY_SPECIES 2 + +/// The priority at which gender is determined, needed for proper randomization. +#define PREFERENCE_PRIORITY_GENDER 3 + +/// The priority at which body type is decided, applied after gender so we can +/// make sure they're non-binary. +#define PREFERENCE_PRIORITY_BODY_TYPE 4 + +/// The priority at which names are decided, needed for proper randomization. +#define PREFERENCE_PRIORITY_NAMES 5 + +/// The maximum preference priority, keep this updated, but don't use it for `priority`. +#define MAX_PREFERENCE_PRIORITY PREFERENCE_PRIORITY_NAMES + +/// For choiced preferences, this key will be used to set display names in constant data. +#define CHOICED_PREFERENCE_DISPLAY_NAMES "display_names" + +/// For main feature preferences, this key refers to a feature considered supplemental. +/// For instance, hair color being supplemental to hair. +#define SUPPLEMENTAL_FEATURE_KEY "supplemental_feature" + +/// An assoc list list of types to instantiated `/datum/preference` instances +GLOBAL_LIST_INIT(preference_entries, init_preference_entries()) + +/// An assoc list of preference entries by their `savefile_key` +GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) + +/proc/init_preference_entries() + var/list/output = list() + for (var/datum/preference/preference_type as anything in subtypesof(/datum/preference)) + if (initial(preference_type.abstract_type) == preference_type) + continue + output[preference_type] = new preference_type + return output + +/proc/init_preference_entries_by_key() + var/list/output = list() + for (var/datum/preference/preference_type as anything in subtypesof(/datum/preference)) + if (initial(preference_type.abstract_type) == preference_type) + continue + output[initial(preference_type.savefile_key)] = GLOB.preference_entries[preference_type] + return output + +/// Returns a flat list of preferences in order of their priority +/proc/get_preferences_in_priority_order() + var/list/preferences[MAX_PREFERENCE_PRIORITY] + + for (var/preference_type in GLOB.preference_entries) + var/datum/preference/preference = GLOB.preference_entries[preference_type] + LAZYADD(preferences[preference.priority], preference) + + var/list/flattened = list() + for (var/index in 1 to MAX_PREFERENCE_PRIORITY) + flattened += preferences[index] + return flattened + +/// Represents an individual preference. +/datum/preference + /// The key inside the savefile to use. + /// This is also sent to the UI. + /// Once you pick this, don't change it. + var/savefile_key + + /// The category of preference, for use by the PreferencesMenu. + /// This isn't used for anything other than as a key for UI data. + /// It is up to the PreferencesMenu UI itself to interpret it. + var/category = "misc" + + /// Do not instantiate if type matches this. + var/abstract_type = /datum/preference + + /// What savefile should this preference be read from? + /// Valid values are PREFERENCE_CHARACTER and PREFERENCE_PLAYER. + /// See the documentation in [code/__DEFINES/preferences.dm]. + var/savefile_identifier + + /// The priority of when to apply this preference. + /// Used for when you need to rely on another preference. + var/priority = PREFERENCE_PRIORITY_DEFAULT + + /// If set, will be available to randomize, but only if the preference + /// is for PREFERENCE_CHARACTER. + var/can_randomize = TRUE + + /// If randomizable (PREFERENCE_CHARACTER and can_randomize), whether + /// or not to enable randomization by default. + /// This doesn't mean it'll always be random, but rather if a player + /// DOES have random body on, will this already be randomized? + var/randomize_by_default = TRUE + + /// If the selected species has this in its /datum/species/mutant_bodyparts, + /// will show the feature as selectable. + var/relevant_mutant_bodypart = null + + /// If the selected species has this in its /datum/species/species_traits, + /// will show the feature as selectable. + var/relevant_species_trait = null + +/// Called on the saved input when retrieving. +/// Also called by the value sent from the user through UI. Do not trust it. +/// Input is the value inside the savefile, output is to tell other code +/// what the value is. +/// This is useful either for more optimal data saving or for migrating +/// older data. +/// Must be overridden by subtypes. +/// Can return null if no value was found. +/datum/preference/proc/deserialize(input, datum/preferences/preferences) + SHOULD_NOT_SLEEP(TRUE) + SHOULD_CALL_PARENT(FALSE) + CRASH("`deserialize()` was not implemented on [type]!") + +/// Called on the input while saving. +/// Input is the current value, output is what to save in the savefile. +/datum/preference/proc/serialize(input) + SHOULD_NOT_SLEEP(TRUE) + return input + +/// Produce a default, potentially random value for when no value for this +/// preference is found in the savefile. +/// Either this or create_informed_default_value must be overriden by subtypes. +/datum/preference/proc/create_default_value() + SHOULD_NOT_SLEEP(TRUE) + SHOULD_CALL_PARENT(FALSE) + CRASH("`create_default_value()` was not implemented on [type]!") + +/// Produce a default, potentially random value for when no value for this +/// preference is found in the savefile. +/// Unlike create_default_value(), will provide the preferences object if you +/// need to use it. +/// If not overriden, will call create_default_value() instead. +/datum/preference/proc/create_informed_default_value(datum/preferences/preferences) + return create_default_value() + +/// Produce a random value for the purposes of character randomization. +/// Will just create a default value by default. +/datum/preference/proc/create_random_value(datum/preferences/preferences) + return create_informed_default_value(preferences) + +/// Returns whether or not a preference can be randomized. +/datum/preference/proc/is_randomizable() + SHOULD_NOT_OVERRIDE(TRUE) + return savefile_identifier == PREFERENCE_CHARACTER && can_randomize + +/// Given a savefile, return either the saved data or an acceptable default. +/// This will write to the savefile if a value was not found with the new value. +/datum/preference/proc/read(savefile/savefile, datum/preferences/preferences) + SHOULD_NOT_OVERRIDE(TRUE) + + var/value + + if (!isnull(savefile)) + READ_FILE(savefile[savefile_key], value) + + if (isnull(value)) + return null + else + return deserialize(value, preferences) + +/// Given a savefile, writes the inputted value. +/// Returns TRUE for a successful application. +/// Return FALSE if it is invalid. +/datum/preference/proc/write(savefile/savefile, value) + SHOULD_NOT_OVERRIDE(TRUE) + + if (!is_valid(value)) + return FALSE + + if (!isnull(savefile)) + WRITE_FILE(savefile[savefile_key], serialize(value)) + + return TRUE + +/// Apply this preference onto the given client. +/// Called when the savefile_identifier == PREFERENCE_PLAYER. +/datum/preference/proc/apply_to_client(client/client, value) + SHOULD_NOT_SLEEP(TRUE) + SHOULD_CALL_PARENT(FALSE) + return + +/// Fired when the preference is updated. +/// Calls apply_to_client by default, but can be overridden. +/datum/preference/proc/apply_to_client_updated(client/client, value) + SHOULD_NOT_SLEEP(TRUE) + apply_to_client(client, value) + +/// Apply this preference onto the given human. +/// Must be overriden by subtypes. +/// Called when the savefile_identifier == PREFERENCE_CHARACTER. +/datum/preference/proc/apply_to_human(mob/living/carbon/human/target, value) + SHOULD_NOT_SLEEP(TRUE) + SHOULD_CALL_PARENT(FALSE) + CRASH("`apply_to_human()` was not implemented for [type]!") + +/// Returns which savefile to use for a given savefile identifier +/datum/preferences/proc/get_savefile_for_savefile_identifier(savefile_identifier) + RETURN_TYPE(/savefile) + + if (!parent) + return null + + switch (savefile_identifier) + if (PREFERENCE_CHARACTER) + return character_savefile + if (PREFERENCE_PLAYER) + if (!game_savefile) + game_savefile = new /savefile(path) + game_savefile.cd = "/" + return game_savefile + else + CRASH("Unknown savefile identifier [savefile_identifier]") + +/// Read a /datum/preference type and return its value. +/// This will write to the savefile if a value was not found with the new value. +/datum/preferences/proc/read_preference(preference_type) + var/datum/preference/preference_entry = GLOB.preference_entries[preference_type] + if (isnull(preference_entry)) + var/extra_info = "" + + // Current initializing subsystem is important to know because it might be a problem with + // things running pre-assets-initialization. + if (!isnull(Master.current_initializing_subsystem)) + extra_info = "Info was attempted to be retrieved while [Master.current_initializing_subsystem] was initializing." + + CRASH("Preference type `[preference_type]` is invalid! [extra_info]") + + if (preference_type in value_cache) + return value_cache[preference_type] + + var/value = preference_entry.read(get_savefile_for_savefile_identifier(preference_entry.savefile_identifier), src) + if (isnull(value)) + value = preference_entry.create_informed_default_value(src) + if (write_preference(preference_entry, value)) + return value + else + CRASH("Couldn't write the default value for [preference_type] (received [value])") + value_cache[preference_type] = value + return value + +/// Set a /datum/preference entry. +/// Returns TRUE for a successful preference application. +/// Returns FALSE if it is invalid. +/datum/preferences/proc/write_preference(datum/preference/preference, preference_value) + var/savefile = get_savefile_for_savefile_identifier(preference.savefile_identifier) + var/new_value = preference.deserialize(preference_value, src) + var/success = preference.write(savefile, new_value) + if (success) + value_cache[preference.type] = new_value + return success + +/// Will perform an update on the preference, but not write to the savefile. +/// This will, for instance, update the character preference view. +/// Performs sanity checks. +/datum/preferences/proc/update_preference(datum/preference/preference, preference_value) + if (!preference.is_accessible(src)) + return FALSE + + var/new_value = preference.deserialize(preference_value, src) + var/success = preference.write(null, new_value) + + if (!success) + return FALSE + + recently_updated_keys |= preference.type + value_cache[preference.type] = new_value + + if (preference.savefile_identifier == PREFERENCE_PLAYER) + preference.apply_to_client_updated(parent, read_preference(preference.type)) + else + character_preview_view?.update_body() + + return TRUE + +/// Checks that a given value is valid. +/// Must be overriden by subtypes. +/// Any type can be passed through. +/datum/preference/proc/is_valid(value) + SHOULD_NOT_SLEEP(TRUE) + SHOULD_CALL_PARENT(FALSE) + CRASH("`is_valid()` was not implemented for [type]!") + +/// Returns data to be sent to users in the menu +/datum/preference/proc/compile_ui_data(mob/user, value) + SHOULD_NOT_SLEEP(TRUE) + + return serialize(value) + +/// Returns data compiled into the preferences JSON asset +/datum/preference/proc/compile_constant_data() + SHOULD_NOT_SLEEP(TRUE) + + return null + +/// Returns whether or not this preference is accessible. +/// If FALSE, will not show in the UI and will not be editable (by update_preference). +/datum/preference/proc/is_accessible(datum/preferences/preferences) + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + + if (!isnull(relevant_mutant_bodypart) || !isnull(relevant_species_trait)) + var/species_type = preferences.read_preference(/datum/preference/choiced/species) + + var/datum/species/species = new species_type + if (!(savefile_key in species.get_features())) + return FALSE + + if (!should_show_on_page(preferences.current_window)) + return FALSE + + return TRUE + +/// Returns whether or not, given the PREFERENCE_TAB_*, this preference should +/// appear. +/datum/preference/proc/should_show_on_page(preference_tab) + var/is_on_character_page = preference_tab == PREFERENCE_TAB_CHARACTER_PREFERENCES + var/is_character_preference = savefile_identifier == PREFERENCE_CHARACTER + return is_on_character_page == is_character_preference + +/// A preference that is a choice of one option among a fixed set. +/// Used for preferences such as clothing. +/datum/preference/choiced + /// If this is TRUE, icons will be generated. + /// This is necessary for if your `init_possible_values()` override + /// returns an assoc list of names to atoms/icons. + var/should_generate_icons = FALSE + + var/list/cached_values + + /// If the preference is a main feature (PREFERENCE_CATEGORY_FEATURES or PREFERENCE_CATEGORY_CLOTHING) + /// this is the name of the feature that will be presented. + var/main_feature_name + + abstract_type = /datum/preference/choiced + +/// Returns a list of every possible value. +/// The first time this is called, will run `init_values()`. +/// Return value can be in the form of: +/// - A flat list of raw values, such as list(MALE, FEMALE, PLURAL). +/// - An assoc list of raw values to atoms/icons. +/datum/preference/choiced/proc/get_choices() + // Override `init_values()` instead. + SHOULD_NOT_OVERRIDE(TRUE) + + if (isnull(cached_values)) + cached_values = init_possible_values() + ASSERT(cached_values.len) + + return cached_values + +/// Returns a list of every possible value, serialized. +/// Return value can be in the form of: +/// - A flat list of serialized values, such as list(MALE, FEMALE, PLURAL). +/// - An assoc list of serialized values to atoms/icons. +/datum/preference/choiced/proc/get_choices_serialized() + // Override `init_values()` instead. + SHOULD_NOT_OVERRIDE(TRUE) + + var/list/serialized_choices = list() + var/choices = get_choices() + + if (should_generate_icons) + for (var/choice in choices) + serialized_choices[serialize(choice)] = choices[choice] + else + for (var/choice in choices) + serialized_choices += serialize(choice) + + return serialized_choices + +/// Returns a list of every possible value. +/// This must be overriden by `/datum/preference/choiced` subtypes. +/// Return value can be in the form of: +/// - A flat list of raw values, such as list(MALE, FEMALE, PLURAL). +/// - An assoc list of raw values to atoms/icons, in which case +/// icons will be generated. +/datum/preference/choiced/proc/init_possible_values() + CRASH("`init_possible_values()` was not implemented for [type]!") + +/datum/preference/choiced/is_valid(value) + return value in get_choices() + +/datum/preference/choiced/deserialize(input, datum/preferences/preferences) + return sanitize_inlist(input, get_choices(), create_default_value()) + +/datum/preference/choiced/create_default_value() + return pick(get_choices()) + +/datum/preference/choiced/compile_constant_data() + var/list/data = list() + + var/list/choices = list() + + for (var/choice in get_choices()) + choices += choice + + data["choices"] = choices + + if (should_generate_icons) + var/list/icons = list() + + for (var/choice in choices) + icons[choice] = get_spritesheet_key(choice) + + data["icons"] = icons + + if (!isnull(main_feature_name)) + data["name"] = main_feature_name + + return data + +/// A preference that represents an RGB color of something, crunched down to 3 hex numbers. +/// Was used heavily in the past, but doesn't provide as much range and only barely conserves space. +/datum/preference/color_legacy + abstract_type = /datum/preference/color_legacy + +/datum/preference/color_legacy/deserialize(input, datum/preferences/preferences) + return sanitize_hexcolor(input) + +/datum/preference/color_legacy/create_default_value() + return random_short_color() + +/datum/preference/color_legacy/is_valid(value) + var/static/regex/is_legacy_color = regex(@"^[0-9a-fA-F]{3}$") + return findtext(value, is_legacy_color) + +/// A preference that represents an RGB color of something. +/// Will give the value as 6 hex digits, without a hash. +/datum/preference/color + abstract_type = /datum/preference/color + +/datum/preference/color/deserialize(input, datum/preferences/preferences) + return sanitize_color(input) + +/datum/preference/color/create_default_value() + return random_color() + +/datum/preference/color/is_valid(value) + return findtext(value, GLOB.is_color) + +/// Takes an assoc list of names to /datum/sprite_accessory and returns a value +/// fit for `/datum/preference/init_possible_values()` +/proc/possible_values_for_sprite_accessory_list(list/datum/sprite_accessory/sprite_accessories) + var/list/possible_values = list() + for (var/name in sprite_accessories) + var/datum/sprite_accessory/sprite_accessory = sprite_accessories[name] + if (istype(sprite_accessory)) + possible_values[name] = icon(sprite_accessory.icon, sprite_accessory.icon_state) + else + // This means it didn't have an icon state + possible_values[name] = icon('icons/mob/landmarks.dmi', "x") + return possible_values + +/// Takes an assoc list of names to /datum/sprite_accessory and returns a value +/// fit for `/datum/preference/init_possible_values()` +/// Different from `possible_values_for_sprite_accessory_list` in that it takes a list of layers +/// such as BEHIND, FRONT, and ADJ. +/// It also takes a "body part name", such as body_markings, moth_wings, etc +/// They are expected to be in order from lowest to top. +/proc/possible_values_for_sprite_accessory_list_for_body_part( + list/datum/sprite_accessory/sprite_accessories, + body_part, + list/layers, +) + var/list/possible_values = list() + + for (var/name in sprite_accessories) + var/datum/sprite_accessory/sprite_accessory = sprite_accessories[name] + + var/icon/final_icon + + for (var/layer in layers) + var/icon/icon = icon(sprite_accessory.icon, "m_[body_part]_[sprite_accessory.icon_state]_[layer]") + + if (isnull(final_icon)) + final_icon = icon + else + final_icon.Blend(icon, ICON_OVERLAY) + + possible_values[name] = final_icon + + return possible_values + +/// A numeric preference with a minimum and maximum value +/datum/preference/numeric + /// The minimum value + var/minimum + + /// The maximum value + var/maximum + + /// The step of the number, such as 1 for integers or 0.5 for half-steps. + var/step = 1 + + abstract_type = /datum/preference/numeric + +/datum/preference/numeric/deserialize(input, datum/preferences/preferences) + return sanitize_float(input, minimum, maximum, step, create_default_value()) + +/datum/preference/numeric/serialize(input) + return sanitize_float(input, minimum, maximum, step, create_default_value()) + +/datum/preference/numeric/create_default_value() + return rand(minimum, maximum) + +/datum/preference/numeric/is_valid(value) + return isnum(value) && value >= minimum && value <= maximum + +/datum/preference/numeric/compile_constant_data() + return list( + "minimum" = minimum, + "maximum" = maximum, + "step" = step, + ) + +/// A prefernece whose value is always TRUE or FALSE +/datum/preference/toggle + abstract_type = /datum/preference/toggle + + /// The default value of the toggle, if create_default_value is not specified + var/default_value = TRUE + +/datum/preference/toggle/create_default_value() + return default_value + +/datum/preference/toggle/deserialize(input, datum/preferences/preferences) + return !!input + +/datum/preference/toggle/is_valid(value) + return value == TRUE || value == FALSE diff --git a/code/modules/client/preferences/admin.dm b/code/modules/client/preferences/admin.dm new file mode 100644 index 0000000000000..9cc1026d473c7 --- /dev/null +++ b/code/modules/client/preferences/admin.dm @@ -0,0 +1,41 @@ +/datum/preference/color/asay_color + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "asaycolor" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/color/asay_color/create_default_value() + return DEFAULT_ASAY_COLOR + +/datum/preference/color/asay_color/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + return is_admin(preferences.parent) && CONFIG_GET(flag/allow_admin_asaycolor) + +/// What outfit to equip when spawning as a briefing officer for an ERT +/datum/preference/choiced/brief_outfit + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "brief_outfit" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/choiced/brief_outfit/deserialize(input, datum/preferences/preferences) + var/path = text2path(input) + if (!ispath(path, /datum/outfit)) + return create_default_value() + + return path + +/datum/preference/choiced/brief_outfit/serialize(input) + return "[input]" + +/datum/preference/choiced/brief_outfit/create_default_value() + return /datum/outfit/centcom/commander + +/datum/preference/choiced/brief_outfit/init_possible_values() + return subtypesof(/datum/outfit) + +/datum/preference/choiced/brief_outfit/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + return is_admin(preferences.parent) diff --git a/code/modules/client/preferences/age.dm b/code/modules/client/preferences/age.dm new file mode 100644 index 0000000000000..cad9786ce1fef --- /dev/null +++ b/code/modules/client/preferences/age.dm @@ -0,0 +1,10 @@ +/datum/preference/numeric/age + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + savefile_key = "age" + savefile_identifier = PREFERENCE_CHARACTER + + minimum = AGE_MIN + maximum = AGE_MAX + +/datum/preference/numeric/age/apply_to_human(mob/living/carbon/human/target, value) + target.age = value diff --git a/code/modules/client/preferences/ai_core_display.dm b/code/modules/client/preferences/ai_core_display.dm new file mode 100644 index 0000000000000..dab61f2b323f3 --- /dev/null +++ b/code/modules/client/preferences/ai_core_display.dm @@ -0,0 +1,25 @@ +/// What to show on the AI screen +/datum/preference/choiced/ai_core_display + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "preferred_ai_core_display" + should_generate_icons = TRUE + +/datum/preference/choiced/ai_core_display/init_possible_values() + var/list/values = list() + + values["Random"] = icon('icons/mob/ai.dmi', "ai-empty") + + for (var/screen in GLOB.ai_core_display_screens - "Portrait" - "Random") + values[screen] = icon('icons/mob/ai.dmi', resolve_ai_icon_sync(screen)) + + return values + +/datum/preference/choiced/ai_core_display/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + return istype(preferences.get_highest_priority_job(), /datum/job/ai) + +/datum/preference/choiced/ai_core_display/apply_to_human(mob/living/carbon/human/target, value) + return diff --git a/code/modules/client/preferences/ambient_occlusion.dm b/code/modules/client/preferences/ambient_occlusion.dm new file mode 100644 index 0000000000000..a81efca00bd8f --- /dev/null +++ b/code/modules/client/preferences/ambient_occlusion.dm @@ -0,0 +1,12 @@ +/// Whether or not to toggle ambient occlusion, the shadows around people +/datum/preference/toggle/ambient_occlusion + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "ambientocclusion" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/toggle/ambient_occlusion/apply_to_client(client/client, value) + var/atom/movable/screen/plane_master/game_world/plane_master = locate() in client?.screen + if (!plane_master) + return + + plane_master.backdrop(client.mob) diff --git a/code/modules/client/preferences/assets.dm b/code/modules/client/preferences/assets.dm new file mode 100644 index 0000000000000..de29b6e4fbdeb --- /dev/null +++ b/code/modules/client/preferences/assets.dm @@ -0,0 +1,65 @@ +/// Assets generated from `/datum/preference` icons +/datum/asset/spritesheet/preferences + name = "preferences" + early = TRUE + cross_round_cachable = TRUE + +/datum/asset/spritesheet/preferences/create_spritesheets() + var/list/to_insert = list() + + for (var/preference_key in GLOB.preference_entries_by_key) + var/datum/preference/choiced/preference = GLOB.preference_entries_by_key[preference_key] + if (!istype(preference)) + continue + + if (!preference.should_generate_icons) + continue + + var/list/choices = preference.get_choices_serialized() + for (var/preference_value in choices) + var/create_icon_of = choices[preference_value] + + var/icon/icon + var/icon_state + + if (ispath(create_icon_of, /atom)) + var/atom/atom_icon_source = create_icon_of + icon = initial(atom_icon_source.icon) + icon_state = initial(atom_icon_source.icon_state) + else if (isicon(create_icon_of)) + icon = create_icon_of + else + CRASH("[create_icon_of] is an invalid preference value (from [preference_key]:[preference_value]).") + + to_insert[preference.get_spritesheet_key(preference_value)] = list(icon, icon_state) + + for (var/spritesheet_key in to_insert) + var/list/inserting = to_insert[spritesheet_key] + Insert(spritesheet_key, inserting[1], inserting[2]) + +/// Returns the key that will be used in the spritesheet for a given value. +/datum/preference/proc/get_spritesheet_key(value) + return "[savefile_key]___[sanitize_css_class_name(value)]" + +/// Sends information needed for shared details on individual preferences +/datum/asset/json/preferences + name = "preferences" + +/datum/asset/json/preferences/generate() + var/list/preference_data = list() + + for (var/middleware_type in subtypesof(/datum/preference_middleware)) + var/datum/preference_middleware/middleware = new middleware_type + var/data = middleware.get_constant_data() + if (!isnull(data)) + preference_data[middleware.key] = data + + qdel(middleware) + + for (var/preference_type in GLOB.preference_entries) + var/datum/preference/preference_entry = GLOB.preference_entries[preference_type] + var/data = preference_entry.compile_constant_data() + if (!isnull(data)) + preference_data[preference_entry.savefile_key] = data + + return preference_data diff --git a/code/modules/client/preferences/auto_fit_viewport.dm b/code/modules/client/preferences/auto_fit_viewport.dm new file mode 100644 index 0000000000000..3550af054549a --- /dev/null +++ b/code/modules/client/preferences/auto_fit_viewport.dm @@ -0,0 +1,7 @@ +/datum/preference/toggle/auto_fit_viewport + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "auto_fit_viewport" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/toggle/auto_fit_viewport/apply_to_client_updated(client/client, value) + INVOKE_ASYNC(client, /client/verb/fit_viewport) diff --git a/code/modules/client/preferences/body_type.dm b/code/modules/client/preferences/body_type.dm new file mode 100644 index 0000000000000..81c6b3e9baa73 --- /dev/null +++ b/code/modules/client/preferences/body_type.dm @@ -0,0 +1,21 @@ +/datum/preference/choiced/body_type + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + priority = PREFERENCE_PRIORITY_BODY_TYPE + savefile_key = "body_type" + savefile_identifier = PREFERENCE_CHARACTER + +/datum/preference/choiced/body_type/init_possible_values() + return list(MALE, FEMALE) + +/datum/preference/choiced/body_type/apply_to_human(mob/living/carbon/human/target, value) + if (target.gender != MALE && target.gender != FEMALE) + target.body_type = value + else + target.body_type = target.gender + +/datum/preference/choiced/body_type/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + var/gender = preferences.read_preference(/datum/preference/choiced/gender) + return gender != MALE && gender != FEMALE diff --git a/code/modules/client/preferences/broadcast_login_logout.dm b/code/modules/client/preferences/broadcast_login_logout.dm new file mode 100644 index 0000000000000..90250118d2a0e --- /dev/null +++ b/code/modules/client/preferences/broadcast_login_logout.dm @@ -0,0 +1,5 @@ +/// Whether or not to announce when the player logs in or out. +/datum/preference/toggle/broadcast_login_logout + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "broadcast_login_logout" + savefile_identifier = PREFERENCE_PLAYER diff --git a/code/modules/client/preferences/buttons_locked.dm b/code/modules/client/preferences/buttons_locked.dm new file mode 100644 index 0000000000000..b4f54ff10f8f9 --- /dev/null +++ b/code/modules/client/preferences/buttons_locked.dm @@ -0,0 +1,5 @@ +/datum/preference/toggle/buttons_locked + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "buttons_locked" + savefile_identifier = PREFERENCE_PLAYER + default_value = FALSE diff --git a/code/modules/client/preferences/clothing.dm b/code/modules/client/preferences/clothing.dm new file mode 100644 index 0000000000000..8dca08d090a37 --- /dev/null +++ b/code/modules/client/preferences/clothing.dm @@ -0,0 +1,151 @@ +/proc/generate_values_for_underwear(list/accessory_list, list/icons, color) + var/icon/lower_half = icon('icons/blanks/32x32.dmi', "nothing") + + for (var/icon in icons) + lower_half.Blend(icon('icons/mob/human_parts_greyscale.dmi', icon), ICON_OVERLAY) + + var/list/values = list() + + for (var/accessory_name in accessory_list) + var/icon/icon_with_socks = new(lower_half) + + if (accessory_name != "Nude") + var/datum/sprite_accessory/accessory = accessory_list[accessory_name] + + var/icon/accessory_icon = icon('icons/mob/clothing/underwear.dmi', accessory.icon_state) + if (color && !accessory.use_static) + accessory_icon.Blend(color, ICON_MULTIPLY) + icon_with_socks.Blend(accessory_icon, ICON_OVERLAY) + + icon_with_socks.Crop(10, 1, 22, 13) + icon_with_socks.Scale(32, 32) + + values[accessory_name] = icon_with_socks + + return values + +/// Backpack preference +/datum/preference/choiced/backpack + savefile_key = "backpack" + savefile_identifier = PREFERENCE_CHARACTER + main_feature_name = "Backpack" + category = PREFERENCE_CATEGORY_CLOTHING + should_generate_icons = TRUE + +/datum/preference/choiced/backpack/init_possible_values() + var/list/values = list() + + values[GBACKPACK] = /obj/item/storage/backpack + values[GSATCHEL] = /obj/item/storage/backpack/satchel + values[LSATCHEL] = /obj/item/storage/backpack/satchel/leather + values[GDUFFELBAG] = /obj/item/storage/backpack/duffelbag + + // In a perfect world, these would be your department's backpack. + // However, this doesn't factor in assistants, or no high slot, and would + // also increase the spritesheet size a lot. + // I play medical doctor, and so medical doctor you get. + values[DBACKPACK] = /obj/item/storage/backpack/medic + values[DSATCHEL] = /obj/item/storage/backpack/satchel/med + values[DDUFFELBAG] = /obj/item/storage/backpack/duffelbag/med + + return values + +/datum/preference/choiced/backpack/apply_to_human(mob/living/carbon/human/target, value) + target.backpack = value + +/// Jumpsuit preference +/datum/preference/choiced/jumpsuit + savefile_key = "jumpsuit_style" + savefile_identifier = PREFERENCE_CHARACTER + main_feature_name = "Jumpsuit" + category = PREFERENCE_CATEGORY_CLOTHING + should_generate_icons = TRUE + +/datum/preference/choiced/jumpsuit/init_possible_values() + var/list/values = list() + + values[PREF_SUIT] = /obj/item/clothing/under/color/grey + values[PREF_SKIRT] = /obj/item/clothing/under/color/jumpskirt/grey + + return values + +/datum/preference/choiced/jumpsuit/apply_to_human(mob/living/carbon/human/target, value) + target.jumpsuit_style = value + +/// Socks preference +/datum/preference/choiced/socks + savefile_key = "socks" + savefile_identifier = PREFERENCE_CHARACTER + main_feature_name = "Socks" + category = PREFERENCE_CATEGORY_CLOTHING + should_generate_icons = TRUE + +/datum/preference/choiced/socks/init_possible_values() + return generate_values_for_underwear(GLOB.socks_list, list("human_r_leg", "human_l_leg")) + +/datum/preference/choiced/socks/apply_to_human(mob/living/carbon/human/target, value) + target.socks = value + +/// Undershirt preference +/datum/preference/choiced/undershirt + savefile_key = "undershirt" + savefile_identifier = PREFERENCE_CHARACTER + main_feature_name = "Undershirt" + category = PREFERENCE_CATEGORY_CLOTHING + should_generate_icons = TRUE + +/datum/preference/choiced/undershirt/init_possible_values() + var/icon/body = icon('icons/mob/human_parts_greyscale.dmi', "human_r_leg") + body.Blend(icon('icons/mob/human_parts_greyscale.dmi', "human_l_leg"), ICON_OVERLAY) + body.Blend(icon('icons/mob/human_parts_greyscale.dmi', "human_r_arm"), ICON_OVERLAY) + body.Blend(icon('icons/mob/human_parts_greyscale.dmi', "human_l_arm"), ICON_OVERLAY) + body.Blend(icon('icons/mob/human_parts_greyscale.dmi', "human_r_hand"), ICON_OVERLAY) + body.Blend(icon('icons/mob/human_parts_greyscale.dmi', "human_l_hand"), ICON_OVERLAY) + body.Blend(icon('icons/mob/human_parts_greyscale.dmi', "human_chest_m"), ICON_OVERLAY) + + var/list/values = list() + + for (var/accessory_name in GLOB.undershirt_list) + var/icon/icon_with_undershirt = icon(body) + + if (accessory_name != "Nude") + var/datum/sprite_accessory/accessory = GLOB.undershirt_list[accessory_name] + icon_with_undershirt.Blend(icon('icons/mob/clothing/underwear.dmi', accessory.icon_state), ICON_OVERLAY) + + icon_with_undershirt.Crop(9, 9, 23, 23) + icon_with_undershirt.Scale(32, 32) + values[accessory_name] = icon_with_undershirt + + return values + +/datum/preference/choiced/undershirt/apply_to_human(mob/living/carbon/human/target, value) + target.undershirt = value + +/// Underwear preference +/datum/preference/choiced/underwear + savefile_key = "underwear" + savefile_identifier = PREFERENCE_CHARACTER + main_feature_name = "Underwear" + category = PREFERENCE_CATEGORY_CLOTHING + should_generate_icons = TRUE + +/datum/preference/choiced/underwear/init_possible_values() + return generate_values_for_underwear(GLOB.underwear_list, list("human_chest_m", "human_r_leg", "human_l_leg"), COLOR_ALMOST_BLACK) + +/datum/preference/choiced/underwear/apply_to_human(mob/living/carbon/human/target, value) + target.underwear = value + +/datum/preference/choiced/underwear/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + var/species_type = preferences.read_preference(/datum/preference/choiced/species) + var/datum/species/species = new species_type + return !(NO_UNDERWEAR in species.species_traits) + +/datum/preference/choiced/underwear/compile_constant_data() + var/list/data = ..() + + data[SUPPLEMENTAL_FEATURE_KEY] = "underwear_color" + + return data diff --git a/code/modules/client/preferences/darkened_flash.dm b/code/modules/client/preferences/darkened_flash.dm new file mode 100644 index 0000000000000..ef89467bf357a --- /dev/null +++ b/code/modules/client/preferences/darkened_flash.dm @@ -0,0 +1,6 @@ +/// When toggled, being flashed will show a dark screen rather than a light one. +/datum/preference/toggle/darkened_flash + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + default_value = FALSE + savefile_key = "darkened_flash" + savefile_identifier = PREFERENCE_PLAYER diff --git a/code/modules/client/preferences/fps.dm b/code/modules/client/preferences/fps.dm new file mode 100644 index 0000000000000..8bd1310932be7 --- /dev/null +++ b/code/modules/client/preferences/fps.dm @@ -0,0 +1,17 @@ +/datum/preference/numeric/fps + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "clientfps" + savefile_identifier = PREFERENCE_PLAYER + + minimum = -1 + maximum = 240 + +/datum/preference/numeric/fps/apply_to_client(client/client, value) + client.fps = (value < 0) ? RECOMMENDED_FPS : value + +/datum/preference/numeric/fps/compile_constant_data() + var/list/data = ..() + + data["recommended_fps"] = RECOMMENDED_FPS + + return data diff --git a/code/modules/client/preferences/gender.dm b/code/modules/client/preferences/gender.dm new file mode 100644 index 0000000000000..a9400465b6708 --- /dev/null +++ b/code/modules/client/preferences/gender.dm @@ -0,0 +1,11 @@ +/// Gender preference +/datum/preference/choiced/gender + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "gender" + priority = PREFERENCE_PRIORITY_GENDER + +/datum/preference/choiced/gender/init_possible_values() + return list(MALE, FEMALE, PLURAL) + +/datum/preference/choiced/gender/apply_to_human(mob/living/carbon/human/target, value) + target.gender = value diff --git a/code/modules/client/preferences/ghost.dm b/code/modules/client/preferences/ghost.dm new file mode 100644 index 0000000000000..ce8e05235ee4c --- /dev/null +++ b/code/modules/client/preferences/ghost.dm @@ -0,0 +1,178 @@ +/// Determines what accessories your ghost will look like they have. +/datum/preference/choiced/ghost_accessories + savefile_key = "ghost_accs" + savefile_identifier = PREFERENCE_PLAYER + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + +/datum/preference/choiced/ghost_accessories/init_possible_values() + return list(GHOST_ACCS_NONE, GHOST_ACCS_DIR, GHOST_ACCS_FULL) + +/datum/preference/choiced/ghost_accessories/create_default_value() + return GHOST_ACCS_DEFAULT_OPTION + +/datum/preference/choiced/ghost_accessories/apply_to_client(client/client, value) + var/mob/dead/observer/ghost = client.mob + if (!istype(ghost)) + return + + ghost.ghost_accs = value + ghost.update_appearance() + +/datum/preference/choiced/ghost_accessories/deserialize(input, datum/preferences/preferences) + // Old ghost preferences used to be 1/50/100. + // Whoever did that wasted an entire day of my time trying to get those sent + // properly, so I'm going to buck them. + if (isnum(input)) + switch (input) + if (1) + input = GHOST_ACCS_NONE + if (50) + input = GHOST_ACCS_DIR + if (100) + input = GHOST_ACCS_FULL + + return ..(input) + +/// Determines the appearance of your ghost to others, when you are a BYOND member +/datum/preference/choiced/ghost_form + savefile_key = "ghost_form" + savefile_identifier = PREFERENCE_PLAYER + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + should_generate_icons = TRUE + + var/static/list/ghost_forms = list( + "catghost" = "Cat", + "ghost" = "Default", + "ghost_black" = "Black", + "ghost_blazeit" = "Blaze it", + "ghost_blue" = "Blue", + "ghost_camo" = "Camo", + "ghost_cyan" = "Cyan", + "ghost_dblue" = "Dark blue", + "ghost_dcyan" = "Dark cyan", + "ghost_dgreen" = "Dark green", + "ghost_dpink" = "Dark pink", + "ghost_dred" = "Dark red", + "ghost_dyellow" = "Dark yellow", + "ghost_fire" = "Fire", + "ghost_funkypurp" = "Funky purple", + "ghost_green" = "Green", + "ghost_grey" = "Grey", + "ghost_mellow" = "Mellow", + "ghost_pink" = "Pink", + "ghost_pinksherbert" = "Pink Sherbert", + "ghost_purpleswirl" = "Purple Swirl", + "ghost_rainbow" = "Rainbow", + "ghost_red" = "Red", + "ghost_yellow" = "Yellow", + "ghostian2" = "Ian", + "ghostking" = "King", + "skeleghost" = "Skeleton", + ) + +/datum/preference/choiced/ghost_form/init_possible_values() + var/list/values = list() + + for (var/ghost_form in ghost_forms) + values[ghost_form] = icon('icons/mob/mob.dmi', ghost_form) + + return values + +/datum/preference/choiced/ghost_form/create_default_value() + return "ghost" + +/datum/preference/choiced/ghost_form/apply_to_client(client/client, value) + var/mob/dead/observer/ghost = client.mob + if (!istype(ghost)) + return + + if (!client.is_content_unlocked()) + return + + ghost.update_icon(ALL, value) + +/datum/preference/choiced/ghost_form/compile_constant_data() + var/list/data = ..() + + data[CHOICED_PREFERENCE_DISPLAY_NAMES] = ghost_forms + + return data + +/// Toggles the HUD for ghosts +/datum/preference/toggle/ghost_hud + savefile_key = "ghost_hud" + savefile_identifier = PREFERENCE_PLAYER + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + +/datum/preference/toggle/ghost_hud/apply_to_client(client/client, value) + if (isobserver(client?.mob)) + client?.mob.hud_used?.show_hud() + +/// Determines what ghosts orbiting look like to you. +/datum/preference/choiced/ghost_orbit + savefile_key = "ghost_orbit" + savefile_identifier = PREFERENCE_PLAYER + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + +/datum/preference/choiced/ghost_orbit/init_possible_values() + return list( + GHOST_ORBIT_CIRCLE, + GHOST_ORBIT_TRIANGLE, + GHOST_ORBIT_SQUARE, + GHOST_ORBIT_HEXAGON, + GHOST_ORBIT_PENTAGON, + ) + +/datum/preference/choiced/ghost_orbit/apply_to_client(client/client, value) + var/mob/dead/observer/ghost = client.mob + if (!istype(ghost)) + return + + if (!client.is_content_unlocked()) + return + + ghost.ghost_orbit = value + +/// Determines how to show other ghosts +/datum/preference/choiced/ghost_others + savefile_key = "ghost_others" + savefile_identifier = PREFERENCE_PLAYER + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + +/datum/preference/choiced/ghost_others/init_possible_values() + return list( + GHOST_OTHERS_SIMPLE, + GHOST_OTHERS_DEFAULT_SPRITE, + GHOST_OTHERS_THEIR_SETTING, + ) + +/datum/preference/choiced/ghost_others/create_default_value() + return GHOST_OTHERS_DEFAULT_OPTION + +/datum/preference/choiced/ghost_others/apply_to_client(client/client, value) + var/mob/dead/observer/ghost = client.mob + if (!istype(ghost)) + return + + ghost.update_sight() + +/datum/preference/choiced/ghost_others/deserialize(input, datum/preferences/preferences) + // Old ghost preferences used to be 1/50/100. + // Whoever did that wasted an entire day of my time trying to get those sent + // properly, so I'm going to buck them. + if (isnum(input)) + switch (input) + if (1) + input = GHOST_OTHERS_SIMPLE + if (50) + input = GHOST_OTHERS_DEFAULT_SPRITE + if (100) + input = GHOST_OTHERS_THEIR_SETTING + + return ..(input, preferences) + +/// Whether or not ghosts can examine things by clicking on them. +/datum/preference/toggle/inquisitive_ghost + savefile_key = "inquisitive_ghost" + savefile_identifier = PREFERENCE_PLAYER + category = PREFERENCE_CATEGORY_GAME_PREFERENCES diff --git a/code/modules/client/preferences/hotkeys.dm b/code/modules/client/preferences/hotkeys.dm new file mode 100644 index 0000000000000..b96b68286d60a --- /dev/null +++ b/code/modules/client/preferences/hotkeys.dm @@ -0,0 +1,7 @@ +/datum/preference/toggle/hotkeys + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "hotkeys" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/toggle/hotkeys/apply_to_client(client/client, value) + client.hotkeys = value diff --git a/code/modules/client/preferences/item_outlines.dm b/code/modules/client/preferences/item_outlines.dm new file mode 100644 index 0000000000000..74b8996617345 --- /dev/null +++ b/code/modules/client/preferences/item_outlines.dm @@ -0,0 +1,4 @@ +/datum/preference/toggle/item_outlines + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "itemoutline_pref" + savefile_identifier = PREFERENCE_PLAYER diff --git a/code/modules/client/preferences/jobless_role.dm b/code/modules/client/preferences/jobless_role.dm new file mode 100644 index 0000000000000..8514451fb71dc --- /dev/null +++ b/code/modules/client/preferences/jobless_role.dm @@ -0,0 +1,12 @@ +/datum/preference/choiced/jobless_role + savefile_key = "joblessrole" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/choiced/jobless_role/create_default_value() + return BERANDOMJOB + +/datum/preference/choiced/jobless_role/init_possible_values() + return list(BEOVERFLOW, BERANDOMJOB, RETURNTOLOBBY) + +/datum/preference/choiced/jobless_role/should_show_on_page(preference_tab) + return preference_tab == PREFERENCE_TAB_CHARACTER_PREFERENCES diff --git a/code/modules/client/preferences/middleware/_middleware.dm b/code/modules/client/preferences/middleware/_middleware.dm new file mode 100644 index 0000000000000..8f47f73642c80 --- /dev/null +++ b/code/modules/client/preferences/middleware/_middleware.dm @@ -0,0 +1,52 @@ +/// Preference middleware is code that helps to decentralize complicated preference features. +/datum/preference_middleware + /// The preferences datum + var/datum/preferences/preferences + + /// The key that will be used for get_constant_data(). + /// If null, will use the typepath minus /datum/preference_middleware. + var/key = null + + /// Map of ui_act actions -> proc paths to call. + /// Signature is `(list/params, mob/user) -> TRUE/FALSE. + /// Return output is the same as ui_act--TRUE if it should update, FALSE if it should not + var/list/action_delegations = list() + +/datum/preference_middleware/New(datum/preferences) + src.preferences = preferences + + if (isnull(key)) + // + 2 coming from the off-by-one of copytext, and then another from the slash + key = copytext("[type]", length("[parent_type]") + 2) + +/datum/preference_middleware/Destroy() + preferences = null + return ..() + +/// Append all of these into ui_data +/datum/preference_middleware/proc/get_ui_data(mob/user) + return list() + +/// Append all of these into ui_static_data +/datum/preference_middleware/proc/get_ui_static_data(mob/user) + return list() + +/// Append all of these into ui_assets +/datum/preference_middleware/proc/get_ui_assets() + return list() + +/// Append all of these into /datum/asset/json/preferences. +/datum/preference_middleware/proc/get_constant_data() + return null + +/// Merge this into the result of compile_character_preferences. +/datum/preference_middleware/proc/get_character_preferences(mob/user) + return null + +/// Called every set_preference, returns TRUE if this handled it. +/datum/preference_middleware/proc/pre_set_preference(mob/user, preference, value) + return FALSE + +/// Called when a character is changed. +/datum/preference_middleware/proc/on_new_character(mob/user) + return diff --git a/code/modules/client/preferences/middleware/antags.dm b/code/modules/client/preferences/middleware/antags.dm new file mode 100644 index 0000000000000..49ed275f0fdc1 --- /dev/null +++ b/code/modules/client/preferences/middleware/antags.dm @@ -0,0 +1,162 @@ +/datum/preference_middleware/antags + action_delegations = list( + "set_antags" = .proc/set_antags, + ) + +/datum/preference_middleware/antags/get_ui_static_data(mob/user) + if (preferences.current_window != PREFERENCE_TAB_CHARACTER_PREFERENCES) + return list() + + var/list/data = list() + + var/list/selected_antags = list() + + for (var/antag in preferences.be_special) + selected_antags += serialize_antag_name(antag) + + data["selected_antags"] = selected_antags + + var/list/antag_bans = get_antag_bans() + if (antag_bans.len) + data["antag_bans"] = antag_bans + + var/list/antag_days_left = get_antag_days_left() + if (antag_days_left?.len) + data["antag_days_left"] = antag_days_left + + return data + +/datum/preference_middleware/antags/get_ui_assets() + return list( + get_asset_datum(/datum/asset/spritesheet/antagonists), + ) + +/datum/preference_middleware/antags/proc/set_antags(list/params, mob/user) + SHOULD_NOT_SLEEP(TRUE) + + var/sent_antags = params["antags"] + var/toggled = params["toggled"] + + var/antags = list() + + var/serialized_antags = get_serialized_antags() + + for (var/sent_antag in sent_antags) + var/special_role = serialized_antags[sent_antag] + if (!special_role) + continue + + antags += special_role + + if (toggled) + preferences.be_special |= antags + else + preferences.be_special -= antags + + // This is predicted on the client + return FALSE + +/datum/preference_middleware/antags/proc/get_antag_bans() + var/list/antag_bans = list() + + for (var/datum/dynamic_ruleset/dynamic_ruleset as anything in subtypesof(/datum/dynamic_ruleset)) + var/antag_flag = initial(dynamic_ruleset.antag_flag) + var/antag_flag_override = initial(dynamic_ruleset.antag_flag_override) + + if (isnull(antag_flag)) + continue + + if (is_banned_from(preferences.parent.ckey, list(antag_flag_override || antag_flag, ROLE_SYNDICATE))) + antag_bans += serialize_antag_name(antag_flag) + + return antag_bans + +/datum/preference_middleware/antags/proc/get_antag_days_left() + if (!CONFIG_GET(flag/use_age_restriction_for_jobs)) + return + + var/list/antag_days_left = list() + + for (var/datum/dynamic_ruleset/dynamic_ruleset as anything in subtypesof(/datum/dynamic_ruleset)) + var/antag_flag = initial(dynamic_ruleset.antag_flag) + var/antag_flag_override = initial(dynamic_ruleset.antag_flag_override) + + if (isnull(antag_flag)) + continue + + var/days_needed = preferences.parent?.get_remaining_days( + GLOB.special_roles[antag_flag_override || antag_flag] + ) + + if (days_needed > 0) + antag_days_left[serialize_antag_name(antag_flag)] = days_needed + + return antag_days_left + +/datum/preference_middleware/antags/proc/get_serialized_antags() + var/list/serialized_antags + + if (isnull(serialized_antags)) + serialized_antags = list() + + for (var/special_role in GLOB.special_roles) + serialized_antags[serialize_antag_name(special_role)] = special_role + + return serialized_antags + +/// Sprites generated for the antagonists panel +/datum/asset/spritesheet/antagonists + name = "antagonists" + early = TRUE + cross_round_cachable = TRUE + +/datum/asset/spritesheet/antagonists/create_spritesheets() + // Antagonists that don't have a dynamic ruleset, but do have a preference + var/static/list/non_ruleset_antagonists = list( + ROLE_LONE_OPERATIVE = /datum/antagonist/nukeop/lone, + ) + + var/list/antagonists = non_ruleset_antagonists.Copy() + + for (var/datum/dynamic_ruleset/ruleset as anything in subtypesof(/datum/dynamic_ruleset)) + var/datum/antagonist/antagonist_type = initial(ruleset.antag_datum) + if (isnull(antagonist_type)) + continue + + // antag_flag is guaranteed to be unique by unit tests. + antagonists[initial(ruleset.antag_flag)] = antagonist_type + + var/list/generated_icons = list() + var/list/to_insert = list() + + for (var/antag_flag in antagonists) + var/datum/antagonist/antagonist_type = antagonists[antag_flag] + + // antag_flag is guaranteed to be unique by unit tests. + var/spritesheet_key = serialize_antag_name(antag_flag) + + if (!isnull(generated_icons[antagonist_type])) + to_insert[spritesheet_key] = generated_icons[antagonist_type] + continue + + var/datum/antagonist/antagonist = new antagonist_type + var/icon/preview_icon = antagonist.get_preview_icon() + + if (isnull(preview_icon)) + continue + + qdel(antagonist) + + // preview_icons are not scaled at this stage INTENTIONALLY. + // If an icon is not prepared to be scaled to that size, it looks really ugly, and this + // makes it harder to figure out what size it *actually* is. + generated_icons[antagonist_type] = preview_icon + to_insert[spritesheet_key] = preview_icon + + for (var/spritesheet_key in to_insert) + Insert(spritesheet_key, to_insert[spritesheet_key]) + +/// Serializes an antag name to be used for preferences UI +/proc/serialize_antag_name(antag_name) + // These are sent through CSS, so they need to be safe to use as class names. + return lowertext(sanitize_css_class_name(antag_name)) diff --git a/code/modules/client/preferences/middleware/jobs.dm b/code/modules/client/preferences/middleware/jobs.dm new file mode 100644 index 0000000000000..50e8b4b0d4581 --- /dev/null +++ b/code/modules/client/preferences/middleware/jobs.dm @@ -0,0 +1,82 @@ +/datum/preference_middleware/jobs + action_delegations = list( + "set_job_preference" = .proc/set_job_preference, + ) + +/datum/preference_middleware/jobs/proc/set_job_preference(list/params, mob/user) + var/job_title = params["job"] + var/level = params["level"] + + if (level != null && level != JP_LOW && level != JP_MEDIUM && level != JP_HIGH) + return FALSE + + var/datum/job/job = SSjob.GetJob(job_title) + + if (isnull(job)) + return FALSE + + if (job.faction != FACTION_STATION) + return FALSE + + if (!preferences.set_job_preference_level(job, level)) + return FALSE + + preferences.character_preview_view?.update_body() + + return TRUE + +/datum/preference_middleware/jobs/get_ui_data(mob/user) + var/list/data = list() + + data["job_preferences"] = preferences.job_preferences + + return data + +/datum/preference_middleware/jobs/get_ui_static_data(mob/user) + var/list/data = list() + + var/list/required_job_playtime = get_required_job_playtime(user) + if (!isnull(required_job_playtime)) + data += required_job_playtime + + var/list/job_bans = get_job_bans(user) + if (job_bans.len) + data["job_bans"] = job_bans + + return data.len > 0 ? data : null + +/datum/preference_middleware/jobs/proc/get_required_job_playtime(mob/user) + var/list/data = list() + + var/list/job_days_left = list() + var/list/job_required_experience = list() + + for (var/datum/job/job as anything in SSjob.all_occupations) + var/required_playtime_remaining = job.required_playtime_remaining(user.client) + if (required_playtime_remaining) + job_required_experience[job.title] = list( + "experience_type" = job.get_exp_req_type(), + "required_playtime" = required_playtime_remaining, + ) + + continue + + if (!job.player_old_enough(user.client)) + job_days_left[job.title] = job.available_in_days(user.client) + + if (job_days_left.len) + data["job_days_left"] = job_days_left + + if (job_required_experience) + data["job_required_experience"] = job_required_experience + + return data + +/datum/preference_middleware/jobs/proc/get_job_bans(mob/user) + var/list/data = list() + + for (var/datum/job/job as anything in SSjob.all_occupations) + if (is_banned_from(user.client?.ckey, job.title)) + data += job.title + + return data diff --git a/code/modules/client/preferences/middleware/keybindings.dm b/code/modules/client/preferences/middleware/keybindings.dm new file mode 100644 index 0000000000000..e51852b408b83 --- /dev/null +++ b/code/modules/client/preferences/middleware/keybindings.dm @@ -0,0 +1,89 @@ +#define MAX_HOTKEY_SLOTS 3 + +/// Middleware to handle keybindings +/datum/preference_middleware/keybindings + action_delegations = list( + "reset_keybinds_to_defaults" = .proc/reset_keybinds_to_defaults, + "set_keybindings" = .proc/set_keybindings, + ) + +/datum/preference_middleware/keybindings/get_ui_static_data(mob/user) + if (preferences.current_window == PREFERENCE_TAB_CHARACTER_PREFERENCES) + return list() + + var/list/keybindings = preferences.key_bindings + + return list( + "keybindings" = keybindings, + ) + +/datum/preference_middleware/keybindings/get_ui_assets() + return list( + get_asset_datum(/datum/asset/json/keybindings) + ) + +/datum/preference_middleware/keybindings/proc/reset_keybinds_to_defaults(list/params, mob/user) + var/keybind_name = params["keybind_name"] + var/datum/keybinding/keybinding = GLOB.keybindings_by_name[keybind_name] + + if (isnull(keybinding)) + return FALSE + + preferences.key_bindings[keybind_name] = preferences.parent.hotkeys ? keybinding.hotkey_keys : keybinding.classic_keys + preferences.key_bindings_by_key = preferences.get_key_bindings_by_key(preferences.key_bindings) + + preferences.update_static_data(user) + + return TRUE + +/datum/preference_middleware/keybindings/proc/set_keybindings(list/params) + var/keybind_name = params["keybind_name"] + + if (isnull(GLOB.keybindings_by_name[keybind_name])) + return FALSE + + var/list/raw_hotkeys = params["hotkeys"] + if (!istype(raw_hotkeys)) + return FALSE + + if (raw_hotkeys.len > MAX_HOTKEY_SLOTS) + return FALSE + + // There's no optimal, easy way to check if something is an array + // and not an object in BYOND, so just sanitize it to make sure. + var/list/hotkeys = list() + for (var/hotkey in raw_hotkeys) + if (!istext(hotkey)) + return FALSE + + // Fairly arbitrary number, it's just so you don't save enormous fake keybinds. + if (length(hotkey) > 100) + return FALSE + + hotkeys += hotkey + + preferences.key_bindings[keybind_name] = hotkeys + preferences.key_bindings_by_key = preferences.get_key_bindings_by_key(preferences.key_bindings) + + return TRUE + +/datum/asset/json/keybindings + name = "keybindings" + +/datum/asset/json/keybindings/generate() + var/list/keybindings = list() + + for (var/name in GLOB.keybindings_by_name) + var/datum/keybinding/keybinding = GLOB.keybindings_by_name[name] + + if (!(keybinding.category in keybindings)) + keybindings[keybinding.category] = list() + + keybindings[keybinding.category][keybinding.name] = list( + "name" = keybinding.full_name, + "description" = keybinding.description, + ) + + return keybindings + +#undef MAX_HOTKEY_SLOTS diff --git a/code/modules/client/preferences/middleware/legacy_toggles.dm b/code/modules/client/preferences/middleware/legacy_toggles.dm new file mode 100644 index 0000000000000..e35832e543440 --- /dev/null +++ b/code/modules/client/preferences/middleware/legacy_toggles.dm @@ -0,0 +1,132 @@ +/// In the before times, toggles were all stored in one bitfield. +/// In order to preserve this existing data (and code) without massive +/// migrations, this middleware attempts to handle this in a way +/// transparent to the preferences UI itself. +/// In the future, the existing toggles data should just be migrated to +/// individual `/datum/preference/toggle`s. +/datum/preference_middleware/legacy_toggles + // DO NOT ADD ANY NEW TOGGLES HERE! + // Use `/datum/preference/toggle` instead. + var/static/list/legacy_toggles = list( + "admin_ignore_cult_ghost" = ADMIN_IGNORE_CULT_GHOST, + "announce_login" = ANNOUNCE_LOGIN, + "combohud_lighting" = COMBOHUD_LIGHTING, + "deadmin_always" = DEADMIN_ALWAYS, + "deadmin_antagonist" = DEADMIN_ANTAGONIST, + "deadmin_position_head" = DEADMIN_POSITION_HEAD, + "deadmin_position_security" = DEADMIN_POSITION_SECURITY, + "deadmin_position_silicon" = DEADMIN_POSITION_SILICON, + "disable_arrivalrattle" = DISABLE_ARRIVALRATTLE, + "disable_deathrattle" = DISABLE_DEATHRATTLE, + "member_public" = MEMBER_PUBLIC, + "sound_adminhelp" = SOUND_ADMINHELP, + "sound_ambience" = SOUND_AMBIENCE, + "sound_announcements" = SOUND_ANNOUNCEMENTS, + "sound_combatmode" = SOUND_COMBATMODE, + "sound_endofround" = SOUND_ENDOFROUND, + "sound_instruments" = SOUND_INSTRUMENTS, + "sound_lobby" = SOUND_LOBBY, + "sound_midi" = SOUND_MIDI, + "sound_prayers" = SOUND_PRAYERS, + "sound_ship_ambience" = SOUND_SHIP_AMBIENCE, + "split_admin_tabs" = SPLIT_ADMIN_TABS, + ) + + var/list/legacy_chat_toggles = list( + "chat_bankcard" = CHAT_BANKCARD, + "chat_dead" = CHAT_DEAD, + "chat_ghostears" = CHAT_GHOSTEARS, + "chat_ghostlaws" = CHAT_GHOSTLAWS, + "chat_ghostpda" = CHAT_GHOSTPDA, + "chat_ghostradio" = CHAT_GHOSTRADIO, + "chat_ghostsight" = CHAT_GHOSTSIGHT, + "chat_ghostwhisper" = CHAT_GHOSTWHISPER, + "chat_login_logout" = CHAT_LOGIN_LOGOUT, + "chat_ooc" = CHAT_OOC, + "chat_prayer" = CHAT_PRAYER, + "chat_pullr" = CHAT_PULLR, + ) + +/datum/preference_middleware/legacy_toggles/get_character_preferences(mob/user) + if (preferences.current_window != PREFERENCE_TAB_GAME_PREFERENCES) + return list() + + var/static/list/admin_only_legacy_toggles = list( + "admin_ignore_cult_ghost", + "announce_login", + "combohud_lighting", + "deadmin_always", + "deadmin_antagonist", + "deadmin_position_head", + "deadmin_position_security", + "deadmin_position_silicon", + "sound_adminhelp", + "sound_prayers", + "split_admin_tabs", + ) + + var/static/list/admin_only_chat_toggles = list( + "chat_dead", + "chat_prayer", + ) + + var/static/list/deadmin_flags = list( + "deadmin_antagonist", + "deadmin_position_head", + "deadmin_position_security", + "deadmin_position_silicon", + ) + + var/list/new_game_preferences = list() + var/is_admin = is_admin(user.client) + + for (var/toggle_name in legacy_toggles) + if (!is_admin && (toggle_name in admin_only_legacy_toggles)) + continue + + if (is_admin && (toggle_name in deadmin_flags) && (preferences.toggles & DEADMIN_ALWAYS)) + continue + + if (toggle_name == "member_public" && !preferences.unlock_content) + continue + + new_game_preferences[toggle_name] = (preferences.toggles & legacy_toggles[toggle_name]) != 0 + + for (var/toggle_name in legacy_chat_toggles) + if (!is_admin && (toggle_name in admin_only_chat_toggles)) + continue + + new_game_preferences[toggle_name] = (preferences.chat_toggles & legacy_chat_toggles[toggle_name]) != 0 + + return list( + PREFERENCE_CATEGORY_GAME_PREFERENCES = new_game_preferences, + ) + +/datum/preference_middleware/legacy_toggles/pre_set_preference(mob/user, preference, value) + var/legacy_flag = legacy_toggles[preference] + if (!isnull(legacy_flag)) + if (value) + preferences.toggles |= legacy_flag + else + preferences.toggles &= ~legacy_flag + + // I know this looks silly, but this is the only one that cares + // and NO NEW LEGACY TOGGLES should ever be added. + if (legacy_flag == SOUND_LOBBY) + if (value && isnewplayer(user)) + user.client?.playtitlemusic() + else + user.stop_sound_channel(CHANNEL_LOBBYMUSIC) + + return TRUE + + var/legacy_chat_flag = legacy_chat_toggles[preference] + if (!isnull(legacy_chat_flag)) + if (value) + preferences.chat_toggles |= legacy_chat_flag + else + preferences.chat_toggles &= ~legacy_chat_flag + + return TRUE + + return FALSE diff --git a/code/modules/client/preferences/middleware/names.dm b/code/modules/client/preferences/middleware/names.dm new file mode 100644 index 0000000000000..34e97f0f727a9 --- /dev/null +++ b/code/modules/client/preferences/middleware/names.dm @@ -0,0 +1,56 @@ +/// Middleware that handles telling the UI which name to show, and waht names +/// they have. +/datum/preference_middleware/names + action_delegations = list( + "randomize_name" = .proc/randomize_name, + ) + +/datum/preference_middleware/names/get_constant_data() + var/list/data = list() + + var/list/types = list() + + for (var/preference_type in GLOB.preference_entries) + var/datum/preference/name/name_preference = GLOB.preference_entries[preference_type] + if (!istype(name_preference)) + continue + + types[name_preference.savefile_key] = list( + "can_randomize" = name_preference.is_randomizable(), + "explanation" = name_preference.explanation, + "group" = name_preference.group, + ) + + data["types"] = types + + return data + +/datum/preference_middleware/names/get_ui_data(mob/user) + var/list/data = list() + + data["name_to_use"] = get_name_to_use() + + return data + +/datum/preference_middleware/names/proc/get_name_to_use() + var/highest_priority_job = preferences.get_highest_priority_job() + + for (var/preference_type in GLOB.preference_entries) + var/datum/preference/name/name_preference = GLOB.preference_entries[preference_type] + if (!istype(name_preference)) + continue + + if (isnull(name_preference.relevant_job)) + continue + + if (istype(highest_priority_job, name_preference.relevant_job)) + return name_preference.savefile_key + + return "real_name" + +/datum/preference_middleware/names/proc/randomize_name(list/params, mob/user) + var/datum/preference/name/name_preference = GLOB.preference_entries_by_key[params["preference"]] + if (!istype(name_preference)) + return FALSE + + return preferences.update_preference(name_preference, name_preference.create_random_value(preferences)) diff --git a/code/modules/client/preferences/middleware/quirks.dm b/code/modules/client/preferences/middleware/quirks.dm new file mode 100644 index 0000000000000..f508c204ab5a4 --- /dev/null +++ b/code/modules/client/preferences/middleware/quirks.dm @@ -0,0 +1,89 @@ +/// Middleware to handle quirks +/datum/preference_middleware/quirks + var/tainted = FALSE + + action_delegations = list( + "give_quirk" = .proc/give_quirk, + "remove_quirk" = .proc/remove_quirk, + ) + +/datum/preference_middleware/quirks/get_ui_static_data(mob/user) + if (preferences.current_window != PREFERENCE_TAB_CHARACTER_PREFERENCES) + return list() + + var/list/data = list() + + data["selected_quirks"] = get_selected_quirks() + + return data + +/datum/preference_middleware/quirks/get_ui_data(mob/user) + var/list/data = list() + + if (tainted) + tainted = FALSE + data["selected_quirks"] = get_selected_quirks() + + return data + +/datum/preference_middleware/quirks/get_constant_data() + var/list/quirk_info = list() + + for (var/quirk_name in SSquirks.quirks) + var/datum/quirk/quirk = SSquirks.quirks[quirk_name] + quirk_info[sanitize_css_class_name(quirk_name)] = list( + "description" = initial(quirk.desc), + "icon" = initial(quirk.icon), + "name" = quirk_name, + "value" = initial(quirk.value), + ) + + return list( + "max_positive_quirks" = MAX_QUIRKS, + "quirk_info" = quirk_info, + "quirk_blacklist" = SSquirks.quirk_blacklist, + ) + +/datum/preference_middleware/quirks/on_new_character(mob/user) + tainted = TRUE + +/datum/preference_middleware/quirks/proc/give_quirk(list/params, mob/user) + var/quirk_name = params["quirk"] + + 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 + // something went wrong with the client prediction, so we should + // catch it back up to speed. + preferences.update_static_data(user) + return TRUE + + preferences.all_quirks = new_quirks + + return TRUE + +/datum/preference_middleware/quirks/proc/remove_quirk(list/params, mob/user) + var/quirk_name = params["quirk"] + + var/list/new_quirks = preferences.all_quirks - quirk_name + if ( \ + !(quirk_name in preferences.all_quirks) \ + || SSquirks.filter_invalid_quirks(new_quirks) != new_quirks \ + ) + // If the client is sending an invalid remove_quirk, that means that + // something went wrong with the client prediction, so we should + // catch it back up to speed. + preferences.update_static_data(user) + return TRUE + + preferences.all_quirks = new_quirks + + return TRUE + +/datum/preference_middleware/quirks/proc/get_selected_quirks() + var/list/selected_quirks = list() + + for (var/quirk in preferences.all_quirks) + selected_quirks += sanitize_css_class_name(quirk) + + return selected_quirks diff --git a/code/modules/client/preferences/middleware/random.dm b/code/modules/client/preferences/middleware/random.dm new file mode 100644 index 0000000000000..17bc259e2c253 --- /dev/null +++ b/code/modules/client/preferences/middleware/random.dm @@ -0,0 +1,84 @@ +/// Middleware for handling randomization preferences +/datum/preference_middleware/random + action_delegations = list( + "randomize_character" = .proc/randomize_character, + "set_random_preference" = .proc/set_random_preference, + ) + +/datum/preference_middleware/random/get_character_preferences(mob/user) + return list( + "randomization" = preferences.randomise, + ) + +/datum/preference_middleware/random/get_constant_data() + var/list/randomizable = list() + + for (var/preference_type in GLOB.preference_entries) + var/datum/preference/preference = GLOB.preference_entries[preference_type] + if (!preference.is_randomizable()) + continue + + randomizable += preference.savefile_key + + return list( + "randomizable" = randomizable, + ) + +/datum/preference_middleware/random/proc/randomize_character() + for (var/datum/preference/preference as anything in get_preferences_in_priority_order()) + if (preferences.should_randomize(preference)) + preferences.write_preference(preference, preference.create_random_value(src)) + + preferences.character_preview_view.update_body() + + return TRUE + +/datum/preference_middleware/random/proc/set_random_preference(list/params, mob/user) + var/requested_preference_key = params["preference"] + var/value = params["value"] + + var/datum/preference/requested_preference = GLOB.preference_entries_by_key[requested_preference_key] + if (isnull(requested_preference)) + return FALSE + + if (!requested_preference.is_randomizable()) + return FALSE + + if (value == RANDOM_ANTAG_ONLY) + preferences.randomise[requested_preference_key] = RANDOM_ANTAG_ONLY + else if (value == RANDOM_ENABLED) + preferences.randomise[requested_preference_key] = RANDOM_ENABLED + else if (value == RANDOM_DISABLED) + preferences.randomise -= requested_preference_key + else + return FALSE + + return TRUE + +/// Returns if a preference should be randomized. +/datum/preferences/proc/should_randomize(datum/preference/preference, is_antag) + if (!preference.is_randomizable()) + return FALSE + + var/requested_randomization = randomise[preference.savefile_key] + + if (istype(preference, /datum/preference/name)) + requested_randomization = read_preference(/datum/preference/choiced/random_name) + + switch (requested_randomization) + if (RANDOM_ENABLED) + return TRUE + if (RANDOM_ANTAG_ONLY) + return is_antag + else + return FALSE + +/// Given randomization flags, will return whether or not this preference should be randomized. +/datum/preference/proc/included_in_randomization_flags(randomize_flags) + return TRUE + +/datum/preference/name/included_in_randomization_flags(randomize_flags) + return !!(randomize_flags & RANDOMIZE_NAME) + +/datum/preference/choiced/species/included_in_randomization_flags(randomize_flags) + return !!(randomize_flags & RANDOMIZE_SPECIES) diff --git a/code/modules/client/preferences/middleware/species.dm b/code/modules/client/preferences/middleware/species.dm new file mode 100644 index 0000000000000..02efe1e223a5b --- /dev/null +++ b/code/modules/client/preferences/middleware/species.dm @@ -0,0 +1,35 @@ +/// Handles the assets for species icons +/datum/preference_middleware/species + +/datum/preference_middleware/species/get_ui_assets() + return list( + get_asset_datum(/datum/asset/spritesheet/species), + ) + +/datum/asset/spritesheet/species + name = "species" + early = TRUE + cross_round_cachable = TRUE + +/datum/asset/spritesheet/species/create_spritesheets() + var/list/to_insert = list() + + for (var/species_id in get_selectable_species()) + var/datum/species/species_type = GLOB.species_list[species_id] + + var/mob/living/carbon/human/dummy/consistent/dummy = new + dummy.set_species(species_type) + dummy.equipOutfit(/datum/outfit/job/assistant/consistent, visualsOnly = TRUE) + dummy.dna.species.prepare_human_for_preview(dummy) + COMPILE_OVERLAYS(dummy) + + var/icon/dummy_icon = getFlatIcon(dummy) + dummy_icon.Scale(64, 64) + dummy_icon.Crop(15, 64, 15 + 31, 64 - 31) + dummy_icon.Scale(64, 64) + to_insert[sanitize_css_class_name(initial(species_type.name))] = dummy_icon + + SSatoms.prepare_deletion(dummy) + + for (var/spritesheet_key in to_insert) + Insert(spritesheet_key, to_insert[spritesheet_key]) diff --git a/code/modules/client/preferences/names.dm b/code/modules/client/preferences/names.dm new file mode 100644 index 0000000000000..7c6dbdd2165a1 --- /dev/null +++ b/code/modules/client/preferences/names.dm @@ -0,0 +1,156 @@ +/// A preference for a name. Used not just for normal names, but also for clown names, etc. +/datum/preference/name + category = "names" + priority = PREFERENCE_PRIORITY_NAMES + savefile_identifier = PREFERENCE_CHARACTER + abstract_type = /datum/preference/name + + /// The display name when showing on the "other names" panel + var/explanation + + /// These will be grouped together on the preferences menu + var/group + + /// Whether or not to allow numbers in the person's name + var/allow_numbers = FALSE + + /// If the highest priority job matches this, will prioritize this name in the UI + var/relevant_job + +/datum/preference/name/apply_to_human(mob/living/carbon/human/target, value) + // Only real_name applies directly, everything else is applied by something else + return + +/datum/preference/name/deserialize(input, datum/preferences/preferences) + return reject_bad_name("[input]", allow_numbers) + +/datum/preference/name/serialize(input) + // `is_valid` should always be run before `serialize`, so it should not + // be possible for this to return `null`. + return reject_bad_name(input, allow_numbers) + +/datum/preference/name/is_valid(value) + return istext(value) && !isnull(reject_bad_name(value, allow_numbers)) + +/// A character's real name +/datum/preference/name/real_name + explanation = "Name" + // The `_` makes it first in ABC order. + group = "_real_name" + savefile_key = "real_name" + +/datum/preference/name/real_name/apply_to_human(mob/living/carbon/human/target, value) + target.real_name = value + target.name = value + +/datum/preference/name/real_name/create_informed_default_value(datum/preferences/preferences) + var/species_type = preferences.read_preference(/datum/preference/choiced/species) + var/gender = preferences.read_preference(/datum/preference/choiced/gender) + + var/datum/species/species = new species_type + + return species.random_name(gender, unique = TRUE) + +/datum/preference/name/real_name/deserialize(input, datum/preferences/preferences) + input = ..(input) + if (!input) + return input + + if (CONFIG_GET(flag/humans_need_surnames) && preferences.read_preference(/datum/preference/choiced/species) == /datum/species/human) + var/first_space = findtext(input, " ") + if(!first_space) //we need a surname + input += " [pick(GLOB.last_names)]" + else if(first_space == length(input)) + input += "[pick(GLOB.last_names)]" + + return reject_bad_name(input, allow_numbers) + +/// The name for a backup human, when nonhumans are made into head of staff +/datum/preference/name/backup_human + explanation = "Backup human name" + group = "backup_human" + savefile_key = "human_name" + +/datum/preference/name/backup_human/create_informed_default_value(datum/preferences/preferences) + var/gender = preferences.read_preference(/datum/preference/choiced/gender) + + return random_unique_name(gender) + +/datum/preference/name/clown + savefile_key = "clown_name" + + explanation = "Clown name" + group = "fun" + relevant_job = /datum/job/clown + +/datum/preference/name/clown/create_default_value() + return pick(GLOB.clown_names) + +/datum/preference/name/mime + savefile_key = "mime_name" + + explanation = "Mime name" + group = "fun" + relevant_job = /datum/job/mime + +/datum/preference/name/mime/create_default_value() + return pick(GLOB.mime_names) + +/datum/preference/name/cyborg + savefile_key = "cyborg_name" + + allow_numbers = TRUE + can_randomize = FALSE + + explanation = "Cyborg name" + group = "silicons" + relevant_job = /datum/job/cyborg + +/datum/preference/name/cyborg/create_default_value() + return DEFAULT_CYBORG_NAME + +/datum/preference/name/ai + savefile_key = "ai_name" + + allow_numbers = TRUE + explanation = "AI name" + group = "silicons" + relevant_job = /datum/job/ai + +/datum/preference/name/ai/create_default_value() + return pick(GLOB.ai_names) + +/datum/preference/name/religion + savefile_key = "religion_name" + + allow_numbers = TRUE + + explanation = "Religion name" + group = "religion" + +/datum/preference/name/religion/create_default_value() + return pick(GLOB.religion_names) + +/datum/preference/name/deity + savefile_key = "deity_name" + + allow_numbers = TRUE + can_randomize = FALSE + + explanation = "Deity name" + group = "religion" + +/datum/preference/name/deity/create_default_value() + return DEFAULT_DEITY + +/datum/preference/name/bible + savefile_key = "bible_name" + + allow_numbers = TRUE + can_randomize = FALSE + + explanation = "Bible name" + group = "religion" + +/datum/preference/name/bible/create_default_value() + return DEFAULT_BIBLE diff --git a/code/modules/client/preferences/ooc.dm b/code/modules/client/preferences/ooc.dm new file mode 100644 index 0000000000000..de436391edc97 --- /dev/null +++ b/code/modules/client/preferences/ooc.dm @@ -0,0 +1,14 @@ +/// The color admins will speak in for OOC. +/datum/preference/color/ooc_color + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "ooccolor" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/color/ooc_color/create_default_value() + return "#c43b23" + +/datum/preference/color/ooc_color/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + return is_admin(preferences.parent) || preferences.unlock_content diff --git a/code/modules/client/preferences/parallax.dm b/code/modules/client/preferences/parallax.dm new file mode 100644 index 0000000000000..24cccce2da62d --- /dev/null +++ b/code/modules/client/preferences/parallax.dm @@ -0,0 +1,38 @@ +/// Determines parallax, "fancy space" +/datum/preference/choiced/parallax + savefile_key = "parallax" + savefile_identifier = PREFERENCE_PLAYER + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + +/datum/preference/choiced/parallax/init_possible_values() + return list( + PARALLAX_INSANE, + PARALLAX_HIGH, + PARALLAX_MED, + PARALLAX_LOW, + PARALLAX_DISABLE, + ) + +/datum/preference/choiced/parallax/create_default_value() + return PARALLAX_HIGH + +/datum/preference/choiced/parallax/apply_to_client(client/client, value) + client.mob?.hud_used?.update_parallax_pref(client?.mob) + +/datum/preference/choiced/parallax/deserialize(input, datum/preferences/preferences) + // Old preferences were numbers, which causes annoyances when + // sending over as lists that isn't worth dealing with. + if (isnum(input)) + switch (input) + if (-1) + input = PARALLAX_INSANE + if (0) + input = PARALLAX_HIGH + if (1) + input = PARALLAX_MED + if (2) + input = PARALLAX_LOW + if (3) + input = PARALLAX_DISABLE + + return ..(input) diff --git a/code/modules/client/preferences/pda.dm b/code/modules/client/preferences/pda.dm new file mode 100644 index 0000000000000..fcbf4f1ad4be5 --- /dev/null +++ b/code/modules/client/preferences/pda.dm @@ -0,0 +1,17 @@ +/// The color of a PDA +/datum/preference/color/pda_color + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "pda_color" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/color/pda_color/create_default_value() + return COLOR_OLIVE + +/// The visual style of a PDA +/datum/preference/choiced/pda_style + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "pda_style" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/choiced/pda_style/init_possible_values() + return GLOB.pda_styles diff --git a/code/modules/client/preferences/phobia.dm b/code/modules/client/preferences/phobia.dm new file mode 100644 index 0000000000000..2a5caa5ba0f8e --- /dev/null +++ b/code/modules/client/preferences/phobia.dm @@ -0,0 +1,16 @@ +/datum/preference/choiced/phobia + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + savefile_key = "phobia" + savefile_identifier = PREFERENCE_CHARACTER + +/datum/preference/choiced/phobia/init_possible_values() + return GLOB.phobia_types + +/datum/preference/choiced/phobia/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + return "Phobia" in preferences.all_quirks + +/datum/preference/choiced/phobia/apply_to_human(mob/living/carbon/human/target, value) + return diff --git a/code/modules/client/preferences/pixel_size.dm b/code/modules/client/preferences/pixel_size.dm new file mode 100644 index 0000000000000..cb166e0139ac5 --- /dev/null +++ b/code/modules/client/preferences/pixel_size.dm @@ -0,0 +1,15 @@ +/datum/preference/numeric/pixel_size + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "pixel_size" + savefile_identifier = PREFERENCE_PLAYER + + minimum = 0 + maximum = 3 + + step = 0.5 + +/datum/preference/numeric/pixel_size/create_default_value() + return 0 + +/datum/preference/numeric/pixel_size/apply_to_client(client/client, value) + client?.view_size?.resetFormat() diff --git a/code/modules/client/preferences/playtime_reward_cloak.dm b/code/modules/client/preferences/playtime_reward_cloak.dm new file mode 100644 index 0000000000000..5eb1c89e9b252 --- /dev/null +++ b/code/modules/client/preferences/playtime_reward_cloak.dm @@ -0,0 +1,20 @@ +/// This can be set to TRUE from the prefs menu only once the user has +/// gained over 5K playtime hours. +/// If true, it allows the user to get a cool looking roundstart cloak. +/datum/preference/toggle/playtime_reward_cloak + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "playtime_reward_cloak" + +/datum/preference/toggle/playtime_reward_cloak/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + return preferences.parent?.is_veteran() + +/datum/preference/toggle/playtime_reward_cloak/apply_to_human(mob/living/carbon/human/target, value) + return + +/// Returns whether the client should receive the gamer cloak +/client/proc/is_veteran() + return get_exp_living(pure_numeric = TRUE) >= PLAYTIME_VETERAN diff --git a/code/modules/client/preferences/preferred_map.dm b/code/modules/client/preferences/preferred_map.dm new file mode 100644 index 0000000000000..8c9f87e823220 --- /dev/null +++ b/code/modules/client/preferences/preferred_map.dm @@ -0,0 +1,52 @@ +/// During map rotation, this will help determine the chosen map. +/datum/preference/choiced/preferred_map + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "preferred_map" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/choiced/preferred_map/init_possible_values() + var/list/maps = list() + maps += "" + + for (var/map in config.maplist) + var/datum/map_config/map_config = config.maplist[map] + if (!map_config.votable) + continue + + maps += map + + return maps + +/datum/preference/choiced/preferred_map/create_default_value() + return "" + +/datum/preference/choiced/preferred_map/compile_constant_data() + var/list/data = ..() + + var/display_names = list() + + if (config.defaultmap) + display_names[""] = "Default ([config.defaultmap.map_name])" + else + display_names[""] = "Default" + + for (var/choice in get_choices()) + if (choice == "") + continue + + var/datum/map_config/map_config = config.maplist[choice] + + var/map_name = map_config.map_name + if (map_config.voteweight <= 0) + map_name += " (disabled)" + display_names[choice] = map_name + + data[CHOICED_PREFERENCE_DISPLAY_NAMES] = display_names + + return data + +/datum/preference/choiced/preferred_map/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + return CONFIG_GET(flag/preference_map_voting) diff --git a/code/modules/client/preferences/random.dm b/code/modules/client/preferences/random.dm new file mode 100644 index 0000000000000..6ad675fcda299 --- /dev/null +++ b/code/modules/client/preferences/random.dm @@ -0,0 +1,53 @@ +/datum/preference/choiced/random_body + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + savefile_key = "random_body" + savefile_identifier = PREFERENCE_CHARACTER + can_randomize = FALSE + +/datum/preference/choiced/random_body/apply_to_human(mob/living/carbon/human/target, value) + return + +/datum/preference/choiced/random_body/init_possible_values() + return list( + RANDOM_ANTAG_ONLY, + RANDOM_DISABLED, + RANDOM_ENABLED, + ) + +/datum/preference/choiced/random_body/create_default_value() + return RANDOM_DISABLED + +/datum/preference/toggle/random_hardcore + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + savefile_key = "random_hardcore" + savefile_identifier = PREFERENCE_CHARACTER + can_randomize = FALSE + default_value = FALSE + +/datum/preference/toggle/random_hardcore/apply_to_human(mob/living/carbon/human/target, value) + return + +/datum/preference/toggle/random_hardcore/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + return preferences.parent.get_exp_living(pure_numeric = TRUE) >= PLAYTIME_HARDCORE_RANDOM + +/datum/preference/choiced/random_name + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + savefile_key = "random_name" + savefile_identifier = PREFERENCE_CHARACTER + can_randomize = FALSE + +/datum/preference/choiced/random_name/apply_to_human(mob/living/carbon/human/target, value) + return + +/datum/preference/choiced/random_name/init_possible_values() + return list( + RANDOM_ANTAG_ONLY, + RANDOM_DISABLED, + RANDOM_ENABLED, + ) + +/datum/preference/choiced/random_name/create_default_value() + return RANDOM_DISABLED diff --git a/code/modules/client/preferences/runechat.dm b/code/modules/client/preferences/runechat.dm new file mode 100644 index 0000000000000..83282fefe36c5 --- /dev/null +++ b/code/modules/client/preferences/runechat.dm @@ -0,0 +1,25 @@ +/datum/preference/toggle/enable_runechat + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "chat_on_map" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/toggle/enable_runechat_non_mobs + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "see_chat_non_mob" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/toggle/see_rc_emotes + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "see_rc_emotes" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/numeric/max_chat_length + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "max_chat_length" + savefile_identifier = PREFERENCE_PLAYER + + minimum = 1 + maximum = CHAT_MESSAGE_MAX_LENGTH + +/datum/preference/numeric/max_chat_length/create_default_value() + return CHAT_MESSAGE_MAX_LENGTH diff --git a/code/modules/client/preferences/scaling_method.dm b/code/modules/client/preferences/scaling_method.dm new file mode 100644 index 0000000000000..63235abaf996f --- /dev/null +++ b/code/modules/client/preferences/scaling_method.dm @@ -0,0 +1,14 @@ +/// The scaling method to show the world in, e.g. nearest neighbor +/datum/preference/choiced/scaling_method + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "scaling_method" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/choiced/scaling_method/create_default_value() + return SCALING_METHOD_DISTORT + +/datum/preference/choiced/scaling_method/init_possible_values() + return list(SCALING_METHOD_DISTORT, SCALING_METHOD_BLUR, SCALING_METHOD_NORMAL) + +/datum/preference/choiced/scaling_method/apply_to_client(client/client, value) + client?.view_size?.setZoomMode() diff --git a/code/modules/client/preferences/screentips.dm b/code/modules/client/preferences/screentips.dm new file mode 100644 index 0000000000000..d446575ac7ba3 --- /dev/null +++ b/code/modules/client/preferences/screentips.dm @@ -0,0 +1,18 @@ +/datum/preference/toggle/enable_screentips + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "screentip_pref" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/toggle/enable_screentips/apply_to_client(client/client, value) + client.mob?.hud_used?.screentips_enabled = value + +/datum/preference/color/screentip_color + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "screentip_color" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/color/screentip_color/apply_to_client(client/client, value) + client.mob?.hud_used?.screentip_color = value + +/datum/preference/color/screentip_color/create_default_value() + return "#ffd391" diff --git a/code/modules/client/preferences/security_department.dm b/code/modules/client/preferences/security_department.dm new file mode 100644 index 0000000000000..d3a637275efbc --- /dev/null +++ b/code/modules/client/preferences/security_department.dm @@ -0,0 +1,27 @@ +/// Which department to put security officers in, when the config is enabled +/datum/preference/choiced/security_department + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + can_randomize = FALSE + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "prefered_security_department" + +// This is what that #warn wants you to remove :) +/datum/preference/choiced/security_department/deserialize(input, datum/preferences/preferences) + if (!(input in GLOB.security_depts_prefs)) + return SEC_DEPT_NONE + return ..(input, preferences) + +/datum/preference/choiced/security_department/init_possible_values() + return GLOB.security_depts_prefs + +/datum/preference/choiced/security_department/apply_to_human(mob/living/carbon/human/target, value) + return + +/datum/preference/choiced/security_department/create_default_value() + return SEC_DEPT_NONE + +/datum/preference/choiced/security_department/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + return !CONFIG_GET(flag/sec_start_brig) diff --git a/code/modules/client/preferences/skin_tone.dm b/code/modules/client/preferences/skin_tone.dm new file mode 100644 index 0000000000000..60e71da430adb --- /dev/null +++ b/code/modules/client/preferences/skin_tone.dm @@ -0,0 +1,37 @@ +/datum/preference/choiced/skin_tone + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "skin_tone" + +/datum/preference/choiced/skin_tone/init_possible_values() + return GLOB.skin_tones + +/datum/preference/choiced/skin_tone/compile_constant_data() + var/list/data = ..() + + data[CHOICED_PREFERENCE_DISPLAY_NAMES] = GLOB.skin_tone_names + + var/list/to_hex = list() + for (var/choice in get_choices()) + var/hex_value = skintone2hex(choice) + var/list/rgb = hex2rgb("#[hex_value]") + var/list/hsl = rgb2hsl(rgb[1], rgb[2], rgb[3]) + + to_hex[choice] = list( + "lightness" = hsl[3], + "value" = hex_value, + ) + + data["to_hex"] = to_hex + + return data + +/datum/preference/choiced/skin_tone/apply_to_human(mob/living/carbon/human/target, value) + target.skin_tone = value + +/datum/preference/choiced/skin_tone/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + var/datum/species/species_type = preferences.read_preference(/datum/preference/choiced/species) + return initial(species_type.use_skintones) diff --git a/code/modules/client/preferences/species.dm b/code/modules/client/preferences/species.dm new file mode 100644 index 0000000000000..6cfffda952569 --- /dev/null +++ b/code/modules/client/preferences/species.dm @@ -0,0 +1,60 @@ +/// Species preference +/datum/preference/choiced/species + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "species" + priority = PREFERENCE_PRIORITY_SPECIES + randomize_by_default = FALSE + +/datum/preference/choiced/species/deserialize(input, datum/preferences/preferences) + return GLOB.species_list[sanitize_inlist(input, get_choices_serialized(), "human")] + +/datum/preference/choiced/species/serialize(input) + var/datum/species/species = input + return initial(species.id) + +/datum/preference/choiced/species/create_default_value() + return /datum/species/human + +/datum/preference/choiced/species/create_random_value(datum/preferences/preferences) + return pick(get_choices()) + +/datum/preference/choiced/species/init_possible_values() + var/list/values = list() + + for (var/species_id in get_selectable_species()) + values += GLOB.species_list[species_id] + + return values + +/datum/preference/choiced/species/apply_to_human(mob/living/carbon/human/target, value) + target.set_species(value, icon_update = FALSE, pref_load = TRUE) + +/datum/preference/choiced/species/compile_constant_data() + var/list/data = list() + + var/list/food_flags = FOOD_FLAGS + + for (var/species_id in get_selectable_species()) + var/species_type = GLOB.species_list[species_id] + var/datum/species/species = new species_type + + var/list/diet = list() + + if (!(TRAIT_NOHUNGER in species.inherent_traits)) + diet = list( + "liked_food" = bitfield2list(species.liked_food, food_flags), + "disliked_food" = bitfield2list(species.disliked_food, food_flags), + "toxic_food" = bitfield2list(species.toxic_food, food_flags), + ) + + data[species_id] = list( + "name" = species.name, + "icon" = sanitize_css_class_name(species.name), + + "use_skintones" = species.use_skintones, + "sexes" = species.sexes, + + "enabled_features" = species.get_features(), + ) + diet + + return data diff --git a/code/modules/client/preferences/species_features/basic.dm b/code/modules/client/preferences/species_features/basic.dm new file mode 100644 index 0000000000000..bf57c5fd1b5a0 --- /dev/null +++ b/code/modules/client/preferences/species_features/basic.dm @@ -0,0 +1,101 @@ +/proc/generate_possible_values_for_sprite_accessories_on_head(accessories) + var/list/values = possible_values_for_sprite_accessory_list(accessories) + + var/icon/head_icon = icon('icons/mob/human_parts_greyscale.dmi', "human_head_m") + head_icon.Blend("#[skintone2hex("caucasian1")]", ICON_MULTIPLY) + + for (var/name in values) + var/datum/sprite_accessory/accessory = accessories[name] + if (accessory == null || accessory.icon_state == null) + continue + + var/icon/final_icon = new(head_icon) + + var/icon/beard_icon = values[name] + beard_icon.Blend(COLOR_DARK_BROWN, ICON_MULTIPLY) + final_icon.Blend(beard_icon, ICON_OVERLAY) + + final_icon.Crop(10, 19, 22, 31) + final_icon.Scale(32, 32) + + values[name] = final_icon + + return values + +/datum/preference/color_legacy/eye_color + savefile_key = "eye_color" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + relevant_species_trait = EYECOLOR + +/datum/preference/color_legacy/eye_color/apply_to_human(mob/living/carbon/human/target, value) + target.eye_color = value + + var/obj/item/organ/eyes/eyes_organ = target.getorgan(/obj/item/organ/eyes) + if (istype(eyes_organ)) + if (!initial(eyes_organ.eye_color)) + eyes_organ.eye_color = value + eyes_organ.old_eye_color = value + +/datum/preference/color_legacy/eye_color/create_default_value() + return random_eye_color() + +/datum/preference/choiced/facial_hairstyle + savefile_key = "facial_style_name" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_FEATURES + main_feature_name = "Facial hair" + should_generate_icons = TRUE + relevant_species_trait = FACEHAIR + +/datum/preference/choiced/facial_hairstyle/init_possible_values() + return generate_possible_values_for_sprite_accessories_on_head(GLOB.facial_hairstyles_list) + +/datum/preference/choiced/facial_hairstyle/apply_to_human(mob/living/carbon/human/target, value) + target.facial_hairstyle = value + +/datum/preference/choiced/facial_hairstyle/compile_constant_data() + var/list/data = ..() + + data[SUPPLEMENTAL_FEATURE_KEY] = "facial_hair_color" + + return data + +/datum/preference/color_legacy/facial_hair_color + savefile_key = "facial_hair_color" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES + relevant_species_trait = FACEHAIR + +/datum/preference/color_legacy/facial_hair_color/apply_to_human(mob/living/carbon/human/target, value) + target.facial_hair_color = value + +/datum/preference/color_legacy/hair_color + savefile_key = "hair_color" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES + relevant_species_trait = HAIR + +/datum/preference/color_legacy/hair_color/apply_to_human(mob/living/carbon/human/target, value) + target.hair_color = value + +/datum/preference/choiced/hairstyle + savefile_key = "hairstyle_name" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_FEATURES + main_feature_name = "Hairstyle" + should_generate_icons = TRUE + relevant_species_trait = HAIR + +/datum/preference/choiced/hairstyle/init_possible_values() + return generate_possible_values_for_sprite_accessories_on_head(GLOB.hairstyles_list) + +/datum/preference/choiced/hairstyle/apply_to_human(mob/living/carbon/human/target, value) + target.hairstyle = value + +/datum/preference/choiced/hairstyle/compile_constant_data() + var/list/data = ..() + + data[SUPPLEMENTAL_FEATURE_KEY] = "hair_color" + + return data diff --git a/code/modules/client/preferences/species_features/ethereal.dm b/code/modules/client/preferences/species_features/ethereal.dm new file mode 100644 index 0000000000000..5a0ac1e5be5f9 --- /dev/null +++ b/code/modules/client/preferences/species_features/ethereal.dm @@ -0,0 +1,33 @@ +/datum/preference/choiced/ethereal_color + savefile_key = "feature_ethcolor" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_FEATURES + main_feature_name = "Ethereal color" + should_generate_icons = TRUE + +/datum/preference/choiced/ethereal_color/init_possible_values() + var/list/values = list() + + var/icon/ethereal_base = icon('icons/mob/human_parts_greyscale.dmi', "ethereal_head_m") + ethereal_base.Blend(icon('icons/mob/human_parts_greyscale.dmi', "ethereal_chest_m"), ICON_OVERLAY) + ethereal_base.Blend(icon('icons/mob/human_parts_greyscale.dmi', "ethereal_l_arm"), ICON_OVERLAY) + ethereal_base.Blend(icon('icons/mob/human_parts_greyscale.dmi', "ethereal_r_arm"), ICON_OVERLAY) + + var/icon/eyes = icon('icons/mob/human_face.dmi', "eyes") + eyes.Blend(COLOR_BLACK, ICON_MULTIPLY) + ethereal_base.Blend(eyes, ICON_OVERLAY) + + ethereal_base.Scale(64, 64) + ethereal_base.Crop(15, 64, 15 + 31, 64 - 31) + + for (var/name in GLOB.color_list_ethereal) + var/color = GLOB.color_list_ethereal[name] + + var/icon/icon = new(ethereal_base) + icon.Blend("#[color]", ICON_MULTIPLY) + values[name] = icon + + return values + +/datum/preference/choiced/ethereal_color/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["ethcolor"] = GLOB.color_list_ethereal[value] diff --git a/code/modules/client/preferences/species_features/felinid.dm b/code/modules/client/preferences/species_features/felinid.dm new file mode 100644 index 0000000000000..bc5445cd2fd0e --- /dev/null +++ b/code/modules/client/preferences/species_features/felinid.dm @@ -0,0 +1,33 @@ +/datum/preference/choiced/tail_human + savefile_key = "feature_human_tail" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + can_randomize = FALSE + relevant_mutant_bodypart = "tail_human" + +/datum/preference/choiced/tail_human/init_possible_values() + return assoc_to_keys(GLOB.tails_list_human) + +/datum/preference/choiced/tail_human/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["tail_human"] = value + +/datum/preference/choiced/tail_human/create_default_value() + var/datum/sprite_accessory/tails/human/cat/tail = /datum/sprite_accessory/tails/human/cat + return initial(tail.name) + +/datum/preference/choiced/ears + savefile_key = "feature_human_ears" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + can_randomize = FALSE + relevant_mutant_bodypart = "ears" + +/datum/preference/choiced/ears/init_possible_values() + return assoc_to_keys(GLOB.ears_list) + +/datum/preference/choiced/ears/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["ears"] = value + +/datum/preference/choiced/ears/create_default_value() + var/datum/sprite_accessory/ears/cat/ears = /datum/sprite_accessory/ears/cat + return initial(ears.name) diff --git a/code/modules/client/preferences/species_features/lizard.dm b/code/modules/client/preferences/species_features/lizard.dm new file mode 100644 index 0000000000000..732639fd63ee5 --- /dev/null +++ b/code/modules/client/preferences/species_features/lizard.dm @@ -0,0 +1,141 @@ +/proc/generate_lizard_side_shots(list/sprite_accessories, key, include_snout = TRUE) + var/list/values = list() + + var/icon/lizard = icon('icons/mob/human_parts_greyscale.dmi', "lizard_head_m", EAST) + + var/icon/eyes = icon('icons/mob/human_face.dmi', "eyes", EAST) + eyes.Blend(COLOR_GRAY, ICON_MULTIPLY) + lizard.Blend(eyes, ICON_OVERLAY) + + if (include_snout) + lizard.Blend(icon('icons/mob/mutant_bodyparts.dmi', "m_snout_round_ADJ", EAST), ICON_OVERLAY) + + for (var/name in sprite_accessories) + var/datum/sprite_accessory/sprite_accessory = sprite_accessories[name] + + var/icon/final_icon = icon(lizard) + + if (sprite_accessory.icon_state != "none") + var/icon/accessory_icon = icon(sprite_accessory.icon, "m_[key]_[sprite_accessory.icon_state]_ADJ", EAST) + final_icon.Blend(accessory_icon, ICON_OVERLAY) + + final_icon.Crop(11, 20, 23, 32) + final_icon.Scale(32, 32) + final_icon.Blend(COLOR_VIBRANT_LIME, ICON_MULTIPLY) + + values[name] = final_icon + + return values + +/datum/preference/choiced/lizard_body_markings + savefile_key = "feature_lizard_body_markings" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_FEATURES + main_feature_name = "Body markings" + should_generate_icons = TRUE + relevant_mutant_bodypart = "body_markings" + +/datum/preference/choiced/lizard_body_markings/init_possible_values() + var/list/values = list() + + var/icon/lizard = icon('icons/mob/human_parts_greyscale.dmi', "lizard_chest_m") + + for (var/name in GLOB.body_markings_list) + var/datum/sprite_accessory/sprite_accessory = GLOB.body_markings_list[name] + + var/icon/final_icon = icon(lizard) + + if (sprite_accessory.icon_state != "none") + var/icon/body_markings_icon = icon( + 'icons/mob/mutant_bodyparts.dmi', + "m_body_markings_[sprite_accessory.icon_state]_ADJ", + ) + + final_icon.Blend(body_markings_icon, ICON_OVERLAY) + + final_icon.Blend(COLOR_VIBRANT_LIME, ICON_MULTIPLY) + final_icon.Crop(10, 8, 22, 23) + final_icon.Scale(26, 32) + final_icon.Crop(-2, 1, 29, 32) + + values[name] = final_icon + + return values + +/datum/preference/choiced/lizard_body_markings/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["body_markings"] = value + +/datum/preference/choiced/lizard_frills + savefile_key = "feature_lizard_frills" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_FEATURES + main_feature_name = "Frills" + should_generate_icons = TRUE + +/datum/preference/choiced/lizard_frills/init_possible_values() + return generate_lizard_side_shots(GLOB.frills_list, "frills") + +/datum/preference/choiced/lizard_frills/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["frills"] = value + +/datum/preference/choiced/lizard_horns + savefile_key = "feature_lizard_horns" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_FEATURES + main_feature_name = "Horns" + should_generate_icons = TRUE + +/datum/preference/choiced/lizard_horns/init_possible_values() + return generate_lizard_side_shots(GLOB.horns_list, "horns") + +/datum/preference/choiced/lizard_horns/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["horns"] = value + +/datum/preference/choiced/lizard_legs + savefile_key = "feature_lizard_legs" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + relevant_mutant_bodypart = "legs" + +/datum/preference/choiced/lizard_legs/init_possible_values() + return assoc_to_keys(GLOB.legs_list) + +/datum/preference/choiced/lizard_legs/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["legs"] = value + +/datum/preference/choiced/lizard_snout + savefile_key = "feature_lizard_snout" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_FEATURES + main_feature_name = "Snout" + should_generate_icons = TRUE + +/datum/preference/choiced/lizard_snout/init_possible_values() + return generate_lizard_side_shots(GLOB.snouts_list, "snout", include_snout = FALSE) + +/datum/preference/choiced/lizard_snout/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["snout"] = value + +/datum/preference/choiced/lizard_spines + savefile_key = "feature_lizard_spines" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + relevant_mutant_bodypart = "spines" + +/datum/preference/choiced/lizard_spines/init_possible_values() + return assoc_to_keys(GLOB.spines_list) + +/datum/preference/choiced/lizard_spines/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["spines"] = value + +/datum/preference/choiced/lizard_tail + savefile_key = "feature_lizard_tail" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + relevant_mutant_bodypart = "tail_lizard" + +/datum/preference/choiced/lizard_tail/init_possible_values() + return assoc_to_keys(GLOB.tails_list_lizard) + +/datum/preference/choiced/lizard_tail/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["tail_lizard"] = value diff --git a/code/modules/client/preferences/species_features/moth.dm b/code/modules/client/preferences/species_features/moth.dm new file mode 100644 index 0000000000000..10e1550a47f79 --- /dev/null +++ b/code/modules/client/preferences/species_features/moth.dm @@ -0,0 +1,102 @@ +/datum/preference/choiced/moth_antennae + savefile_key = "feature_moth_antennae" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_FEATURES + main_feature_name = "Antennae" + should_generate_icons = TRUE + +/datum/preference/choiced/moth_antennae/init_possible_values() + var/list/values = list() + + var/icon/moth_head = icon('icons/mob/human_parts.dmi', "moth_head_m") + moth_head.Blend(icon('icons/mob/human_face.dmi', "motheyes"), ICON_OVERLAY) + + for (var/antennae_name in GLOB.moth_antennae_list) + var/datum/sprite_accessory/antennae = GLOB.moth_antennae_list[antennae_name] + + var/icon/icon_with_antennae = new(moth_head) + icon_with_antennae.Blend(icon('icons/mob/moth_antennae.dmi', "m_moth_antennae_[antennae.icon_state]_FRONT"), ICON_OVERLAY) + icon_with_antennae.Scale(64, 64) + icon_with_antennae.Crop(15, 64, 15 + 31, 64 - 31) + + values[antennae.name] = icon_with_antennae + + return values + +/datum/preference/choiced/moth_antennae/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["moth_antennae"] = value + +/datum/preference/choiced/moth_markings + savefile_key = "feature_moth_markings" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_FEATURES + main_feature_name = "Body markings" + should_generate_icons = TRUE + relevant_mutant_bodypart = "moth_markings" + +/datum/preference/choiced/moth_markings/init_possible_values() + var/list/values = list() + + var/icon/moth_body = icon('icons/blanks/32x32.dmi', "nothing") + + moth_body.Blend(icon('icons/mob/moth_wings.dmi', "m_moth_wings_plain_BEHIND"), ICON_OVERLAY) + + var/list/body_parts = list( + BODY_ZONE_HEAD, + BODY_ZONE_CHEST, + BODY_ZONE_L_ARM, + BODY_ZONE_R_ARM, + ) + + for (var/body_part in body_parts) + var/gender = (body_part == "chest" || body_part == "head") ? "_m" : "" + moth_body.Blend(icon('icons/mob/human_parts.dmi', "moth_[body_part][gender]"), ICON_OVERLAY) + + moth_body.Blend(icon('icons/mob/human_face.dmi', "motheyes"), ICON_OVERLAY) + + for (var/markings_name in GLOB.moth_markings_list) + var/datum/sprite_accessory/markings = GLOB.moth_markings_list[markings_name] + var/icon/icon_with_markings = new(moth_body) + + if (markings_name != "None") + for (var/body_part in body_parts) + var/icon/body_part_icon = icon(markings.icon, "[markings.icon_state]_[body_part]") + body_part_icon.Crop(1, 1, 32, 32) + icon_with_markings.Blend(body_part_icon, ICON_OVERLAY) + + icon_with_markings.Blend(icon('icons/mob/moth_wings.dmi', "m_moth_wings_plain_FRONT"), ICON_OVERLAY) + icon_with_markings.Blend(icon('icons/mob/moth_antennae.dmi', "m_moth_antennae_plain_FRONT"), ICON_OVERLAY) + + // Zoom in on the top of the head and the chest + icon_with_markings.Scale(64, 64) + icon_with_markings.Crop(15, 64, 15 + 31, 64 - 31) + + values[markings.name] = icon_with_markings + + return values + +/datum/preference/choiced/moth_markings/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["moth_markings"] = value + +/datum/preference/choiced/moth_wings + savefile_key = "feature_moth_wings" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_FEATURES + main_feature_name = "Moth wings" + should_generate_icons = TRUE + +/datum/preference/choiced/moth_wings/init_possible_values() + var/list/icon/values = possible_values_for_sprite_accessory_list_for_body_part( + GLOB.moth_wings_list, + "moth_wings", + list("BEHIND", "FRONT"), + ) + + // Moth wings are in a stupid dimension + for (var/name in values) + values[name].Crop(1, 1, 32, 32) + + return values + +/datum/preference/choiced/moth_wings/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["moth_wings"] = value diff --git a/code/modules/client/preferences/species_features/mutants.dm b/code/modules/client/preferences/species_features/mutants.dm new file mode 100644 index 0000000000000..3d79bcb5c342b --- /dev/null +++ b/code/modules/client/preferences/species_features/mutants.dm @@ -0,0 +1,20 @@ +/datum/preference/color_legacy/mutant_color + savefile_key = "feature_mcolor" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + relevant_species_trait = MUTCOLORS + +/datum/preference/color_legacy/mutant_color/create_default_value() + return sanitize_hexcolor("[pick("7F", "FF")][pick("7F", "FF")][pick("7F", "FF")]") + +/datum/preference/color_legacy/mutant_color/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["mcolor"] = value + +/datum/preference/color_legacy/mutant_color/is_valid(value) + if (!..(value)) + return FALSE + + if (is_color_dark(expand_three_digit_color(value))) + return FALSE + + return TRUE diff --git a/code/modules/client/preferences/tgui.dm b/code/modules/client/preferences/tgui.dm new file mode 100644 index 0000000000000..44a8666e642bf --- /dev/null +++ b/code/modules/client/preferences/tgui.dm @@ -0,0 +1,20 @@ +/datum/preference/toggle/tgui_fancy + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "tgui_fancy" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/toggle/tgui_fancy/apply_to_client(client/client, value) + for (var/datum/tgui/tgui as anything in client.mob?.tgui_open_uis) + // Force it to reload either way + tgui.update_static_data(client.mob) + +/datum/preference/toggle/tgui_lock + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "tgui_lock" + savefile_identifier = PREFERENCE_PLAYER + default_value = FALSE + +/datum/preference/toggle/tgui_lock/apply_to_client(client/client, value) + for (var/datum/tgui/tgui as anything in client.mob?.tgui_open_uis) + // Force it to reload either way + tgui.update_static_data(client.mob) diff --git a/code/modules/client/preferences/tgui_prefs_migration.dm b/code/modules/client/preferences/tgui_prefs_migration.dm new file mode 100644 index 0000000000000..c6492dfa60ace --- /dev/null +++ b/code/modules/client/preferences/tgui_prefs_migration.dm @@ -0,0 +1,116 @@ +/// Handle the migrations necessary from pre-tgui prefs to post-tgui prefs +/datum/preferences/proc/migrate_preferences_to_tgui_prefs_menu() + migrate_antagonists() + migrate_key_bindings() + +/// Handle the migrations necessary from pre-tgui prefs to post-tgui prefs, for characters +/datum/preferences/proc/migrate_character_to_tgui_prefs_menu() + migrate_randomization() + +// Key bindings used to be "key" -> list("action"), +// such as "X" -> list("swap_hands"). +// This made it impossible to determine any order, meaning placing a new +// hotkey would produce non-deterministic order. +// tgui prefs menu moves this over to "swap_hands" -> list("X"). +/datum/preferences/proc/migrate_key_bindings() + var/new_key_bindings = list() + + for (var/unbound_hotkey in key_bindings["Unbound"]) + new_key_bindings[unbound_hotkey] = list() + + for (var/hotkey in key_bindings) + if (hotkey == "Unbound") + continue + + for (var/keybind in key_bindings[hotkey]) + if (keybind in new_key_bindings) + new_key_bindings[keybind] |= hotkey + else + new_key_bindings[keybind] = list(hotkey) + + key_bindings = new_key_bindings + +// Before tgui preferences menu, "traitor" would handle both roundstart, midround, and latejoin. +// These were split apart. +/datum/preferences/proc/migrate_antagonists() + migrate_antagonist(ROLE_HERETIC, list(ROLE_HERETIC_SMUGGLER)) + migrate_antagonist(ROLE_MALF, list(ROLE_MALF_MIDROUND)) + migrate_antagonist(ROLE_OPERATIVE, list(ROLE_OPERATIVE_MIDROUND, ROLE_LONE_OPERATIVE)) + migrate_antagonist(ROLE_REV_HEAD, list(ROLE_PROVOCATEUR)) + migrate_antagonist(ROLE_TRAITOR, list(ROLE_SYNDICATE_INFILTRATOR, ROLE_SLEEPER_AGENT)) + migrate_antagonist(ROLE_WIZARD, list(ROLE_WIZARD_MIDROUND)) + + // "Familes [sic] Antagonists" was the old name of the catch-all. + migrate_antagonist("Familes Antagonists", list(ROLE_FAMILIES, ROLE_FAMILY_HEAD_ASPIRANT)) + +/datum/preferences/proc/migrate_antagonist(will_exist, list/to_add) + if (will_exist in be_special) + for (var/add in to_add) + be_special += add + +// Randomization used to be an assoc list of fields to TRUE. +// Antagonist randomization was not even available to all options. +// tgui prefs menu changes from list("random_socks" = TRUE, "random_name_antag" = TRUE) +// to list("socks" = "enabled", "name" = "antag") +// as well as removing anything that was set to FALSE, as this can be extrapolated. +/datum/preferences/proc/migrate_randomization() + var/static/list/random_settings = list( + "random_age" = "age", + "random_backpack" = "backpack", + "random_eye_color" = "eye_color", + "random_facial_hair_color" = "facial_hair_color", + "random_facial_hairstyle" = "facial_hairstyle", + "random_gender" = "gender", + "random_hair_color" = "hair_color", + "random_hairstyle" = "hairstyle", + "random_jumpsuit_style" = "jumpsuit_style", + "random_skin_tone" = "skin_tone", + "random_socks" = "socks", + "random_species" = "species", + "random_undershirt" = "undershirt", + "random_underwear" = "underwear", + "random_underwear_color" = "underwear_color", + ) + + var/static/list/random_antag_settings = list( + "random_age_antag" = "age", + "random_gender_antag" = "gender", + "random_name_antag" = "name", + ) + + var/list/new_randomise = list() + + for (var/old_setting in random_settings) + if (randomise[old_setting]) + new_randomise[random_settings[old_setting]] = RANDOM_ENABLED + + for (var/old_antag_setting in random_antag_settings) + if (randomise[old_antag_setting]) + new_randomise[random_settings[old_antag_setting]] = RANDOM_ANTAG_ONLY + + migrate_randomization_to_new_pref( + /datum/preference/choiced/random_body, + "random_body", + "random_body_antag", + ) + + migrate_randomization_to_new_pref( + /datum/preference/choiced/random_name, + "random_name", + "random_name_antag", + ) + + if (randomise["random_hardcore"]) + write_preference(GLOB.preference_entries[/datum/preference/toggle/random_hardcore], TRUE) + + randomise = new_randomise + +/datum/preferences/proc/migrate_randomization_to_new_pref( + preference_type, + key, + key_antag, +) + if (randomise[key_antag]) + write_preference(GLOB.preference_entries[preference_type], RANDOM_ANTAG_ONLY) + else if (randomise[key]) + write_preference(GLOB.preference_entries[preference_type], RANDOM_ENABLED) diff --git a/code/modules/client/preferences/tooltips.dm b/code/modules/client/preferences/tooltips.dm new file mode 100644 index 0000000000000..1d7f558783888 --- /dev/null +++ b/code/modules/client/preferences/tooltips.dm @@ -0,0 +1,15 @@ +/datum/preference/numeric/tooltip_delay + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "tip_delay" + savefile_identifier = PREFERENCE_PLAYER + + minimum = 0 + maximum = 5000 + +/datum/preference/numeric/tooltip_delay/create_default_value() + return 500 + +/datum/preference/toggle/enable_tooltips + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "enable_tips" + savefile_identifier = PREFERENCE_PLAYER diff --git a/code/modules/client/preferences/ui_style.dm b/code/modules/client/preferences/ui_style.dm new file mode 100644 index 0000000000000..08f1af6c7dd53 --- /dev/null +++ b/code/modules/client/preferences/ui_style.dm @@ -0,0 +1,26 @@ +/// UI style preference +/datum/preference/choiced/ui_style + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_identifier = PREFERENCE_PLAYER + savefile_key = "UI_style" + should_generate_icons = TRUE + +/datum/preference/choiced/ui_style/init_possible_values() + var/list/values = list() + + for (var/style in GLOB.available_ui_styles) + var/icon/icons = GLOB.available_ui_styles[style] + + var/icon/icon = icon(icons, "hand_r") + icon.Crop(1, 1, world.icon_size * 2, world.icon_size) + icon.Blend(icon(icons, "hand_l"), ICON_OVERLAY, world.icon_size) + + values[style] = icon + + return values + +/datum/preference/choiced/ui_style/create_default_value() + return GLOB.available_ui_styles[1] + +/datum/preference/choiced/ui_style/apply_to_client(client/client, value) + client.mob?.hud_used?.update_ui_style(ui_style2icon(value)) diff --git a/code/modules/client/preferences/underwear_color.dm b/code/modules/client/preferences/underwear_color.dm new file mode 100644 index 0000000000000..cb0aa0c9bdafe --- /dev/null +++ b/code/modules/client/preferences/underwear_color.dm @@ -0,0 +1,15 @@ +/datum/preference/color_legacy/underwear_color + savefile_key = "underwear_color" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES + +/datum/preference/color_legacy/underwear_color/apply_to_human(mob/living/carbon/human/target, value) + target.underwear_color = value + +/datum/preference/color_legacy/underwear_color/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + var/species_type = preferences.read_preference(/datum/preference/choiced/species) + var/datum/species/species = new species_type + return !(NO_UNDERWEAR in species.species_traits) diff --git a/code/modules/client/preferences/uplink_location.dm b/code/modules/client/preferences/uplink_location.dm new file mode 100644 index 0000000000000..8ae892bb4024e --- /dev/null +++ b/code/modules/client/preferences/uplink_location.dm @@ -0,0 +1,26 @@ +/datum/preference/choiced/uplink_location + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "uplink_loc" + can_randomize = FALSE + +/datum/preference/choiced/uplink_location/init_possible_values() + return list(UPLINK_PDA, UPLINK_RADIO, UPLINK_PEN, UPLINK_IMPLANT) + +/datum/preference/choiced/uplink_location/compile_constant_data() + var/list/data = ..() + + data[CHOICED_PREFERENCE_DISPLAY_NAMES] = list( + UPLINK_PDA = "PDA", + UPLINK_RADIO = "Radio", + UPLINK_PEN = "Pen", + UPLINK_IMPLANT = "Implant ([UPLINK_IMPLANT_TELECRYSTAL_COST]TC)", + ) + + return data + +/datum/preference/choiced/uplink_location/create_default_value() + return UPLINK_PDA + +/datum/preference/choiced/uplink_location/apply_to_human(mob/living/carbon/human/target, value) + return diff --git a/code/modules/client/preferences/widescreen.dm b/code/modules/client/preferences/widescreen.dm new file mode 100644 index 0000000000000..1041a4f6f272c --- /dev/null +++ b/code/modules/client/preferences/widescreen.dm @@ -0,0 +1,7 @@ +/datum/preference/toggle/widescreen + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "widescreenpref" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/toggle/widescreen/apply_to_client(client/client, value) + client.view_size?.setDefault(getScreenSize(value)) diff --git a/code/modules/client/preferences/window_flashing.dm b/code/modules/client/preferences/window_flashing.dm new file mode 100644 index 0000000000000..687315387c631 --- /dev/null +++ b/code/modules/client/preferences/window_flashing.dm @@ -0,0 +1,5 @@ +/// Enables flashing the window in your task tray for important events +/datum/preference/toggle/window_flashing + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "windowflashing" + savefile_identifier = PREFERENCE_PLAYER diff --git a/code/modules/client/preferences_menu.dm b/code/modules/client/preferences_menu.dm new file mode 100644 index 0000000000000..887816a0a0af9 --- /dev/null +++ b/code/modules/client/preferences_menu.dm @@ -0,0 +1,26 @@ +/datum/verbs/menu/Preferences/verb/open_character_preferences() + set category = "OOC" + set name = "Open Character Preferences" + set desc = "Open Character Preferences" + + var/datum/preferences/preferences = usr?.client?.prefs + if (!preferences) + return + + preferences.current_window = PREFERENCE_TAB_CHARACTER_PREFERENCES + preferences.update_static_data(usr) + preferences.ui_interact(usr) + +/datum/verbs/menu/Preferences/verb/open_game_preferences() + set category = "OOC" + set name = "Open Game Preferences" + set desc = "Open Game Preferences" + + var/datum/preferences/preferences = usr?.client?.prefs + if (!preferences) + return + + preferences.current_window = PREFERENCE_TAB_GAME_PREFERENCES + preferences.update_static_data(usr) + preferences.ui_interact(usr) + diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm index ad8e4353066b1..f91997bcaa8b4 100644 --- a/code/modules/client/preferences_toggles.dm +++ b/code/modules/client/preferences_toggles.dm @@ -2,8 +2,9 @@ set name = "Game Preferences" set category = "Preferences" set desc = "Open Game Preferences Window" - prefs.current_tab = 1 - prefs.ShowChoices(usr) + prefs.current_window = PREFERENCE_TAB_CHARACTER_PREFERENCES + prefs.update_static_data(usr) + prefs.ui_interact(usr) /client/verb/toggle_ghost_ears() set name = "Show/Hide GhostEars" diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm index a5f8eba901321..42afece6a650f 100644 --- a/code/modules/client/verbs/ooc.dm +++ b/code/modules/client/verbs/ooc.dm @@ -87,7 +87,8 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") if(holder) if(!holder.fakekey || C.holder) if(check_rights_for(src, R_ADMIN)) - to_chat(C, "[badge_data][CONFIG_GET(flag/allow_admin_ooccolor) && prefs.ooccolor ? "" :"" ]OOC: [keyname][holder.fakekey ? "/([holder.fakekey])" : ""]: [msg]", allow_linkify = TRUE) + var/ooccolor = prefs.read_preference(/datum/preference/color/ooc_color) + to_chat(C, "[badge_data][CONFIG_GET(flag/allow_admin_ooccolor) && ooccolor ? "" :"" ]OOC: [keyname][holder.fakekey ? "/([holder.fakekey])" : ""]: [msg]", allow_linkify = TRUE) else to_chat(C, "[badge_data]OOC: [keyname][holder.fakekey ? "/([holder.fakekey])" : ""]: [msg]") else diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index 1d81773192981..d383251ef3763 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -187,7 +187,7 @@ /obj/item/clothing/suit/space/hardsuit/dropped(mob/user) ..() - if(jetpack) + if(isatom(jetpack)) for(var/X in jetpack.actions) var/datum/action/A = X A.Remove(user) diff --git a/code/modules/clothing/suits/toggles.dm b/code/modules/clothing/suits/toggles.dm index 42b330ef71490..6ea3a0a973e2c 100644 --- a/code/modules/clothing/suits/toggles.dm +++ b/code/modules/clothing/suits/toggles.dm @@ -151,7 +151,8 @@ helmet.suit = null qdel(helmet) helmet = null - QDEL_NULL(jetpack) + if (isatom(jetpack)) + QDEL_NULL(jetpack) return ..() /obj/item/clothing/head/helmet/space/hardsuit/Destroy() diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index f2a416e2e4e35..10668c2c086f4 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -710,10 +710,10 @@ GLOBAL_LIST_INIT(hallucination_list, list( feedback_details += "Type: [is_radio ? "Radio" : "Talk"], Source: [person.real_name], Message: [message]" // Display message - if (!is_radio && !(target.client?.prefs.toggles & PREFTOGGLE_RUNECHAT_GLOBAL)) + if (!is_radio && !target.client?.prefs.read_preference(/datum/preference/toggle/enable_runechat)) var/image/speech_overlay = image('icons/mob/talk.dmi', person, "default0", layer = ABOVE_MOB_LAYER) INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(flick_overlay), speech_overlay, list(target.client), 30) - if (target.client?.prefs.toggles & PREFTOGGLE_RUNECHAT_GLOBAL) + if (target.client?.prefs.read_preference(/datum/preference/toggle/enable_runechat)) create_chat_message(person, understood_language, list(target), chosen, spans) to_chat(target, message) qdel(src) diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm index eba7c10108c21..f7f3f0cb0dc57 100644 --- a/code/modules/jobs/job_types/_job.dm +++ b/code/modules/jobs/job_types/_job.dm @@ -389,3 +389,94 @@ scandisease.spread_text = "None" scandisease.visibility_flags |= HIDDEN_SCANNER H.ForceContractDisease(scandisease) + +/// Spawns the mob to be played as, taking into account preferences and the desired spawn point. +/datum/job/proc/get_spawn_mob(client/player_client, atom/spawn_point) + var/mob/living/spawn_instance + if(ispath(spawn_type, /mob/living/silicon/ai)) + // This is unfortunately necessary because of snowflake AI init code. To be refactored. + spawn_instance = new spawn_type(get_turf(spawn_point), null, player_client.mob) + else + spawn_instance = new spawn_type(player_client.mob.loc) + spawn_point.JoinPlayerHere(spawn_instance, TRUE) + spawn_instance.apply_prefs_job(player_client, src) + if(!player_client) + qdel(spawn_instance) + return // Disconnected while checking for the appearance ban. + return spawn_instance + + +/// Applies the preference options to the spawning mob, taking the job into account. Assumes the client has the proper mind. +/mob/living/proc/apply_prefs_job(client/player_client, datum/job/job) + + +/mob/living/carbon/human/apply_prefs_job(client/player_client, datum/job/job) + var/fully_randomize = GLOB.current_anonymous_theme || player_client.prefs.should_be_random_hardcore(job, player_client.mob.mind) || is_banned_from(player_client.ckey, "Appearance") + if(!player_client) + return // Disconnected while checking for the appearance ban. + + var/require_human = CONFIG_GET(flag/enforce_human_authority) && (job.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND) + + if(fully_randomize) + if(require_human) + player_client.prefs.randomise_appearance_prefs(~RANDOMIZE_SPECIES) + else + player_client.prefs.randomise_appearance_prefs() + + player_client.prefs.apply_prefs_to(src) + + if (require_human) + set_species(/datum/species/human) + + if(GLOB.current_anonymous_theme) + fully_replace_character_name(null, GLOB.current_anonymous_theme.anonymous_name(src)) + else + var/is_antag = (player_client.mob.mind in GLOB.pre_setup_antags) + if(require_human) + player_client.prefs.randomise["species"] = FALSE + player_client.prefs.safe_transfer_prefs_to(src, TRUE, is_antag) + if (require_human) + set_species(/datum/species/human) + if(CONFIG_GET(flag/force_random_names)) + var/species_type = player_client.prefs.read_preference(/datum/preference/choiced/species) + var/datum/species/species = new species_type + + var/gender = player_client.prefs.read_preference(/datum/preference/choiced/gender) + real_name = species.random_name(gender, TRUE) + dna.update_dna_identity() + + +/mob/living/silicon/ai/apply_prefs_job(client/player_client, datum/job/job) + if(GLOB.current_anonymous_theme) + fully_replace_character_name(real_name, GLOB.current_anonymous_theme.anonymous_ai_name(TRUE)) + return + apply_pref_name(/datum/preference/name/ai, player_client) // This proc already checks if the player is appearance banned. + set_core_display_icon(null, player_client) + + +/mob/living/silicon/robot/apply_prefs_job(client/player_client, datum/job/job) + if(mmi) + var/organic_name + if(GLOB.current_anonymous_theme) + organic_name = GLOB.current_anonymous_theme.anonymous_name(src) + else if(player_client.prefs.read_preference(/datum/preference/choiced/random_name) == RANDOM_ENABLED || CONFIG_GET(flag/force_random_names) || is_banned_from(player_client.ckey, "Appearance")) + if(!player_client) + return // Disconnected while checking the appearance ban. + + var/species_type = player_client.prefs.read_preference(/datum/preference/choiced/species) + var/datum/species/species = new species_type + organic_name = species.random_name(player_client.prefs.read_preference(/datum/preference/choiced/gender), TRUE) + else + if(!player_client) + return // Disconnected while checking the appearance ban. + organic_name = player_client.prefs.read_preference(/datum/preference/name/real_name) + + mmi.name = "[initial(mmi.name)]: [organic_name]" + if(mmi.brain) + mmi.brain.name = "[organic_name]'s brain" + if(mmi.brainmob) + mmi.brainmob.real_name = organic_name //the name of the brain inside the cyborg is the robotized human's name. + mmi.brainmob.name = organic_name + // If this checks fails, then the name will have been handled during initialization. + if(!GLOB.current_anonymous_theme && player_client.prefs.read_preference(/datum/preference/name/cyborg) != DEFAULT_CYBORG_NAME) + apply_pref_name(/datum/preference/name/cyborg, player_client) diff --git a/code/modules/jobs/job_types/assistant.dm b/code/modules/jobs/job_types/assistant.dm index 24abeb9533aa0..4e72a1b468b85 100644 --- a/code/modules/jobs/job_types/assistant.dm +++ b/code/modules/jobs/job_types/assistant.dm @@ -42,16 +42,29 @@ Assistant /datum/outfit/job/assistant/pre_equip(mob/living/carbon/human/H) ..() if (CONFIG_GET(flag/grey_assistants)) - if(H.jumpsuit_style == PREF_SUIT) - uniform = /obj/item/clothing/under/color/grey - else - uniform = /obj/item/clothing/under/color/jumpskirt/grey + give_grey_suit(H) else if(H.jumpsuit_style == PREF_SUIT) uniform = /obj/item/clothing/under/color/random else uniform = /obj/item/clothing/under/color/jumpskirt/random -/datum/outfit/job/assistant - name = "Assistant" - id = /obj/item/card/id/job/assistant +/datum/outfit/job/assistant/proc/give_grey_suit(mob/living/carbon/human/target) + if (target.jumpsuit_style == PREF_SUIT) + uniform = /obj/item/clothing/under/color/grey + else + uniform = /obj/item/clothing/under/color/jumpskirt/grey + +/datum/outfit/job/assistant/consistent + name = "[JOB_NAME_ASSISTANT] - Always Grey" + +/datum/outfit/job/assistant/consistent/pre_equip(mob/living/carbon/human/H) + ..() + give_grey_suit(H) + +/datum/outfit/job/assistant/consistent/post_equip(mob/living/carbon/human/H, visualsOnly) + ..() + + // This outfit is used by the assets SS, which is ran before the atoms SS + if (SSatoms.initialized == INITIALIZATION_INSSATOMS) + H.w_uniform?.update_greyscale() diff --git a/code/modules/jobs/job_types/chaplain.dm b/code/modules/jobs/job_types/chaplain.dm index c10cbdb599d4b..b59565967c94e 100644 --- a/code/modules/jobs/job_types/chaplain.dm +++ b/code/modules/jobs/job_types/chaplain.dm @@ -43,17 +43,11 @@ return H.mind?.holy_role = HOLY_ROLE_HIGHPRIEST - var/new_religion = DEFAULT_RELIGION - if(M.client && M.client.prefs.active_character.custom_names["religion"]) - new_religion = M.client.prefs.active_character.custom_names["religion"] - - var/new_deity = DEFAULT_DEITY - if(M.client && M.client.prefs.active_character.custom_names["deity"]) - new_deity = M.client.prefs.active_character.custom_names["deity"] + var/new_religion = player_client?.prefs?.read_preference(/datum/preference/name/religion) || DEFAULT_RELIGION + var/new_deity = player_client?.prefs?.read_preference(/datum/preference/name/deity) || DEFAULT_DEITY B.deity_name = new_deity - switch(lowertext(new_religion)) if("christianity") // DEFAULT_RELIGION B.name = pick("The Holy Bible","The Dead Sea Scrolls") diff --git a/code/modules/jobs/job_types/clown.dm b/code/modules/jobs/job_types/clown.dm index f86fc0ba0c0d6..b383bd5bac281 100644 --- a/code/modules/jobs/job_types/clown.dm +++ b/code/modules/jobs/job_types/clown.dm @@ -28,7 +28,10 @@ /datum/job/clown/after_spawn(mob/living/carbon/human/H, mob/M) . = ..() - H.apply_pref_name("clown", M.client) + if(!ishuman(H)) + return + H.apply_pref_name(/datum/preference/name/clown, M.client) + /datum/outfit/job/clown name = JOB_NAME_CLOWN diff --git a/code/modules/jobs/job_types/mime.dm b/code/modules/jobs/job_types/mime.dm index ecb69ccc22827..fd6f0e74d5a44 100644 --- a/code/modules/jobs/job_types/mime.dm +++ b/code/modules/jobs/job_types/mime.dm @@ -27,7 +27,10 @@ /datum/job/mime/after_spawn(mob/living/carbon/human/H, mob/M) . = ..() - H.apply_pref_name("mime", M.client) + if(!ishuman(H)) + return + H.apply_pref_name(/datum/preference/name/mime, M.client) + /datum/outfit/job/mime name = JOB_NAME_MIME diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm index ecfaf11b36911..325cc0eb4e2c6 100644 --- a/code/modules/jobs/job_types/security_officer.dm +++ b/code/modules/jobs/job_types/security_officer.dm @@ -46,7 +46,7 @@ GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, S // Assign department security var/department if(M?.client?.prefs) - department = M.client.prefs.active_character.preferred_security_department + department = player_client.prefs.read_preference(/datum/preference/choiced/security_department) if(!LAZYLEN(GLOB.available_depts) || department == "None") return else if(department in GLOB.available_depts) diff --git a/code/modules/keybindings/setup.dm b/code/modules/keybindings/setup.dm index 01a989265915e..510217f56b428 100644 --- a/code/modules/keybindings/setup.dm +++ b/code/modules/keybindings/setup.dm @@ -61,7 +61,7 @@ winset(src, "[setname]-close-tgui-say", "parent=[setname];name=Escape;command=[tgui_say_create_close_command()]") - if(prefs.toggles2 & PREFTOGGLE_2_HOTKEYS) + if(hotkeys) winset(src, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED] mainwindow.macro=default") else winset(src, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED] mainwindow.macro=old_default") diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index d889c32181291..0ab01dc502ecd 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -119,7 +119,10 @@ relevant_cap = max(hpc, epc) if(href_list["show_preferences"]) - client.prefs.ShowChoices(src) + var/datum/preferences/preferences = client.prefs + preferences.current_window = PREFERENCE_TAB_CHARACTER_PREFERENCES + preferences.update_static_data(usr) + preferences.ui_interact(usr) return 1 if(href_list["ready"]) @@ -232,7 +235,7 @@ observer.client = client observer.set_ghost_appearance() if(observer.client && observer.client.prefs) - observer.real_name = observer.client.prefs.active_character.real_name + observer.real_name = observer.client.prefs.read_preference(/datum/preference/name/real_name) observer.name = observer.real_name observer.update_icon() observer.stop_sound_channel(CHANNEL_LOBBYMUSIC) @@ -438,8 +441,9 @@ popup.set_content(jointext(dat, "")) popup.open(FALSE) // 0 is passed to open so that it doesn't use the onclose() proc -/mob/dead/new_player/proc/create_character(transfer_after) - spawning = 1 +/// Creates, assigns and returns the new_character to spawn as. Assumes a valid mind.assigned_role exists. +/mob/dead/new_player/proc/create_character(atom/destination) + spawning = TRUE close_spawn_windows() var/mob/living/carbon/human/H = new(loc) @@ -508,7 +512,7 @@ /mob/dead/new_player/proc/check_preferences() if(!client) return FALSE //Not sure how this would get run without the mob having a client, but let's just be safe. - if(client.prefs.active_character.joblessrole != RETURNTOLOBBY) + if(client.prefs.read_preference(/datum/preference/choiced/jobless_role) != RETURNTOLOBBY) return TRUE // If they have antags enabled, they're potentially doing this on purpose instead of by accident. Notify admins if so. var/has_antags = FALSE diff --git a/code/modules/mob/dead/new_player/preferences_setup.dm b/code/modules/mob/dead/new_player/preferences_setup.dm new file mode 100644 index 0000000000000..9069823374a07 --- /dev/null +++ b/code/modules/mob/dead/new_player/preferences_setup.dm @@ -0,0 +1,109 @@ +/// Fully randomizes everything in the character. +/datum/preferences/proc/randomise_appearance_prefs(randomize_flags = ALL) + for (var/datum/preference/preference as anything in get_preferences_in_priority_order()) + if (!preference.included_in_randomization_flags(randomize_flags)) + continue + + if (preference.is_randomizable()) + write_preference(preference, preference.create_random_value(src)) + +/// Randomizes the character according to preferences. +/datum/preferences/proc/apply_character_randomization_prefs(antag_override = FALSE) + switch (read_preference(/datum/preference/choiced/random_body)) + if (RANDOM_ANTAG_ONLY) + if (!antag_override) + return + + if (RANDOM_DISABLED) + return + + for (var/datum/preference/preference as anything in get_preferences_in_priority_order()) + if (should_randomize(preference, antag_override)) + write_preference(preference, preference.create_random_value(src)) + +///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() + character.hardcore_survival_score = next_hardcore_score ** 1.2 //30 points would be about 60 score + + +/** + * 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() + . = 0 + + var/quirk_budget = rand(8, 35) + + all_quirks = list() //empty it out + + 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 + + if(!available_hardcore_quirks.len) + break + + var/datum/quirk/picked_quirk = pick(available_hardcore_quirks) + + var/picked_quirk_blacklisted = FALSE + for(var/bl in SSquirks.quirk_blacklist) //Check if the quirk is blacklisted with our current quirks. quirk_blacklist is a list of lists. + var/list/blacklist = bl + if(!(picked_quirk in blacklist)) + continue + for(var/iterator_quirk in all_quirks) //Go through all the quirks we've already selected to see if theres a blacklist match + if((iterator_quirk in blacklist) && !(iterator_quirk == picked_quirk)) //two quirks have lined up in the list of the list of quirks that conflict with each other, so return (see quirks.dm for more details) + picked_quirk_blacklisted = TRUE + break + if(picked_quirk_blacklisted) + break + + if(picked_quirk_blacklisted) + available_hardcore_quirks -= picked_quirk + continue + + if(initial(picked_quirk.mood_quirk) && CONFIG_GET(flag/disable_human_mood)) //check for moodlet quirks + available_hardcore_quirks -= picked_quirk + continue + + all_quirks += initial(picked_quirk.name) + quirk_budget -= available_hardcore_quirks[picked_quirk] + . += available_hardcore_quirks[picked_quirk] + available_hardcore_quirks -= picked_quirk + +/// Returns what job is marked as highest +/datum/preferences/proc/get_highest_priority_job() + var/datum/job/preview_job + var/highest_pref = 0 + + for(var/job in job_preferences) + if(job_preferences[job] > highest_pref) + preview_job = SSjob.GetJob(job) + highest_pref = job_preferences[job] + + return preview_job + +/datum/preferences/proc/render_new_preview_appearance(mob/living/carbon/human/dummy/mannequin) + var/datum/job/preview_job = get_highest_priority_job() + + if(preview_job) + // Silicons only need a very basic preview since there is no customization for them. + if (istype(preview_job,/datum/job/ai)) + return image('icons/mob/ai.dmi', icon_state = resolve_ai_icon(read_preference(/datum/preference/choiced/ai_core_display)), dir = SOUTH) + if (istype(preview_job,/datum/job/cyborg)) + return image('icons/mob/robots.dmi', icon_state = "robot", dir = SOUTH) + + // Set up the dummy for its photoshoot + apply_prefs_to(mannequin, TRUE) + + if(preview_job) + mannequin.job = preview_job.title + mannequin.dress_up_as_job(preview_job, TRUE) + + COMPILE_OVERLAYS(mannequin) + return mannequin.appearance diff --git a/code/modules/mob/dead/observer/login.dm b/code/modules/mob/dead/observer/login.dm index 5550a454d5c54..36c697b7c503e 100644 --- a/code/modules/mob/dead/observer/login.dm +++ b/code/modules/mob/dead/observer/login.dm @@ -1,16 +1,16 @@ /mob/dead/observer/Login() ..() - ghost_accs = client.prefs.ghost_accs - ghost_others = client.prefs.ghost_others + ghost_accs = client.prefs.read_preference(/datum/preference/choiced/ghost_accessories) + ghost_others = client.prefs.read_preference(/datum/preference/choiced/ghost_others) var/preferred_form = null if(IsAdminGhost(src)) has_unlimited_silicon_privilege = 1 if(client.prefs.unlock_content) - preferred_form = client.prefs.ghost_form - ghost_orbit = client.prefs.ghost_orbit + preferred_form = client.prefs.read_preference(/datum/preference/choiced/ghost_form) + ghost_orbit = client.prefs.read_preference(/datum/preference/choiced/ghost_orbit) var/turf/T = get_turf(src) if (isturf(T)) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index f3f7016302bd1..d9593ca5b8012 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -200,9 +200,10 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER) */ /mob/dead/observer/update_icon(updates = ALL, new_form) . = ..() - if(client) //We update our preferences in case they changed right before update_icon was called. - ghost_accs = client.prefs.ghost_accs - ghost_others = client.prefs.ghost_others + + if(client) //We update our preferences in case they changed right before update_appearance was called. + ghost_accs = client.prefs.read_preference(/datum/preference/choiced/ghost_accessories) + ghost_others = client.prefs.read_preference(/datum/preference/choiced/ghost_others) if(hair_overlay) cut_overlay(hair_overlay) @@ -220,7 +221,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER) else ghostimage_default.icon_state = new_form - if(ghost_accs >= GHOST_ACCS_DIR && (icon_state in GLOB.ghost_forms_with_directions_list)) //if this icon has dirs AND the client wants to show them, we make sure we update the dir on movement + if((ghost_accs == GHOST_ACCS_DIR || ghost_accs == GHOST_ACCS_FULL) && (icon_state in GLOB.ghost_forms_with_directions_list)) //if this icon has dirs AND the client wants to show them, we make sure we update the dir on movement updatedir = 1 else updatedir = 0 //stop updating the dir in case we want to show accessories with dirs on a ghost sprite without dirs @@ -397,8 +398,9 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp if(source) var/atom/movable/screen/alert/A = throw_alert("[REF(source)]_notify_cloning", /atom/movable/screen/alert/notify_cloning) if(A) - if(client && client.prefs && client.prefs.UI_style) - A.icon = ui_style2icon(client.prefs.UI_style) + var/ui_style = client?.prefs?.read_preference(/datum/preference/choiced/ui_style) + if(ui_style) + A.icon = ui_style2icon(ui_style) A.desc = message var/old_layer = source.layer var/old_plane = source.plane @@ -583,7 +585,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp /mob/dead/observer/update_sight() if(client) - ghost_others = client.prefs.ghost_others //A quick update just in case this setting was changed right before calling the proc + ghost_others = client.prefs.read_preference(/datum/preference/choiced/ghost_others) //A quick update just in case this setting was changed right before calling the proc if (!ghostvision) see_invisible = SEE_INVISIBLE_LIVING @@ -611,11 +613,11 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp client.images -= GLOB.ghost_images_default if(GHOST_OTHERS_SIMPLE) client.images -= GLOB.ghost_images_simple - lastsetting = client.prefs.ghost_others + lastsetting = client.prefs.read_preference(/datum/preference/choiced/ghost_others) if(!ghostvision) return - if(client.prefs.ghost_others != GHOST_OTHERS_THEIR_SETTING) - switch(client.prefs.ghost_others) + if(lastsetting != GHOST_OTHERS_THEIR_SETTING) + switch(lastsetting) if(GHOST_OTHERS_DEFAULT_SPRITE) client.images |= (GLOB.ghost_images_default-ghostimage_default) if(GHOST_OTHERS_SIMPLE) @@ -791,10 +793,11 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp set_ghost_appearance() if(client?.prefs) - deadchat_name = client.prefs.active_character.real_name + var/real_name = client.prefs.read_preference(/datum/preference/name/real_name) + deadchat_name = real_name if(mind) - mind.ghostname = client.prefs.active_character.real_name - name = client.prefs.active_character.real_name + mind.ghostname = real_name + name = real_name /mob/dead/observer/proc/set_ghost_appearance() if((!client) || (!client.prefs)) @@ -805,12 +808,18 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp if(client.prefs.active_character.be_random_body) client.prefs.active_character.randomise(gender) - if(HAIR in client.prefs.active_character.pref_species.species_traits) - hair_style = client.prefs.active_character.hair_style - hair_color = brighten_color(client.prefs.active_character.hair_color) - if(FACEHAIR in client.prefs.active_character.pref_species.species_traits) - facial_hair_style = client.prefs.active_character.facial_hair_style - facial_hair_color = brighten_color(client.prefs.active_character.facial_hair_color) + var/species_type = client.prefs.read_preference(/datum/preference/choiced/species) + var/datum/species/species = new species_type + + if(HAIR in species.species_traits) + hairstyle = client.prefs.read_preference(/datum/preference/choiced/hairstyle) + hair_color = brighten_color(client.prefs.read_preference(/datum/preference/color_legacy/hair_color)) + + if(FACEHAIR in species.species_traits) + facial_hairstyle = client.prefs.read_preference(/datum/preference/choiced/facial_hairstyle) + facial_hair_color = brighten_color(client.prefs.read_preference(/datum/preference/color_legacy/facial_hair_color)) + + qdel(species) update_icon() diff --git a/code/modules/mob/living/carbon/human/dummy.dm b/code/modules/mob/living/carbon/human/dummy.dm index 16a2885aa5f43..04aa0d070a062 100644 --- a/code/modules/mob/living/carbon/human/dummy.dm +++ b/code/modules/mob/living/carbon/human/dummy.dm @@ -23,6 +23,26 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy) randomize_human(src) dna.initialize_dna(skip_index = TRUE) //Skip stuff that requires full round init. +/// Provides a dummy that is consistently bald, white, naked, etc. +/mob/living/carbon/human/dummy/consistent + +/mob/living/carbon/human/dummy/consistent/setup_human_dna() + create_dna(src) + dna.initialize_dna(skip_index = TRUE) + dna.features["body_markings"] = "None" + dna.features["ears"] = "Cat" + dna.features["ethcolor"] = COLOR_WHITE + dna.features["frills"] = "None" + dna.features["horns"] = "None" + dna.features["mcolor"] = COLOR_VIBRANT_LIME + dna.features["moth_antennae"] = "Plain" + dna.features["moth_markings"] = "None" + dna.features["moth_wings"] = "Plain" + dna.features["snout"] = "Round" + dna.features["spines"] = "None" + dna.features["tail_human"] = "Cat" + dna.features["tail_lizard"] = "Smooth" + //Inefficient pooling/caching way. GLOBAL_LIST_EMPTY(human_dummy_list) GLOBAL_LIST_EMPTY(dummy_mob_list) diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index bef46105f6589..d73bb1b6b4b01 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -202,3 +202,23 @@ return TRUE if(isclothing(wear_mask) && (wear_mask.clothing_flags & SCAN_BOOZEPOWER)) return TRUE + +///copies over clothing preferences like underwear to another human +/mob/living/carbon/human/proc/copy_clothing_prefs(mob/living/carbon/human/destination) + destination.underwear = underwear + destination.underwear_color = underwear_color + destination.undershirt = undershirt + destination.socks = socks + destination.jumpsuit_style = jumpsuit_style + + +/// Fully randomizes everything according to the given flags. +/mob/living/carbon/human/proc/randomize_human_appearance(randomize_flags = ALL) + var/datum/preferences/preferences = new + + for (var/datum/preference/preference as anything in get_preferences_in_priority_order()) + if (!preference.included_in_randomization_flags(randomize_flags)) + continue + + if (preference.is_randomizable()) + preferences.write_preference(preference, preference.create_random_value(preferences)) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 28d865b8ca787..90f288c3d4dee 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -2220,3 +2220,8 @@ GLOBAL_LIST_EMPTY(roundstart_races) /datum/species/proc/get_huff_sound(mob/living/carbon/user) return + +/// Given a human, will adjust it before taking a picture for the preferences UI. +/// This should create a CONSISTENT result, so the icons don't randomly change. +/datum/species/proc/prepare_human_for_preview(mob/living/carbon/human/human) + return diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm index 113554872b820..592309f15eacc 100644 --- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm +++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm @@ -184,3 +184,10 @@ /datum/species/ethereal/get_sniff_sound(mob/living/carbon/user) return SPECIES_DEFAULT_SNIFF_SOUND(user) + +/datum/species/ethereal/get_features() + var/list/features = ..() + + features += "feature_ethcolor" + + return features diff --git a/code/modules/mob/living/carbon/human/species_types/felinid.dm b/code/modules/mob/living/carbon/human/species_types/felinid.dm index 64b38f6a978bc..0cafa73b94f45 100644 --- a/code/modules/mob/living/carbon/human/species_types/felinid.dm +++ b/code/modules/mob/living/carbon/human/species_types/felinid.dm @@ -171,3 +171,13 @@ if(!silent) to_chat(H, "You are no longer a cat.") + +/datum/species/human/felinid/prepare_human_for_preview(mob/living/carbon/human/human) + human.hairstyle = "Hime Cut" + human.hair_color = "fcc" // pink + human.update_hair() + + var/obj/item/organ/ears/cat/cat_ears = human.getorgan(/obj/item/organ/ears/cat) + if (cat_ears) + cat_ears.color = human.hair_color + human.update_body() diff --git a/code/modules/mob/living/carbon/human/species_types/humans.dm b/code/modules/mob/living/carbon/human/species_types/humans.dm index cd2093443f287..1a7236a2b17d7 100644 --- a/code/modules/mob/living/carbon/human/species_types/humans.dm +++ b/code/modules/mob/living/carbon/human/species_types/humans.dm @@ -42,3 +42,8 @@ /datum/species/human/get_sniff_sound(mob/living/carbon/user) return SPECIES_DEFAULT_SNIFF_SOUND(user) + +/datum/species/human/prepare_human_for_preview(mob/living/carbon/human/human) + human.hairstyle = "Business Hair" + human.hair_color = "b96" // brown + human.update_hair() diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index b25af8edbfac3..0cc9c2c62d9b0 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -138,7 +138,7 @@ create_modularInterface() create_eye() if(client) - apply_pref_name("ai",client) + INVOKE_ASYNC(src, .proc/apply_pref_name, /datum/preference/name/ai, client) INVOKE_ASYNC(src, PROC_REF(set_core_display_icon)) @@ -214,10 +214,10 @@ /mob/living/silicon/ai/proc/set_core_display_icon(input, client/C) if(client && !C) C = client - if(!input && !C?.prefs?.active_character.preferred_ai_core_display) + if(!input && !C?.prefs?.read_preference(/datum/preference/choiced/ai_core_display)) icon_state = initial(icon_state) else - var/preferred_icon = input ? input : C.prefs.active_character.preferred_ai_core_display + var/preferred_icon = input ? input : C.prefs.read_preference(/datum/preference/choiced/ai_core_display) icon_state = resolve_ai_icon(preferred_icon) /mob/living/silicon/ai/verb/pick_icon() diff --git a/code/modules/mob/living/silicon/ai/ai_portrait_picker.dm b/code/modules/mob/living/silicon/ai/ai_portrait_picker.dm index 45ddb4250395d..c880e11c70b56 100644 --- a/code/modules/mob/living/silicon/ai/ai_portrait_picker.dm +++ b/code/modules/mob/living/silicon/ai/ai_portrait_picker.dm @@ -37,9 +37,9 @@ /datum/portrait_picker/ui_data(mob/user) var/list/data = list() - data["library"] = SSpersistence.paintings["library"] ? SSpersistence.paintings["library"] : 0 - data["library_secure"] = SSpersistence.paintings["library_secure"] ? SSpersistence.paintings["library_secure"] : 0 - data["library_private"] = SSpersistence.paintings["library_private"] ? SSpersistence.paintings["library_private"] : 0 //i'm gonna regret this, won't i? + data["library"] = SSpersistent_paintings.paintings["library"] ? SSpersistent_paintings.paintings["library"] : 0 + data["library_secure"] = SSpersistent_paintings.paintings["library_secure"] ? SSpersistent_paintings.paintings["library_secure"] : 0 + data["library_private"] = SSpersistent_paintings.paintings["library_private"] ? SSpersistent_paintings.paintings["library_private"] : 0 //i'm gonna regret this, won't i? return data /datum/portrait_picker/ui_act(action, params) @@ -50,7 +50,7 @@ if("select") var/list/tab2key = list(TAB_LIBRARY = "library", TAB_SECURE = "library_secure", TAB_PRIVATE = "library_private") var/folder = tab2key[params["tab"]] - var/list/current_list = SSpersistence.paintings[folder] + var/list/current_list = SSpersistent_paintings.paintings[folder] var/list/chosen_portrait = current_list[params["selected"]] var/png = "data/paintings/[folder]/[chosen_portrait["md5"]].png" var/icon/portrait_icon = new(png) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 0eee6ce2955ee..de76d2778e274 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -285,8 +285,8 @@ var/changed_name = "" if(custom_name) changed_name = custom_name - if(changed_name == "" && C && C.prefs.active_character.custom_names["cyborg"] != DEFAULT_CYBORG_NAME) - if(apply_pref_name("cyborg", C)) + if(changed_name == "" && C && C.prefs.read_preference(/datum/preference/name/cyborg) != DEFAULT_CYBORG_NAME) + if(apply_pref_name(/datum/preference/name/cyborg, C)) return //built in camera handled in proc if(!changed_name) changed_name = get_standard_name() diff --git a/code/modules/mob/living/simple_animal/hostile/zombie.dm b/code/modules/mob/living/simple_animal/hostile/zombie.dm index d7742cd633f20..e446b65169bb3 100644 --- a/code/modules/mob/living/simple_animal/hostile/zombie.dm +++ b/code/modules/mob/living/simple_animal/hostile/zombie.dm @@ -29,21 +29,21 @@ setup_visuals() /mob/living/simple_animal/hostile/zombie/proc/setup_visuals() - var/datum/character_save/CS = new - CS.pref_species = new /datum/species/zombie - CS.be_random_body = TRUE - var/datum/job/J = SSjob.GetJob(zombiejob) - var/datum/outfit/O - if(J.outfit) - O = new J.outfit - //They have claws now. - O.r_hand = null - O.l_hand = null + var/datum/job/job = SSjob.GetJob(zombiejob) + + var/datum/outfit/outfit = new job.outfit + outfit.l_hand = null + outfit.r_hand = null + + var/mob/living/carbon/human/dummy/dummy = new + dummy.equipOutfit(outfit) + dummy.set_species(/datum/species/zombie) + COMPILE_OVERLAYS(dummy) + icon = getFlatIcon(dummy) + qdel(dummy) - var/icon/P = get_flat_human_icon("zombie_[zombiejob]", J , CS, "zombie", outfit_override = O) - icon = P corpse = new(src) - corpse.outfit = O + corpse.outfit = outfit corpse.mob_species = /datum/species/zombie corpse.mob_name = name diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 6708a188a6d0d..5ef619e8e0f30 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -35,7 +35,7 @@ create_mob_hud() if(hud_used) hud_used.show_hud(hud_used.hud_version) - hud_used.update_ui_style(ui_style2icon(client.prefs.UI_style)) + hud_used.update_ui_style(ui_style2icon(client.prefs?.read_preference(/datum/preference/choiced/ui_style))) next_move = 1 @@ -72,11 +72,15 @@ if(client.view_size) client.view_size.resetToDefault(getScreenSize(src)) // Sets the defaul view_size because it can be different to what it was on the lobby. else +<<<<<<< HEAD client.change_view(getScreenSize(src)) // Resets the client.view in case it was changed. //Reset verb information, give verbs accessible to the mob. if(client.tgui_panel) client.tgui_panel.set_verb_infomation(client) +======= + client.change_view(getScreenSize(client.prefs.read_preference(/datum/preference/toggle/widescreen))) +>>>>>>> 5a4c87a9fc3 (tgui Preferences Menu + total rewrite of the preferences backend (#61313)) if(client.player_details.player_actions.len) for(var/datum/action/A in client.player_details.player_actions) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 158d91c04a191..ae7f63b2eb09d 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -280,17 +280,19 @@ show_message(self_message, MSG_AUDIBLE, deaf_message, MSG_VISUAL) ///Returns the client runechat visible messages preference according to the message type. -/atom/proc/runechat_prefs_check(mob/target, list/visible_message_flags) - if(!(target.client?.prefs.toggles & PREFTOGGLE_RUNECHAT_GLOBAL) || !(target.client.prefs.toggles & PREFTOGGLE_RUNECHAT_NONMOBS)) +/atom/proc/runechat_prefs_check(mob/target, visible_message_flags = NONE) + if(!target.client?.prefs.read_preference(/datum/preference/toggle/enable_runechat)) return FALSE - if(LAZYFIND(visible_message_flags, CHATMESSAGE_EMOTE) && !(target.client.prefs.toggles & PREFTOGGLE_RUNECHAT_EMOTES)) + if (!target.client?.prefs.read_preference(/datum/preference/toggle/enable_runechat_non_mobs)) + return FALSE + if(LAZYFIND(visible_message_flags, CHATMESSAGE_EMOTE) && !target.client.prefs.read_preference(/datum/preference/toggle/see_rc_emotes)) return FALSE return TRUE -/mob/runechat_prefs_check(mob/target, list/visible_message_flags) - if(!(target.client?.prefs.toggles & PREFTOGGLE_RUNECHAT_GLOBAL)) +/mob/runechat_prefs_check(mob/target, visible_message_flags = NONE) + if(!target.client?.prefs.read_preference(/datum/preference/toggle/enable_runechat)) return FALSE - if(LAZYFIND(visible_message_flags, CHATMESSAGE_EMOTE) && !(target.client.prefs.toggles & PREFTOGGLE_RUNECHAT_EMOTES)) + if(LAZYFIND(visible_message_flags, CHATMESSAGE_EMOTE) && !target.client.prefs.read_preference(/datum/preference/toggle/see_rc_emotes)) return FALSE return TRUE diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 2f28c2611c421..36cf557e2f471 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -429,8 +429,9 @@ if(source) var/atom/movable/screen/alert/notify_action/A = O.throw_alert("[REF(source)]_notify_action", /atom/movable/screen/alert/notify_action) if(A) - if(O.client.prefs && O.client.prefs.UI_style) - A.icon = ui_style2icon(O.client.prefs.UI_style) + var/ui_style = O.client?.prefs?.read_preference(/datum/preference/choiced/ui_style) + if(ui_style) + A.icon = ui_style2icon(ui_style) if (header) A.name = header A.desc = message diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 3155d09be87ba..8ce1a895ae3bc 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -515,7 +515,7 @@ . = new /mob/living/silicon/ai(pick(landmark_loc), null, src) if(preference_source) - apply_pref_name("ai",preference_source) + apply_pref_name(/datum/preference/name/ai, preference_source) qdel(src) diff --git a/code/modules/modular_computers/file_system/programs/portrait_printer.dm b/code/modules/modular_computers/file_system/programs/portrait_printer.dm index d55ea45b59eba..3146e223f0be2 100644 --- a/code/modules/modular_computers/file_system/programs/portrait_printer.dm +++ b/code/modules/modular_computers/file_system/programs/portrait_printer.dm @@ -23,9 +23,9 @@ /datum/computer_file/program/portrait_printer/ui_data(mob/user) var/list/data = list() - data["library"] = SSpersistence.paintings["library"] ? SSpersistence.paintings["library"] : 0 - data["library_secure"] = SSpersistence.paintings["library_secure"] ? SSpersistence.paintings["library_secure"] : 0 - data["library_private"] = SSpersistence.paintings["library_private"] ? SSpersistence.paintings["library_private"] : 0 //i'm gonna regret this, won't i? + data["library"] = SSpersistent_paintings.paintings["library"] ? SSpersistent_paintings.paintings["library"] : 0 + data["library_secure"] = SSpersistent_paintings.paintings["library_secure"] ? SSpersistent_paintings.paintings["library_secure"] : 0 + data["library_private"] = SSpersistent_paintings.paintings["library_private"] ? SSpersistent_paintings.paintings["library_private"] : 0 //i'm gonna regret this, won't i? return data /datum/computer_file/program/portrait_printer/ui_assets(mob/user) @@ -55,7 +55,7 @@ //canvas printing! var/list/tab2key = list(TAB_LIBRARY = "library", TAB_SECURE = "library_secure", TAB_PRIVATE = "library_private") var/folder = tab2key[params["tab"]] - var/list/current_list = SSpersistence.paintings[folder] + var/list/current_list = SSpersistent_paintings.paintings[folder] var/list/chosen_portrait = current_list[params["selected"]] var/author = chosen_portrait["author"] var/title = chosen_portrait["title"] diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm index ed01d09278635..ec2aacfd2ca2d 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -7,7 +7,6 @@ return var/paths = subtypesof(/datum/reagent) - GLOB.chemical_reagents_list = list() for(var/path in paths) var/datum/reagent/D = new path() diff --git a/code/modules/research/techweb/_techweb_node.dm b/code/modules/research/techweb/_techweb_node.dm index 7c699e51b4036..a9fb62e57e899 100644 --- a/code/modules/research/techweb/_techweb_node.dm +++ b/code/modules/research/techweb/_techweb_node.dm @@ -42,9 +42,9 @@ VARSET_TO_LIST(., display_name) VARSET_TO_LIST(., hidden) VARSET_TO_LIST(., starting_node) - VARSET_TO_LIST(., assoc_list_strip_value(prereq_ids)) - VARSET_TO_LIST(., assoc_list_strip_value(design_ids)) - VARSET_TO_LIST(., assoc_list_strip_value(unlock_ids)) + VARSET_TO_LIST(., assoc_to_keys(prereq_ids)) + VARSET_TO_LIST(., assoc_to_keys(design_ids)) + VARSET_TO_LIST(., assoc_to_keys(unlock_ids)) VARSET_TO_LIST(., boost_item_paths) VARSET_TO_LIST(., autounlock_by_boost) VARSET_TO_LIST(., export_price) diff --git a/code/modules/surgery/organs/external/_external_organs.dm b/code/modules/surgery/organs/external/_external_organs.dm new file mode 100644 index 0000000000000..e3c612fdf3d4c --- /dev/null +++ b/code/modules/surgery/organs/external/_external_organs.dm @@ -0,0 +1,257 @@ +/** +* System for drawing organs with overlays. These overlays are drawn directly on the bodypart, attached to a person or not +* Works in tandem with the /datum/sprite_accessory datum to generate sprites +* Unlike normal organs, we're actually inside a persons limbs at all times +*/ +/obj/item/organ/external + name = "external organ" + desc = "An external organ that is too external." + + ///Unremovable is until the features are completely finished + organ_flags = ORGAN_UNREMOVABLE | ORGAN_EDIBLE + + ///Sometimes we need multiple layers, for like the back, middle and front of the person + var/layers + ///Convert the bitflag define into the actual layer define + var/static/list/all_layers = list(EXTERNAL_FRONT, EXTERNAL_ADJACENT, EXTERNAL_BEHIND) + + ///Defines what kind of 'organ' we're looking at. Sprites have names like 'm_firemoth_mothwings'. 'mothwings' would then be feature_key + var/feature_key = "" + + /// The savefile_key of the preference this relates to. Used for the preferences UI. + var/preference + + ///Sprite datum we use to draw on the bodypart + var/datum/sprite_accessory/sprite_datum + ///Key of the icon states of all the sprite_datums for easy caching + var/cache_key = "" + + ///With what DNA block do we mutate in mutate_feature() ? For genetics + var/dna_block + + ///Reference to the limb we're inside of + var/obj/item/bodypart/ownerlimb + +/**mob_sprite is optional if you havent set sprite_datums for the object, and is used mostly to generate sprite_datums from a persons DNA +* For _mob_sprite we make a distinction between "Round Snout" and "round". Round Snout is the name of the sprite datum, while "round" would be part of the sprite +* I'm sorry +*/ +/obj/item/organ/external/Initialize(mapload, mob_sprite) + . = ..() + if(mob_sprite) + set_sprite(mob_sprite) + +/obj/item/organ/external/Insert(mob/living/carbon/reciever, special, drop_if_replaced) + var/obj/item/bodypart/limb = reciever.get_bodypart(zone) + + if(!limb) + return FALSE + + limb.external_organs.Add(src) + ownerlimb = limb + + . = ..() + + limb.contents.Add(src) + + reciever.update_body_parts() + +/obj/item/organ/external/Remove(mob/living/carbon/organ_owner, special) + . = ..() + + if(ownerlimb) + ownerlimb.external_organs.Remove(src) + ownerlimb.contents.Remove(src) + ownerlimb = null + + organ_owner.update_body_parts() + +/obj/item/organ/external/transfer_to_limb(obj/item/bodypart/bodypart, mob/living/carbon/bodypart_owner) + . = ..() + + bodypart.external_organs.Add(src) + bodypart.contents.Add(src) + +///Add the overlays we need to draw on a person. Called from _bodyparts.dm +/obj/item/organ/external/proc/get_overlays(list/overlay_list, image_dir, image_layer, body_type, image_color) + if(!sprite_datum) + return + + var/gender = (body_type == FEMALE) ? "f" : "m" + var/finished_icon_state = (sprite_datum.gender_specific ? gender : "m") + "_" + feature_key + "_" + sprite_datum.icon_state + mutant_bodyparts_layertext(image_layer) + var/mutable_appearance/appearance = mutable_appearance(sprite_datum.icon, finished_icon_state, layer = -image_layer) + appearance.dir = image_dir + + if(sprite_datum.color_src) //There are multiple flags, but only one is ever used so meh :/ + appearance.color = image_color + + if(sprite_datum.center) + center_image(appearance, sprite_datum.dimension_x, sprite_datum.dimension_y) + + overlay_list += appearance + +/obj/item/organ/external/proc/set_sprite(sprite_name) + sprite_datum = get_sprite_datum(sprite_name) + cache_key = generate_icon_cache() + +///Generate a unique key based on our sprites. So that if we've aleady drawn these sprites, they can be found in the cache and wont have to be drawn again (blessing and curse) +/obj/item/organ/external/proc/generate_icon_cache() + return "[sprite_datum.icon_state]_[feature_key]" + +/**This exists so sprite accessories can still be per-layer without having to include that layer's +* number in their sprite name, which causes issues when those numbers change. +*/ +/obj/item/organ/external/proc/mutant_bodyparts_layertext(layer) + switch(layer) + if(BODY_BEHIND_LAYER) + return "_BEHIND" + if(BODY_ADJ_LAYER) + return "_ADJ" + if(BODY_FRONT_LAYER) + return "_FRONT" + +///Converts a bitflag to the right layer. I'd love to make this a static index list, but byond made an attempt on my life when i did +/obj/item/organ/external/proc/bitflag_to_layer(layer) + switch(layer) + if(EXTERNAL_BEHIND) + return BODY_BEHIND_LAYER + if(EXTERNAL_ADJACENT) + return BODY_ADJ_LAYER + if(EXTERNAL_FRONT) + return BODY_FRONT_LAYER + +///Because all the preferences have names like "Beautiful Sharp Snout" we need to get the sprite datum with the actual important info +/obj/item/organ/external/proc/get_sprite_datum(sprite) + var/list/feature_list = get_global_feature_list() + return feature_list[sprite] + +///Return a dumb glob list for this specific feature (called from parse_sprite) +/obj/item/organ/external/proc/get_global_feature_list() + return null + +///Check whether we can draw the overlays. You generally don't want lizard snouts to draw over an EVA suit +/obj/item/organ/external/proc/can_draw_on_bodypart(mob/living/carbon/human/human) + return TRUE + +///Update our features after something changed our appearance +/obj/item/organ/external/proc/mutate_feature(features, mob/living/carbon/human/human) + if(!dna_block || !get_global_feature_list()) + return + + var/list/feature_list = get_global_feature_list() + + set_sprite(feature_list[deconstruct_block(getblock(features, dna_block), feature_list.len)]) + +///The horns of a lizard! +/obj/item/organ/external/horns + zone = BODY_ZONE_HEAD + slot = ORGAN_SLOT_EXTERNAL_HORNS + layers = EXTERNAL_ADJACENT + + feature_key = "horns" + preference = "feature_lizard_horns" + + dna_block = DNA_HORNS_BLOCK + +/obj/item/organ/external/horns/can_draw_on_bodypart(mob/living/carbon/human/human) + if(!(human.head?.flags_inv & HIDEHAIR) || (human.wear_mask?.flags_inv & HIDEHAIR)) + return TRUE + return FALSE + +/obj/item/organ/external/horns/get_global_feature_list() + return GLOB.horns_list + +///The frills of a lizard (like weird fin ears) +/obj/item/organ/external/frills + zone = BODY_ZONE_HEAD + slot = ORGAN_SLOT_EXTERNAL_FRILLS + layers = EXTERNAL_ADJACENT + + feature_key = "frills" + preference = "feature_lizard_frills" + + dna_block = DNA_FRILLS_BLOCK + +/obj/item/organ/external/frills/can_draw_on_bodypart(mob/living/carbon/human/human) + if(!(human.head?.flags_inv & HIDEEARS)) + return TRUE + return FALSE + + +/obj/item/organ/external/frills/get_global_feature_list() + return GLOB.frills_list + +///Guess what part of the lizard this is? +/obj/item/organ/external/snout + zone = BODY_ZONE_HEAD + slot = ORGAN_SLOT_EXTERNAL_SNOUT + layers = EXTERNAL_ADJACENT + + feature_key = "snout" + preference = "feature_lizard_snout" + + dna_block = DNA_SNOUT_BLOCK + +/obj/item/organ/external/snout/can_draw_on_bodypart(mob/living/carbon/human/human) + if(!(human.wear_mask?.flags_inv & HIDESNOUT) && !(human.head?.flags_inv & HIDESNOUT)) + return TRUE + return FALSE + +/obj/item/organ/external/snout/get_global_feature_list() + return GLOB.snouts_list + +///A moth's antennae +/obj/item/organ/external/antennae + zone = BODY_ZONE_HEAD + slot = ORGAN_SLOT_EXTERNAL_ANTENNAE + layers = EXTERNAL_FRONT | EXTERNAL_BEHIND + + feature_key = "moth_antennae" + preference = "feature_moth_antennae" + + dna_block = DNA_MOTH_ANTENNAE_BLOCK + + ///Are we burned? + var/burnt = FALSE + ///Store our old sprite here for if our antennae wings are healed + var/original_sprite = "" + +/obj/item/organ/external/antennae/Insert(mob/living/carbon/reciever, special, drop_if_replaced) + . = ..() + + RegisterSignal(reciever, COMSIG_HUMAN_BURNING, .proc/try_burn_antennae) + RegisterSignal(reciever, COMSIG_LIVING_POST_FULLY_HEAL, .proc/heal_antennae) + +/obj/item/organ/external/antennae/Remove(mob/living/carbon/organ_owner, special) + . = ..() + + UnregisterSignal(organ_owner, list(COMSIG_HUMAN_BURNING, COMSIG_LIVING_POST_FULLY_HEAL)) + +/obj/item/organ/external/antennae/get_global_feature_list() + return GLOB.moth_antennae_list + +/obj/item/organ/external/antennae/can_draw_on_bodypart(mob/living/carbon/human/human) + return TRUE + +///check if our antennae can burn off ;_; +/obj/item/organ/external/antennae/proc/try_burn_antennae(mob/living/carbon/human/human) + SIGNAL_HANDLER + + if(!burnt && human.bodytemperature >= 800 && human.fire_stacks > 0) //do not go into the extremely hot light. you will not survive + to_chat(human, span_danger("Your precious antennae burn to a crisp!")) + + burn_antennae() + human.update_body_parts() + +/obj/item/organ/external/antennae/proc/burn_antennae() + burnt = TRUE + original_sprite = sprite_datum.name + set_sprite("Burnt Off") + +///heal our antennae back up!! +/obj/item/organ/external/antennae/proc/heal_antennae() + SIGNAL_HANDLER + + if(burnt) + burnt = FALSE + set_sprite(original_sprite) diff --git a/code/modules/surgery/organs/external/wings.dm b/code/modules/surgery/organs/external/wings.dm new file mode 100644 index 0000000000000..9b7a2e7a4e43b --- /dev/null +++ b/code/modules/surgery/organs/external/wings.dm @@ -0,0 +1,226 @@ +///Wing base type. doesn't really do anything +/obj/item/organ/external/wings + zone = BODY_ZONE_CHEST + slot = ORGAN_SLOT_EXTERNAL_WINGS + layers = EXTERNAL_BEHIND | EXTERNAL_ADJACENT | EXTERNAL_FRONT + + feature_key = "wings" + +/obj/item/organ/external/wings/can_draw_on_bodypart(mob/living/carbon/human/human) + if(!human.wear_suit) + return TRUE + if(!(human.wear_suit.flags_inv & HIDEJUMPSUIT)) + return TRUE + if(human.wear_suit.species_exception && is_type_in_list(src, human.wear_suit.species_exception)) + return TRUE + return FALSE + +///The true wings that you can use to fly and shit (you cant actually shit with them) +/obj/item/organ/external/wings/functional + ///The flight action object + var/datum/action/innate/flight/fly + + ///The preference type for opened wings + var/wings_open_feature_key = "wingsopen" + ///The preference type for closed wings + var/wings_closed_feature_key = "wings" + + ///Are our wings open or closed? + var/wings_open = FALSE + +/obj/item/organ/external/wings/functional/get_global_feature_list() + if(wings_open) + return GLOB.wings_open_list + else + return GLOB.wings_list + +/obj/item/organ/external/wings/functional/Insert(mob/living/carbon/reciever, special, drop_if_replaced) + . = ..() + + if(isnull(fly)) + fly = new + fly.Grant(reciever) + +/obj/item/organ/external/wings/functional/Remove(mob/living/carbon/organ_owner, special) + . = ..() + + fly.Remove(organ_owner) + +/obj/item/organ/external/wings/functional/on_life(delta_time, times_fired) + . = ..() + + handle_flight(owner) + +///Called on_life(). Handle flight code and check if we're still flying +/obj/item/organ/external/wings/functional/proc/handle_flight(mob/living/carbon/human/human) + if(human.movement_type & ~FLYING) + return FALSE + if(!can_fly(human)) + toggle_flight(human) + return FALSE + return TRUE + + +///Check if we're still eligible for flight (wings covered, atmosphere too thin, etc) +/obj/item/organ/external/wings/functional/proc/can_fly(mob/living/carbon/human/human) + if(human.stat || human.body_position == LYING_DOWN) + return FALSE + //Jumpsuits have tail holes, so it makes sense they have wing holes too + if(human.wear_suit && ((human.wear_suit.flags_inv & HIDEJUMPSUIT) && (!human.wear_suit.species_exception || !is_type_in_list(src, human.wear_suit.species_exception)))) + to_chat(human, span_warning("Your suit blocks your wings from extending!")) + return FALSE + var/turf/location = get_turf(human) + if(!location) + return FALSE + + var/datum/gas_mixture/environment = location.return_air() + if(environment && !(environment.return_pressure() > 30)) + to_chat(human, span_warning("The atmosphere is too thin for you to fly!")) + return FALSE + else + return TRUE + +///Slipping but in the air? +/obj/item/organ/external/wings/functional/proc/fly_slip(mob/living/carbon/human/human) + var/obj/buckled_obj + if(human.buckled) + buckled_obj = human.buckled + + to_chat(human, span_notice("Your wings spazz out and launch you!")) + + playsound(human.loc, 'sound/misc/slip.ogg', 50, TRUE, -3) + + for(var/obj/item/choking_hazard in human.held_items) + human.accident(choking_hazard) + + var/olddir = human.dir + + human.stop_pulling() + if(buckled_obj) + buckled_obj.unbuckle_mob(human) + step(buckled_obj, olddir) + else + new /datum/forced_movement(human, get_ranged_target_turf(human, olddir, 4), 1, FALSE, CALLBACK(human, /mob/living/carbon/.proc/spin, 1, 1)) + return TRUE + +///UNSAFE PROC, should only be called through the Activate or other sources that check for CanFly +/obj/item/organ/external/wings/functional/proc/toggle_flight(mob/living/carbon/human/human) + if(!HAS_TRAIT_FROM(human, TRAIT_MOVE_FLYING, SPECIES_FLIGHT_TRAIT)) + human.physiology.stun_mod *= 2 + ADD_TRAIT(human, TRAIT_NO_FLOATING_ANIM, SPECIES_FLIGHT_TRAIT) + ADD_TRAIT(human, TRAIT_MOVE_FLYING, SPECIES_FLIGHT_TRAIT) + passtable_on(human, SPECIES_TRAIT) + open_wings() + else + human.physiology.stun_mod *= 0.5 + REMOVE_TRAIT(human, TRAIT_NO_FLOATING_ANIM, SPECIES_FLIGHT_TRAIT) + REMOVE_TRAIT(human, TRAIT_MOVE_FLYING, SPECIES_FLIGHT_TRAIT) + passtable_off(human, SPECIES_TRAIT) + close_wings() + +///SPREAD OUR WINGS AND FLLLLLYYYYYY +/obj/item/organ/external/wings/functional/proc/open_wings() + feature_key = wings_open_feature_key + wings_open = TRUE + + cache_key = generate_icon_cache() //we've changed preference to open, so we only need to update the key and ask for an update to change our sprite + owner.update_body_parts() + +///close our wings +/obj/item/organ/external/wings/functional/proc/close_wings() + feature_key = wings_closed_feature_key + wings_open = FALSE + + cache_key = generate_icon_cache() + owner.update_body_parts() + if(isturf(owner?.loc)) + var/turf/location = loc + location.Entered(src, NONE) + +///hud action for starting and stopping flight +/datum/action/innate/flight + name = "Toggle Flight" + check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_IMMOBILE + icon_icon = 'icons/mob/actions/actions_items.dmi' + button_icon_state = "flight" + +/datum/action/innate/flight/Activate() + var/mob/living/carbon/human/human = owner + var/obj/item/organ/external/wings/functional/wings = human.getorganslot(ORGAN_SLOT_EXTERNAL_WINGS) + if(wings && wings.can_fly(human)) + wings.toggle_flight(human) + if(!(human.movement_type & FLYING)) + to_chat(human, span_notice("You settle gently back onto the ground...")) + else + to_chat(human, span_notice("You beat your wings and begin to hover gently above the ground...")) + human.set_resting(FALSE, TRUE) + +///Moth wings! They can flutter in low-grav and burn off in heat +/obj/item/organ/external/wings/moth + feature_key = "moth_wings" + preference = "feature_moth_wings" + layers = EXTERNAL_BEHIND | EXTERNAL_FRONT + + dna_block = DNA_MOTH_WINGS_BLOCK + + ///Are we burned? + var/burnt = FALSE + ///Store our old sprite here for if our burned wings are healed + var/original_sprite = "" + +/obj/item/organ/external/wings/moth/get_global_feature_list() + return GLOB.moth_wings_list + +/obj/item/organ/external/wings/moth/can_draw_on_bodypart(mob/living/carbon/human/human) + return TRUE + +/obj/item/organ/external/wings/moth/Insert(mob/living/carbon/reciever, special, drop_if_replaced) + . = ..() + + RegisterSignal(reciever, COMSIG_HUMAN_BURNING, .proc/try_burn_wings) + RegisterSignal(reciever, COMSIG_LIVING_POST_FULLY_HEAL, .proc/heal_wings) + RegisterSignal(reciever, COMSIG_MOVABLE_PRE_MOVE, .proc/update_float_move) + +/obj/item/organ/external/wings/moth/Remove(mob/living/carbon/organ_owner, special) + . = ..() + + UnregisterSignal(organ_owner, list(COMSIG_HUMAN_BURNING, COMSIG_LIVING_POST_FULLY_HEAL, COMSIG_MOVABLE_PRE_MOVE)) + REMOVE_TRAIT(organ_owner, TRAIT_FREE_FLOAT_MOVEMENT, src) + +///Check if we can flutter around +/obj/item/organ/external/wings/moth/proc/update_float_move() + SIGNAL_HANDLER + + if(!isspaceturf(owner.loc) && !burnt) + var/datum/gas_mixture/current = owner.loc.return_air() + if(current && (current.return_pressure() >= ONE_ATMOSPHERE*0.85)) //as long as there's reasonable pressure and no gravity, flight is possible + ADD_TRAIT(owner, TRAIT_FREE_FLOAT_MOVEMENT, src) + return + + REMOVE_TRAIT(owner, TRAIT_FREE_FLOAT_MOVEMENT, src) + +///check if our wings can burn off ;_; +/obj/item/organ/external/wings/moth/proc/try_burn_wings(mob/living/carbon/human/human) + SIGNAL_HANDLER + + if(!burnt && human.bodytemperature >= 800 && human.fire_stacks > 0) //do not go into the extremely hot light. you will not survive + to_chat(human, span_danger("Your precious wings burn to a crisp!")) + SEND_SIGNAL(human, COMSIG_ADD_MOOD_EVENT, "burnt_wings", /datum/mood_event/burnt_wings) + + burn_wings() + human.update_body_parts() + +///burn the wings off +/obj/item/organ/external/wings/moth/proc/burn_wings() + burnt = TRUE + + original_sprite = sprite_datum.name + set_sprite("Burnt Off") + +///heal our wings back up!! +/obj/item/organ/external/wings/moth/proc/heal_wings() + SIGNAL_HANDLER + + if(burnt) + burnt = FALSE + set_sprite(original_sprite) diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index 0191803273ad9..17fbc526fb992 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -30,6 +30,12 @@ var/useable = TRUE var/list/food_reagents = list(/datum/reagent/consumable/nutriment = 5) +// Players can look at prefs before atoms SS init, and without this +// they would not be able to see external organs, such as moth wings. +// This is also necessary because assets SS is before atoms, and so +// any nonhumans created in that time would experience the same effect. +INITIALIZE_IMMEDIATE(/obj/item/organ) + /obj/item/organ/Initialize() . = ..() if(organ_flags & ORGAN_EDIBLE) diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm index e33fc59ac1afa..a938f512e0f10 100644 --- a/code/modules/tgui/tgui.dm +++ b/code/modules/tgui/tgui.dm @@ -57,7 +57,7 @@ /datum/tgui/New(mob/user, datum/src_object, interface, title, ui_x, ui_y) if(!user.client) // No client to show the TGUI to, so stop here return - log_tgui(user, "new [interface] fancy [user.client.prefs.toggles2 & PREFTOGGLE_2_FANCY_TGUI]") + log_tgui(user, "new [interface] fancy [user?.client?.prefs.read_preference(/datum/preference/toggle/tgui_fancy)]") src.user = user src.src_object = src_object src.window_key = "[REF(src_object)]-main" @@ -101,7 +101,7 @@ if(!window.is_ready()) window.initialize( strict_mode = TRUE, - fancy = (user.client.prefs.toggles & PREFTOGGLE_2_FANCY_TGUI), + fancy = user.client.prefs.read_preference(/datum/preference/toggle/tgui_fancy), assets = list( get_asset_datum(/datum/asset/simple/tgui), )) @@ -243,8 +243,8 @@ "window" = list( "key" = window_key, "size" = window_size, - "fancy" = (user.client.prefs.toggles2 & PREFTOGGLE_2_FANCY_TGUI), - "locked" = (user.client.prefs.toggles2 & PREFTOGGLE_2_LOCKED_BUTTONS), + "fancy" = user.client.prefs.read_preference(/datum/preference/toggle/tgui_fancy), + "locked" = user.client.prefs.read_preference(/datum/preference/toggle/tgui_lock), ), "client" = list( "ckey" = user.client.ckey, diff --git a/code/modules/tooltip/tooltip.dm b/code/modules/tooltip/tooltip.dm index 577e16f790437..f9bf43d508c39 100644 --- a/code/modules/tooltip/tooltip.dm +++ b/code/modules/tooltip/tooltip.dm @@ -108,8 +108,9 @@ Notes: /proc/openToolTip(mob/user = null, atom/movable/tip_src = null, params = null,title = "",content = "",theme = "") if(istype(user)) if(user.client && user.client.tooltips) - if(!theme && user.client.prefs && user.client.prefs.UI_style) - theme = lowertext(user.client.prefs.UI_style) + var/ui_style = user.client?.prefs?.read_preference(/datum/preference/choiced/ui_style) + if(!theme && ui_style) + theme = lowertext(ui_style) if(!theme) theme = "default" user.client.tooltips.show(tip_src, params,title,content,theme) diff --git a/code/modules/unit_tests/dynamic_ruleset_sanity.dm b/code/modules/unit_tests/dynamic_ruleset_sanity.dm index 84a111a074591..cc1acb8b6a179 100644 --- a/code/modules/unit_tests/dynamic_ruleset_sanity.dm +++ b/code/modules/unit_tests/dynamic_ruleset_sanity.dm @@ -26,3 +26,25 @@ var/midround_ruleset_style = initial(ruleset.midround_ruleset_style) if (midround_ruleset_style != MIDROUND_RULESET_STYLE_HEAVY && midround_ruleset_style != MIDROUND_RULESET_STYLE_LIGHT) Fail("[ruleset] has an invalid midround_ruleset_style, it should be MIDROUND_RULESET_STYLE_HEAVY or MIDROUND_RULESET_STYLE_LIGHT") + +/// Verifies that dynamic rulesets have unique antag_flag. +/datum/unit_test/dynamic_unique_antag_flags + +/datum/unit_test/dynamic_unique_antag_flags/Run() + var/list/known_antag_flags = list() + + for (var/datum/dynamic_ruleset/ruleset as anything in subtypesof(/datum/dynamic_ruleset)) + if (isnull(initial(ruleset.antag_datum))) + continue + + var/antag_flag = initial(ruleset.antag_flag) + + if (isnull(antag_flag)) + Fail("[ruleset] has a null antag_flag!") + continue + + if (antag_flag in known_antag_flags) + Fail("[ruleset] has a non-unique antag_flag [antag_flag] (used by [known_antag_flags[antag_flag]])!") + continue + + known_antag_flags[antag_flag] = ruleset diff --git a/code/modules/unit_tests/preferences.dm b/code/modules/unit_tests/preferences.dm new file mode 100644 index 0000000000000..e3d6afbe53300 --- /dev/null +++ b/code/modules/unit_tests/preferences.dm @@ -0,0 +1,51 @@ +/// Requires all preferences to implement required methods. +/datum/unit_test/preferences_implement_everything + +/datum/unit_test/preferences_implement_everything/Run() + var/datum/preferences/preferences = new + var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human) + + for (var/preference_type in GLOB.preference_entries) + var/datum/preference/preference = GLOB.preference_entries[preference_type] + if (preference.savefile_identifier == PREFERENCE_CHARACTER) + preference.apply_to_human(human, preference.create_informed_default_value(preferences)) + + if (istype(preference, /datum/preference/choiced)) + var/datum/preference/choiced/choiced_preference = preference + choiced_preference.init_possible_values() + + // Smoke-test is_valid + preference.is_valid(TRUE) + preference.is_valid("string") + preference.is_valid(100) + preference.is_valid(list(1, 2, 3)) + +/// Requires all preferences to have a valid, unique savefile_identifier. +/datum/unit_test/preferences_valid_savefile_key + +/datum/unit_test/preferences_valid_savefile_key/Run() + var/list/known_savefile_keys = list() + + for (var/preference_type in GLOB.preference_entries) + var/datum/preference/preference = GLOB.preference_entries[preference_type] + if (!istext(preference.savefile_key)) + Fail("[preference_type] has an invalid savefile_key.") + + if (preference.savefile_key in known_savefile_keys) + Fail("[preference_type] has a non-unique savefile_key `[preference.savefile_key]`!") + + known_savefile_keys += preference.savefile_key + +/// Requires all main features have a main_feature_name +/datum/unit_test/preferences_valid_main_feature_name + +/datum/unit_test/preferences_valid_main_feature_name/Run() + for (var/preference_type in GLOB.preference_entries) + var/datum/preference/choiced/preference = GLOB.preference_entries[preference_type] + if (!istype(preference)) + continue + + if (preference.category != PREFERENCE_CATEGORY_FEATURES && preference.category != PREFERENCE_CATEGORY_CLOTHING) + continue + + TEST_ASSERT(!isnull(preference.main_feature_name), "Preference [preference_type] does not have a main_feature_name set!") diff --git a/code/modules/unit_tests/quirks.dm b/code/modules/unit_tests/quirks.dm new file mode 100644 index 0000000000000..ad0ca261c8bb5 --- /dev/null +++ b/code/modules/unit_tests/quirks.dm @@ -0,0 +1,21 @@ +/// Ensure every quirk has a unique icon +/datum/unit_test/quirk_icons + +/datum/unit_test/quirk_icons/Run() + var/list/used_icons = list() + + for (var/datum/quirk/quirk_type as anything in subtypesof(/datum/quirk)) + if (initial(quirk_type.abstract_parent_type) == quirk_type) + continue + + var/icon = initial(quirk_type.icon) + + if (isnull(icon)) + Fail("[quirk_type] has no icon!") + continue + + if (icon in used_icons) + Fail("[icon] used in both [quirk_type] and [used_icons[icon]]!") + continue + + used_icons[icon] = quirk_type diff --git a/config/game_options.txt b/config/game_options.txt index e5d1a42a8e58b..c32553546274a 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -530,9 +530,12 @@ ROUNDSTART_RACES plasmaman ##------------------------------------------------------------------------------------------- +<<<<<<< HEAD ## Uncomment to give players the choice of joining as a human with mutant bodyparts before they join the game JOIN_WITH_MUTANT_HUMANS +======= +>>>>>>> 5a4c87a9fc3 (tgui Preferences Menu + total rewrite of the preferences backend (#61313)) ##Overflow job. Default is assistant OVERFLOW_JOB Assistant diff --git a/icons/UI_Icons/antags/obsessed.dmi b/icons/UI_Icons/antags/obsessed.dmi new file mode 100644 index 0000000000000000000000000000000000000000..219a6e594132bc1b9d652df826679f986bc6cbcb GIT binary patch literal 2339 zcmV+;3EcLHP)V=-0C=1w$2$suFc1a6I(v$T_TsNiBN0iVcTil-78=L~y}pIrOo?Hf`~o+7 zdN`@X8y0<4lX+#&0Tx|LCa|0#|Ixf=hD8xKWmSoHL1vevuxQ8}X}$P&iE(jov!@Th zqZ{CY-B-*200?hML_t(og~gX!Oq=H!$A6zcpTU4_PR0h~7-Iuv&~_y$O%oz@+7<{~ zr>2uO%BF4YI%zjmHKy%uH@o1b)7151xkF6bRX3@jZQ7_!)Af`S;Sh)&f;l-DW1EBh z`SjWTb^&P&*%GN(Y1dZIr1b;o|cE_=H`B|w7Br|(UIY4;1Y0dU~uTh*9IWV za_`lvpK<75CtlyTjFL(5+a0MIu(Gfw}B?;uH8 zL{XrqXc3UH+v^?C==zJ1$l8meBf~M^^zIlN7#vFfw*bQZHepv+SMAc$&DDRs{#Q;u z`%^U9sy$y65y&G5GLUC}{ssa6EPnqixm@nLv$1*1U@)F;Y3<%W!LAPp1D9@QY zW8Jp|d3jr&ocK`P-~TjPz2mOrxon2HiH{in;54hjS*jcdD66a^kyU8zJVfWgBWQH` zodiM(rI^9*pC#a*B@mcJlBA2fK(Fr$Fg^9*LsqMKs;lb|8g2Dmb4!6qCO#bF%KQIh zD;h=+1QbO9Kx-(avGtqu|Kj)98odaDaOaW`gggQ=$>b(}|1596^~RiPzttD=`M#LxORc7=TDPLZ0?DwMp5(>6%*^x zB5iH$$g-T?7a$n)_gc&*RH`p(UsM#fC5dP{PxsN|{P3AU9y$0eZf>gaHn(xG`zekc zIYu#;;@YS05?%~^?#!K%Wf{F*jo&{DFt#s%EN2cI3??Fx2#G|3d_K>u_!jf=1Zwv| z+S{MN>-N#u=mj*ibQ~d;QDd$-K!5Ku=qNCI@jVK1?w;7GREiR#n$XJfCE)M(oPyDj z;hxskRu-4m@CU+JEEX&l3$j{EIGM&*S&n{XnT$@M&0a_AkG@AHU%;p<;egXdsZLLH zJw{oX1+`Xp4?sGdrn*YY#Ke`Qfx)54{f=Ru+wI2Z>%?^cLZJ|$P>7XKh`iB6E^feR z)MH4bF&P!I=?tPuq|IRmm5S8HHa$-qr{HYa&s=i3ESZeFmCa_yVgEkR+tlPnR2wPL z2zWdm91aJbc$~>=GelxBqB((jvkpZpL9N%L5G!w`-IMuNw4z$ zdo%B{bFGWk}G8vYZmXIU~lgUIV6e68WphZCzL(z8RD#_>bY^Afv#!>>|D1nP-u~gMkV{?4I zf6B|tF&GR~S~UdxvkVLly?;CJoeuPLbv^cz*0v+uoL?atjZ$GY)7905EXzb95lki% zk|d#Mv|L-;z-_k@*-mjIvc=SDoIr3=Yx2?UW`T~ zDwPV8Nq@Wf+y$W8t$%-geZz4fl|vP~g)yHZvKHn>Ab`POpuD`CbUKaQZbuYFBq@nV zf$~xZQ93~tN6N|+#mXwgo<*-;RJGW&q%iN8bRp68R z0laMMvwO`Lh>EUT+?=(Gyy%r*iA40=6Ir;~U*&g$wa#bS|6 zCPSI2j(5(y!H=K&&C5^j&CUZ}M7K5e#;ISox3~YSvC(O>*=)6SyyZB$pe5JfGzn1lxC_SVy&WJs3PP+D&%;2($GzQ3Qo)PcPk z9T~O(eY>OA<#LJ5%^nUt-h)~ZP#20QnF3mijZ{J6)TvikURrbw3=S=PB?tCuU~p&^ zIJG+%9T{#{6y^DbhK6S?7Hf?tY7mWDa&nApHo@}J;^zI5!&h)%KaGwIyMSKckk99{ z*VWm2Dl02_^Ub$NCX=HBgG0}KtpIyq>VcE&l;|DcZx8x+{0}0>sh1^jh&TWM002ov JPDHLkV1oIWY4rdA literal 0 HcmV?d00001 diff --git a/tgui/.eslintrc.yml b/tgui/.eslintrc.yml index 3f62ae1e83c1d..9d5b3c912b77e 100644 --- a/tgui/.eslintrc.yml +++ b/tgui/.eslintrc.yml @@ -13,6 +13,7 @@ env: plugins: - sonarjs - react + - unused-imports settings: react: version: '16.10' @@ -763,3 +764,6 @@ rules: react/jsx-uses-vars: error ## Prevent missing parentheses around multilines JSX (fixable) react/jsx-wrap-multilines: error + ## Prevents the use of unused imports. + ## This could be done by enabling no-unused-vars, but we're doing this for now + unused-imports/no-unused-imports: error diff --git a/tgui/package.json b/tgui/package.json index a613508b7993b..4893883c3847a 100644 --- a/tgui/package.json +++ b/tgui/package.json @@ -44,6 +44,7 @@ "eslint-config-prettier": "^8.8.0", "eslint-plugin-react": "^7.32.2", "eslint-plugin-sonarjs": "^0.18.0", + "eslint-plugin-unused-imports": "^2.0.0", "file-loader": "^6.2.0", "inferno": "^8.2.1", "jest": "^29.5.0", diff --git a/tgui/packages/common/collections.ts b/tgui/packages/common/collections.ts index 8c385f2aaa055..23996f766ff98 100644 --- a/tgui/packages/common/collections.ts +++ b/tgui/packages/common/collections.ts @@ -69,6 +69,25 @@ export const map: MapFunction = throw new Error(`map() can't iterate on type ${typeof collection}`); }; +/** + * Given a collection, will run each element through an iteratee function. + * Will then filter out undefined values. + */ +export const filterMap = (collection: T[], iterateeFn: ( + value: T +) => U | undefined): U[] => { + const finalCollection: U[] = []; + + for (const value of collection) { + const output = iterateeFn(value); + if (output !== undefined) { + finalCollection.push(output); + } + } + + return finalCollection; +}; + const COMPARATOR = (objA, objB) => { const criteriaA = objA.criteria; const criteriaB = objB.criteria; @@ -122,6 +141,8 @@ export const sortBy = return values; }; +export const sortStrings = sortBy(); + /** * * returns a range of numbers from start to end, exclusively. @@ -235,3 +256,48 @@ export const zipWith = (...arrays: T[][]): U[] => { return map((values: T[]) => iterateeFn(...values))(zip(...arrays)); }; + +const binarySearch = ( + getKey: (value: T) => U, + collection: readonly T[], + inserting: T, +): number => { + if (collection.length === 0) { + return 0; + } + + const insertingKey = getKey(inserting); + + let [low, high] = [0, collection.length]; + + // Because we have checked if the collection is empty, it's impossible + // for this to be used before assignment. + let compare: U = undefined as unknown as U; + let middle = 0; + + while (low < high) { + middle = (low + high) >> 1; + + compare = getKey(collection[middle]); + + if (compare < insertingKey) { + low = middle + 1; + } else if (compare === insertingKey) { + return middle; + } else { + high = middle; + } + } + + return compare > insertingKey ? middle : middle + 1; +}; + +export const binaryInsertWith = (getKey: (value: T) => U): + ((collection: readonly T[], value: T) => T[]) => +{ + return (collection, value) => { + const copy = [...collection]; + copy.splice(binarySearch(getKey, collection, value), 0, value); + return copy; + }; +}; diff --git a/tgui/packages/common/exhaustive.ts b/tgui/packages/common/exhaustive.ts new file mode 100644 index 0000000000000..bc41757515b08 --- /dev/null +++ b/tgui/packages/common/exhaustive.ts @@ -0,0 +1,19 @@ +/** + * Throws an error such that a non-exhaustive check will error at compile time + * when using TypeScript, rather than at runtime. + * + * For example: + * enum Color { Red, Green, Blue } + * switch (color) { + * case Color.Red: + * return "red"; + * case Color.Green: + * return "green"; + * default: + * // This will error at compile time that we forgot blue. + * exhaustiveCheck(color); + * } + */ +export const exhaustiveCheck = (input: never) => { + throw new Error(`Unhandled case: ${input}`); +}; diff --git a/tgui/packages/tgfont/icons/ATTRIBUTIONS.md b/tgui/packages/tgfont/icons/ATTRIBUTIONS.md new file mode 100644 index 0000000000000..2f218388d3648 --- /dev/null +++ b/tgui/packages/tgfont/icons/ATTRIBUTIONS.md @@ -0,0 +1,6 @@ +bad-touch.svg contains: +- hug by Phạm Thanh Lộc from the Noun Project +- Fight by Rudez Studio from the Noun Project + +prosthetic-leg.svg contains: +- prosthetic leg by Gan Khoon Lay from the Noun Project diff --git a/tgui/packages/tgfont/icons/bad-touch.svg b/tgui/packages/tgfont/icons/bad-touch.svg new file mode 100644 index 0000000000000..6dc3c9a718a79 --- /dev/null +++ b/tgui/packages/tgfont/icons/bad-touch.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/tgui/packages/tgfont/icons/non-binary.svg b/tgui/packages/tgfont/icons/non-binary.svg new file mode 100644 index 0000000000000..9aaec674bbbc2 --- /dev/null +++ b/tgui/packages/tgfont/icons/non-binary.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/tgui/packages/tgfont/icons/prosthetic-leg.svg b/tgui/packages/tgfont/icons/prosthetic-leg.svg new file mode 100644 index 0000000000000..c1f6ceee3fc34 --- /dev/null +++ b/tgui/packages/tgfont/icons/prosthetic-leg.svg @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/tgui/packages/tgui/components/Button.js b/tgui/packages/tgui/components/Button.js index 1e9d981dc9580..c95dcbd0d7ca6 100644 --- a/tgui/packages/tgui/components/Button.js +++ b/tgui/packages/tgui/components/Button.js @@ -76,7 +76,11 @@ export const Button = (props) => { computeBoxClassName(rest), ])} tabIndex={!disabled && '0'} - onKeyDown={(e) => { + onKeyDown={e => { + if (props.captureKeys === false) { + return; + } + const keyCode = window.event ? e.which : e.keyCode; // Simulate a click when pressing space or enter. if (keyCode === KEY_SPACE || keyCode === KEY_ENTER) { diff --git a/tgui/packages/tgui/components/Dropdown.js b/tgui/packages/tgui/components/Dropdown.js index 7a33912fc8733..6379c8b660512 100644 --- a/tgui/packages/tgui/components/Dropdown.js +++ b/tgui/packages/tgui/components/Dropdown.js @@ -47,16 +47,28 @@ export class Dropdown extends Component { buildMenu() { const { options = [] } = this.props; - const ops = options.map((option) => ( - { - this.setSelected(option); - }}> - {option} - - )); + const ops = options.map(option => { + let displayText, value; + + if (typeof option === "string") { + displayText = option; + value = option; + } else { + displayText = option.displayText; + value = option.value; + } + + return ( + { + this.setSelected(value); + }}> + {displayText} + + ); + }); return ops.length ? ops : 'No Options Found'; } @@ -66,13 +78,16 @@ export class Dropdown extends Component { icon, iconRotation, iconSpin, + clipSelectedText = true, color = 'default', + dropdownStyle, over, noscroll, nochevron, width, height, onClick, + onOpen, selected, disabled, displayText, @@ -99,7 +114,7 @@ export class Dropdown extends Component { ) : null; return ( -
    +
    { + onClick={(event) => { if (disabled && !this.state.open) { return; } this.setOpen(!this.state.open); + + if (props.onOpen) { + props.onOpen(event); + } + }}> + {icon && ( + + )} + - {icon && } - {displayText ? displayText : this.state.selected} + {displayText ? displayText : this.state.selected} + {!!nochevron || ( diff --git a/tgui/packages/tgui/components/FitText.tsx b/tgui/packages/tgui/components/FitText.tsx new file mode 100644 index 0000000000000..445e59119c99c --- /dev/null +++ b/tgui/packages/tgui/components/FitText.tsx @@ -0,0 +1,89 @@ +import { Component, createRef, RefObject } from "inferno"; + +const DEFAULT_ACCEPTABLE_DIFFERENCE = 5; + +export class FitText extends Component<{ + acceptableDifference?: number, + maxWidth: number, + maxFontSize: number, + native?: HTMLAttributes, +}, { + fontSize: number, +}> { + ref: RefObject = createRef(); + state = { + fontSize: 0, + } + + constructor() { + super(); + + this.resize = this.resize.bind(this); + + window.addEventListener("resize", this.resize); + } + + componentDidUpdate(prevProps) { + if (prevProps.children !== this.props.children) { + this.resize(); + } + } + + componentWillUnmount() { + window.removeEventListener("resize", this.resize); + } + + resize() { + const element = this.ref.current; + if (!element) { + return; + } + + const maxWidth = this.props.maxWidth; + + let start = 0; + let end = this.props.maxFontSize; + + for (let _ = 0; _ < 10; _++) { + const middle = Math.round((start + end) / 2); + element.style.fontSize = `${middle}px`; + + const difference = element.offsetWidth - maxWidth; + + if (difference > 0) { + end = middle; + } else if ( + difference + < (this.props.acceptableDifference ?? DEFAULT_ACCEPTABLE_DIFFERENCE) + ) { + start = middle; + } else { + break; + } + } + + this.setState({ + fontSize: Math.round((start + end) / 2), + }); + } + + componentDidMount() { + this.resize(); + } + + render() { + return ( + + {this.props.children} + + ); + } +} diff --git a/tgui/packages/tgui/components/Input.js b/tgui/packages/tgui/components/Input.js index 73afce0063870..c6aee95edafbc 100644 --- a/tgui/packages/tgui/components/Input.js +++ b/tgui/packages/tgui/components/Input.js @@ -65,6 +65,11 @@ export class Input extends Component { return; } if (e.keyCode === KEY_ESCAPE) { + if (this.props.onEscape) { + this.props.onEscape(e); + return; + } + this.setEditing(false); e.target.value = toInputValue(this.props.value); e.target.blur(); @@ -79,8 +84,15 @@ export class Input extends Component { if (input) { input.value = toInputValue(nextValue); } - if (this.props.autoFocus) { - setTimeout(() => input.focus(), 1); + + if (this.props.autoFocus || this.props.autoSelect) { + setTimeout(() => { + input.focus(); + + if (this.props.autoSelect) { + input.select(); + } + }, 1); } } diff --git a/tgui/packages/tgui/components/KeyListener.tsx b/tgui/packages/tgui/components/KeyListener.tsx new file mode 100644 index 0000000000000..46b5276536541 --- /dev/null +++ b/tgui/packages/tgui/components/KeyListener.tsx @@ -0,0 +1,39 @@ +import { Component } from "inferno"; +import { KeyEvent } from "../events"; +import { listenForKeyEvents } from "../hotkeys"; + +type KeyListenerProps = Partial<{ + onKey: (key: KeyEvent) => void, + onKeyDown: (key: KeyEvent) => void, + onKeyUp: (key: KeyEvent) => void, +}>; + +export class KeyListener extends Component { + dispose: () => void; + + constructor() { + super(); + + this.dispose = listenForKeyEvents(key => { + if (this.props.onKey) { + this.props.onKey(key); + } + + if (key.isDown() && this.props.onKeyDown) { + this.props.onKeyDown(key); + } + + if (key.isUp() && this.props.onKeyUp) { + this.props.onKeyUp(key); + } + }); + } + + componentWillUnmount() { + this.dispose(); + } + + render() { + return null; + } +} diff --git a/tgui/packages/tgui/components/Popper.tsx b/tgui/packages/tgui/components/Popper.tsx index dfe00a61cf5c3..d830c805eb9b7 100644 --- a/tgui/packages/tgui/components/Popper.tsx +++ b/tgui/packages/tgui/components/Popper.tsx @@ -64,7 +64,14 @@ export class Popper extends Component { } renderPopperContent(callback: () => void) { - render(this.props.popperContent, this.renderedContent, callback); + // `render` errors when given false, so we convert it to `null`, + // which is supported. + render( + this.props.popperContent || null, + this.renderedContent, + callback, + this.context + ); } render() { diff --git a/tgui/packages/tgui/components/Stack.tsx b/tgui/packages/tgui/components/Stack.tsx index 8816564f11e99..6111ce26db540 100644 --- a/tgui/packages/tgui/components/Stack.tsx +++ b/tgui/packages/tgui/components/Stack.tsx @@ -5,6 +5,8 @@ */ import { classes } from 'common/react'; +import { RefObject } from 'inferno'; +import { computeBoxProps } from './Box'; import { computeFlexClassName, computeFlexItemClassName, computeFlexItemProps, computeFlexProps, FlexItemProps, FlexProps } from './Flex'; type StackProps = FlexProps & { @@ -31,10 +33,22 @@ export const Stack = (props: StackProps) => { ); }; -const StackItem = (props: FlexProps) => { - const { className, ...rest } = props; +type StackItemProps = FlexProps & { + innerRef?: RefObject, +}; + +const StackItem = (props: StackItemProps) => { + const { className, innerRef, ...rest } = props; return ( -
    +
    ); }; diff --git a/tgui/packages/tgui/components/TrackOutsideClicks.tsx b/tgui/packages/tgui/components/TrackOutsideClicks.tsx new file mode 100644 index 0000000000000..fc52eba7e6c1e --- /dev/null +++ b/tgui/packages/tgui/components/TrackOutsideClicks.tsx @@ -0,0 +1,38 @@ +import { Component, createRef } from "inferno"; + +export class TrackOutsideClicks extends Component<{ + onOutsideClick: () => void, +}> { + ref = createRef(); + + constructor() { + super(); + + this.handleOutsideClick = this.handleOutsideClick.bind(this); + + document.addEventListener("click", this.handleOutsideClick); + } + + componentWillUnmount() { + document.removeEventListener("click", this.handleOutsideClick); + } + + handleOutsideClick(event: MouseEvent) { + if (!(event.target instanceof Node)) { + return; + } + + if (this.ref.current && !this.ref.current.contains(event.target)) { + this.props.onOutsideClick(); + } + } + + render() { + return ( +
    + {this.props.children} +
    + ); + } +} + diff --git a/tgui/packages/tgui/components/index.js b/tgui/packages/tgui/components/index.js index 9797751480aa0..a82d04c4b4455 100644 --- a/tgui/packages/tgui/components/index.js +++ b/tgui/packages/tgui/components/index.js @@ -20,10 +20,12 @@ export { DraggableControl } from './DraggableControl'; export { DraggableClickableControl } from './DraggableClickableControl'; export { Dropdown } from './Dropdown'; export { Flex } from './Flex'; +export { FitText } from './FitText'; export { Grid } from './Grid'; export { Icon } from './Icon'; export { InfinitePlane } from './InfinitePlane'; export { Input } from './Input'; +export { KeyListener } from './KeyListener'; export { Knob } from './Knob'; export { LabeledControls } from './LabeledControls'; export { LabeledList } from './LabeledList'; @@ -43,4 +45,5 @@ export { Table } from './Table'; export { Tabs } from './Tabs'; export { TextArea } from './TextArea'; export { TimeDisplay } from './TimeDisplay'; +export { TrackOutsideClicks } from './TrackOutsideClicks'; export { Tooltip } from './Tooltip'; diff --git a/tgui/packages/tgui/hotkeys.ts b/tgui/packages/tgui/hotkeys.ts index 2d7f418ad402e..53a6688869d0c 100644 --- a/tgui/packages/tgui/hotkeys.ts +++ b/tgui/packages/tgui/hotkeys.ts @@ -143,9 +143,10 @@ export const releaseHeldKeys = () => { }; export const updateHotkeyMode = () => - Byond.winget('mainwindow', 'macro').then((macro) => { - hotkeyMode = macro !== 'old_default'; - }); + Byond.winget("mainwindow", "macro") + .then(macro => { + hotkeyMode = macro !== "old_default"; + }); type ByondSkinMacro = { command: string; diff --git a/tgui/packages/tgui/http.ts b/tgui/packages/tgui/http.ts new file mode 100644 index 0000000000000..96a2d9b56df51 --- /dev/null +++ b/tgui/packages/tgui/http.ts @@ -0,0 +1,17 @@ +/** + * An equivalent to `fetch`, except will automatically retry. + */ +export const fetchRetry + = ( + url: string, + options?: RequestInit, + retryTimer: number = 1000, + ): Promise => { + return fetch(url, options).catch(() => { + return new Promise(resolve => { + setTimeout(() => { + fetchRetry(url, options, retryTimer).then(resolve); + }, retryTimer); + }); + }); + }; diff --git a/tgui/packages/tgui/interfaces/Aquarium.js b/tgui/packages/tgui/interfaces/Aquarium.js index 0f7b364a8cd62..79703265f93ad 100644 --- a/tgui/packages/tgui/interfaces/Aquarium.js +++ b/tgui/packages/tgui/interfaces/Aquarium.js @@ -1,5 +1,5 @@ import { useBackend } from '../backend'; -import { Button, Dropdown, Flex, Knob, LabeledControls, Section } from '../components'; +import { Button, Flex, Knob, LabeledControls, Section } from '../components'; import { Window } from '../layouts'; export const Aquarium = (props, context) => { diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/CircuitInfo.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/CircuitInfo.js index 8cbf6fe249eb9..da0274aaf6855 100644 --- a/tgui/packages/tgui/interfaces/IntegratedCircuit/CircuitInfo.js +++ b/tgui/packages/tgui/interfaces/IntegratedCircuit/CircuitInfo.js @@ -1,4 +1,4 @@ -import { Button, Section, Stack, Box } from '../../components'; +import { Button, Stack, Box } from '../../components'; export const CircuitInfo = (props, context) => { const { name, desc, notices, ...rest } = props; diff --git a/tgui/packages/tgui/interfaces/NtosNetChat.js b/tgui/packages/tgui/interfaces/NtosNetChat.js index e833b49417b70..c2a6d1ede1a2d 100644 --- a/tgui/packages/tgui/interfaces/NtosNetChat.js +++ b/tgui/packages/tgui/interfaces/NtosNetChat.js @@ -1,5 +1,5 @@ import { useBackend } from '../backend'; -import { Box, Button, Dimmer, Icon, Input, Section, Stack, Table, Tooltip } from '../components'; +import { Box, Button, Dimmer, Icon, Input, Section, Stack } from '../components'; import { NtosWindow } from '../layouts'; // byond defines for the program state diff --git a/tgui/packages/tgui/interfaces/NtosSecurEye.js b/tgui/packages/tgui/interfaces/NtosSecurEye.js index cceb1f616b42d..e333ee6605c48 100644 --- a/tgui/packages/tgui/interfaces/NtosSecurEye.js +++ b/tgui/packages/tgui/interfaces/NtosSecurEye.js @@ -1,13 +1,7 @@ -import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; -import { classes } from 'common/react'; -import { createSearch } from 'common/string'; -import { Fragment } from 'inferno'; -import { useBackend, useLocalState } from '../backend'; -import { Button, ByondUi, Input, Section } from '../components'; +import { useBackend } from '../backend'; +import { Button, ByondUi } from '../components'; import { NtosWindow } from '../layouts'; import { prevNextCamera, selectCameras, CameraConsoleContent } from './CameraConsole'; -import { logger } from '../logging'; export const NtosSecurEye = (props, context) => { const { act, data, config } = useBackend(context); diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/AntagsPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/AntagsPage.tsx new file mode 100644 index 0000000000000..5307c25a9fd21 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/AntagsPage.tsx @@ -0,0 +1,207 @@ +import { binaryInsertWith } from "common/collections"; +import { classes } from "common/react"; +import { useBackend, useLocalState } from "../../backend"; +import { Box, Button, Divider, Flex, Section, Stack, Tooltip } from "../../components"; +import { Antagonist, Category } from "./antagonists/base"; +import { PreferencesMenuData } from "./data"; + +const requireAntag = require.context("./antagonists/antagonists", false, /.ts$/); + +const antagsByCategory = new Map(); + +// This will break at priorities higher than 10, but that almost definitely +// will not happen. +const binaryInsertAntag = binaryInsertWith((antag: Antagonist) => { + return `${antag.priority}_${antag.name}`; +}); + +for (const antagKey of requireAntag.keys()) { + const antag = requireAntag<{ + default?: Antagonist, + }>(antagKey).default; + + if (!antag) { + continue; + } + + antagsByCategory.set( + antag.category, + binaryInsertAntag( + antagsByCategory.get(antag.category) || [], + antag, + ) + ); +} + +const AntagSelection = (props: { + antagonists: Antagonist[], + name: string, +}, context) => { + const { act, data } = useBackend(context); + const className = "PreferencesMenu__Antags__antagSelection"; + + const [predictedState, setPredictedState] + = useLocalState( + context, + "AntagSelection_predictedState", + new Set(data.selected_antags), + ); + + const enableAntags = (antags: string[]) => { + const newState = new Set(predictedState); + + for (const antag of antags) { + newState.add(antag); + } + + setPredictedState(newState); + + act("set_antags", { + antags, + toggled: true, + }); + }; + + const disableAntags = (antags: string[]) => { + const newState = new Set(predictedState); + + for (const antag of antags) { + newState.delete(antag); + } + + setPredictedState(newState); + + act("set_antags", { + antags, + toggled: false, + }); + }; + + const antagonistKeys = props.antagonists.map(antagonist => antagonist.key); + + return ( +
    + + + + + )}> + + {props.antagonists.map(antagonist => { + const isBanned = data.antag_bans + && data.antag_bans.indexOf(antagonist.key) !== -1; + + const daysLeft + = (data.antag_days_left && data.antag_days_left[antagonist.key]) + || 0; + + return ( + 0) + ? "banned" + : predictedState.has(antagonist.key) ? "on" : "off" + }`, + ])} + key={antagonist.key} + > + + + {antagonist.name} + + + + { + return ( +
    + {text} + { + index !== antagonist.description.length - 1 + && + } +
    + ); + }) + } position="bottom"> + { + if (isBanned) { + return; + } + + if (predictedState.has(antagonist.key)) { + disableAntags([antagonist.key]); + } else { + enableAntags([antagonist.key]); + } + }} + > + + + {isBanned && ( + + )} + + {daysLeft > 0 && ( + + {daysLeft} days left + + )} + +
    +
    +
    +
    + ); + })} +
    +
    + ); +}; + +export const AntagsPage = () => { + return ( + + + + + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx new file mode 100644 index 0000000000000..14f622362afe4 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx @@ -0,0 +1,164 @@ +import { exhaustiveCheck } from "common/exhaustive"; +import { useBackend, useLocalState } from "../../backend"; +import { Button, Stack } from "../../components"; +import { Window } from "../../layouts"; +import { PreferencesMenuData } from "./data"; +import { PageButton } from "./PageButton"; +import { AntagsPage } from "./AntagsPage"; +import { JobsPage } from "./JobsPage"; +import { MainPage } from "./MainPage"; +import { SpeciesPage } from "./SpeciesPage"; +import { QuirksPage } from "./QuirksPage"; + +enum Page { + Antags, + Main, + Jobs, + Species, + Quirks, +} + +const CharacterProfiles = (props: { + activeSlot: number, + onClick: (index: number) => void, + profiles: (string | null)[], +}) => { + const { profiles } = props; + + return ( + + {profiles.map((profile, slot) => ( + + + + ))} + + ); +}; + +export const CharacterPreferenceWindow = (props, context) => { + const { act, data } = useBackend(context); + + const [currentPage, setCurrentPage] = useLocalState(context, "currentPage", Page.Main); + + let pageContents; + + switch (currentPage) { + case Page.Antags: + pageContents = ; + break; + case Page.Jobs: + pageContents = ; + break; + case Page.Main: + pageContents = ( setCurrentPage(Page.Species)} + />); + + break; + case Page.Species: + pageContents = ( setCurrentPage(Page.Main)} + />); + + break; + case Page.Quirks: + pageContents = ; + break; + default: + exhaustiveCheck(currentPage); + } + + return ( + + + + + { + act("change_slot", { + slot: slot + 1, + }); + }} + profiles={data.character_profiles} + /> + + + {!data.content_unlocked && ( + + Buy BYOND premium for more slots! + + )} + + + + + + + + Character + + + + + + {/* + Fun fact: This isn't "Jobs" so that it intentionally + catches your eyes, because it's really important! + */} + + Occupations + + + + + + Antagonists + + + + + + Quirks + + + + + + + + + {pageContents} + + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreview.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreview.tsx new file mode 100644 index 0000000000000..ab27d2fe2f54a --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreview.tsx @@ -0,0 +1,15 @@ +import { ByondUi } from "../../components"; + +export const CharacterPreview = (props: { + height: string, + id: string, +}) => { + return (); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferenceWindow.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferenceWindow.tsx new file mode 100644 index 0000000000000..4bddd3305edbf --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferenceWindow.tsx @@ -0,0 +1,77 @@ +import { Stack } from "../../components"; +import { Window } from "../../layouts"; +import { KeybindingsPage } from "./KeybindingsPage"; +import { GamePreferencesPage } from "./GamePreferencesPage"; +import { PageButton } from "./PageButton"; +import { useBackend, useLocalState } from "../../backend"; +import { GamePreferencesSelectedPage, PreferencesMenuData } from "./data"; +import { exhaustiveCheck } from "common/exhaustive"; + +export const GamePreferenceWindow = (props: { + startingPage?: GamePreferencesSelectedPage, +}, context) => { + const { act, data } = useBackend(context); + + const [currentPage, setCurrentPage] + = useLocalState( + context, + "currentPage", + props.startingPage ?? GamePreferencesSelectedPage.Settings, + ); + + let pageContents; + + switch (currentPage) { + case GamePreferencesSelectedPage.Keybindings: + pageContents = ; + break; + case GamePreferencesSelectedPage.Settings: + pageContents = ; + break; + default: + exhaustiveCheck(currentPage); + } + + + return ( + + + + + + + + Settings + + + + + + Keybindings + + + + + + + + + {pageContents} + + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx new file mode 100644 index 0000000000000..7e0245ed7661e --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx @@ -0,0 +1,102 @@ +import { binaryInsertWith, sortBy } from "common/collections"; +import { InfernoNode } from "inferno"; +import { useBackend } from "../../backend"; +import { Box, Flex, Tooltip } from "../../components"; +import { PreferencesMenuData } from "./data"; +import features from "./preferences/features"; +import { FeatureValueInput } from "./preferences/features/base"; +import { TabbedMenu } from "./TabbedMenu"; + +type PreferenceChild = { + name: string, + children: InfernoNode, +}; + +const binaryInsertPreference = binaryInsertWith( + (child) => child.name, +); + +const sortByName = sortBy<[string, PreferenceChild[]]>(([name]) => name); + +export const GamePreferencesPage = (props, context) => { + const { act, data } = useBackend(context); + + const gamePreferences: Record = {}; + + for (const [featureId, value] of Object.entries( + data.character_preferences.game_preferences + )) { + const feature = features[featureId]; + + let nameInner: InfernoNode = feature?.name || featureId; + + if (feature?.description) { + nameInner = ( + + {nameInner} + + ); + } + + let name: InfernoNode = ( + + {nameInner} + + ); + + if (feature?.description) { + name = ( + + {name} + + ); + } + + const child = ( + + {name} + + + {feature && || ( + + ...is not filled out properly!!! + + )} + + + ); + + const entry = { + name: feature?.name || featureId, + children: child, + }; + + const category = feature?.category || "ERROR"; + + gamePreferences[category] + = binaryInsertPreference(gamePreferences[category] || [], entry); + } + + const gamePreferenceEntries: [string, InfernoNode][] = sortByName( + Object.entries(gamePreferences) + ).map( + ([category, preferences]) => { + return [category, preferences.map(entry => entry.children)]; + }); + + return ( + + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx new file mode 100644 index 0000000000000..46f6fa2bf6324 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx @@ -0,0 +1,441 @@ +import { binaryInsertWith } from "common/collections"; +import { classes } from "common/react"; +import { InfernoNode } from "inferno"; +import { useBackend } from "../../backend"; +import { Box, Button, Dropdown, Stack, Tooltip } from "../../components"; +import { createSetPreference, JoblessRole, JobPriority, PreferencesMenuData } from "./data"; +import { Job } from "./jobs/base"; +import * as Departments from "./jobs/departments"; + +const requireJob = require.context("./jobs/jobs", false, /.ts$/); +const jobsByDepartment = new Map(); + +const binaryInsertJob = binaryInsertWith((job: Job) => { + return job.name; +}); + +const PRIORITY_BUTTON_SIZE = "18px"; + +for (const jobKey of requireJob.keys()) { + const job = requireJob<{ + default?: Job, + }>(jobKey).default; + + if (!job) { + continue; + } + + + let departmentInfo = jobsByDepartment.get(job.department); + if (departmentInfo === undefined) { + departmentInfo = { + jobs: [], + head: undefined, + }; + + jobsByDepartment.set(job.department, departmentInfo); + } + + if (job.department.head === job.name) { + departmentInfo.head = job; + } else { + departmentInfo.jobs = binaryInsertJob(departmentInfo.jobs, job); + } +} + +const PriorityButton = (props: { + name: string, + color: string, + modifier?: string, + enabled: boolean, + onClick: () => void, +}) => { + const className = `PreferencesMenu__Jobs__departments__priority`; + + return ( + + + ); + + if (typingHotkey && onClick) { + return ( + // onClick will cancel it + + {child} + + ); + } else { + return child; + } + } +} + +const KeybindingName = (props: { + keybinding: Keybinding, +}) => { + const { keybinding } = props; + + return keybinding.description + ? ( + + + {keybinding.name} + + + ) + : {keybinding.name}; +}; + +KeybindingName.defaultHooks = { + onComponentShouldUpdate: (lastProps, nextProps) => { + return lastProps.keybinding !== nextProps.keybinding; + }, +}; + +const ResetToDefaultButton = (props: { + keybindingId: string, +}, context) => { + const { act } = useBackend(context); + + return ( + + ); +}; + +export class KeybindingsPage extends Component<{}, KeybindingsPageState> { + cancelNextKeyUp?: number; + keybindingOnClicks: Record void)[]> = {}; + lastKeybinds?: PreferencesMenuData["keybindings"]; + + state: KeybindingsPageState = { + lastKeyboardEvent: undefined, + keybindings: undefined, + selectedKeybindings: undefined, + rebindingHotkey: undefined, + }; + + constructor() { + super(); + + this.handleKeyDown = this.handleKeyDown.bind(this); + this.handleKeyUp = this.handleKeyUp.bind(this); + } + + componentDidMount() { + this.populateSelectedKeybindings(); + this.populateKeybindings(); + } + + componentDidUpdate() { + const { data } = useBackend(this.context); + + // keybindings is static data, so it'll pass `===` checks. + // This'll change when resetting to defaults. + if (data.keybindings !== this.lastKeybinds) { + this.populateSelectedKeybindings(); + } + } + + setRebindingHotkey(value?: string) { + const { act } = useBackend(this.context); + + this.setState((state) => { + let selectedKeybindings = state.selectedKeybindings; + if (!selectedKeybindings) { + return state; + } + + if (!state.rebindingHotkey) { + return state; + } + + selectedKeybindings = { ...selectedKeybindings }; + + const [keybindName, slot] = state.rebindingHotkey; + + if (selectedKeybindings[keybindName]) { + if (value) { + selectedKeybindings[keybindName][Math.min( + selectedKeybindings[keybindName].length, + slot + )] = value; + } else { + selectedKeybindings[keybindName].splice(slot, 1); + } + } else if (!value) { + return state; + } else { + selectedKeybindings[keybindName] = [value]; + } + + act("set_keybindings", { + "keybind_name": keybindName, + "hotkeys": selectedKeybindings[keybindName], + }); + + return { + lastKeyboardEvent: undefined, + rebindingHotkey: undefined, + selectedKeybindings, + }; + }); + } + + handleKeyDown(keyEvent: KeyEvent) { + const event = keyEvent.event; + const rebindingHotkey = this.state.rebindingHotkey; + + if (!rebindingHotkey) { + return; + } + + event.preventDefault(); + + this.cancelNextKeyUp = keyEvent.code; + + if (isStandardKey(event)) { + this.setRebindingHotkey(formatKeyboardEvent(event)); + return; + } else if (event.key === "Esc") { + this.setRebindingHotkey(undefined); + return; + } + + this.setState({ + lastKeyboardEvent: event, + }); + } + + handleKeyUp(keyEvent: KeyEvent) { + if (this.cancelNextKeyUp === keyEvent.code) { + this.cancelNextKeyUp = undefined; + keyEvent.event.preventDefault(); + } + } + + getKeybindingOnClick( + keybindingId: string, + slot: number, + ): () => void { + if (!this.keybindingOnClicks[keybindingId]) { + this.keybindingOnClicks[keybindingId] = []; + } + + if (!this.keybindingOnClicks[keybindingId][slot]) { + this.keybindingOnClicks[keybindingId][slot] = () => { + if (this.state.rebindingHotkey === undefined) { + this.setState({ + lastKeyboardEvent: undefined, + rebindingHotkey: [keybindingId, slot], + }); + } else { + this.setState({ + lastKeyboardEvent: undefined, + rebindingHotkey: undefined, + }); + } + }; + } + + return this.keybindingOnClicks[keybindingId][slot]; + } + + getTypingHotkey(keybindingId: string, slot: number): string | undefined { + const { lastKeyboardEvent, rebindingHotkey } = this.state; + + if (!rebindingHotkey) { + return undefined; + } + + if (rebindingHotkey[0] !== keybindingId + || rebindingHotkey[1] !== slot + ) { + return undefined; + } + + if (lastKeyboardEvent === undefined) { + return "..."; + } + + return formatKeyboardEvent(lastKeyboardEvent); + } + + async populateKeybindings() { + const keybindingsResponse = await fetchRetry(resolveAsset("keybindings.json")); + const keybindingsData: Keybindings = await keybindingsResponse.json(); + + this.setState({ + keybindings: keybindingsData, + }); + } + + populateSelectedKeybindings() { + const { data } = useBackend(this.context); + + this.lastKeybinds = data.keybindings; + + this.setState({ + selectedKeybindings: Object.fromEntries( + Object.entries(data.keybindings) + .map(([keybind, hotkeys]) => { + return [keybind, hotkeys.filter(value => value !== "Unbound")]; + }) + ), + }); + } + + render() { + const keybindings = this.state.keybindings; + + if (!keybindings) { + return Loading keybindings...; + } + + const keybindingEntries = sortKeybindingsByCategory( + Object.entries(keybindings) + ); + + moveToBottom(keybindingEntries, "EMOTE"); + moveToBottom(keybindingEntries, "ADMIN"); + + return ( + <> + + + { + return [category, ( + + {sortKeybindings(Object.entries(keybindings)).map( + ([keybindingId, keybinding]) => { + const keys + = this.state.selectedKeybindings![keybindingId] + || []; + + const name = ( + + + + ); + + return ( + + + {name} + + {range(0, 3).map(key => ( + + + + ))} + + + + + + + ); + } + )} + + )]; + } + )} + /> + + ); + } +} diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx new file mode 100644 index 0000000000000..408cc297819e5 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx @@ -0,0 +1,596 @@ +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 { CharacterPreview } from "./CharacterPreview"; +import { RandomizationButton } from "./RandomizationButton"; +import { ServerPreferencesFetcher } from "./ServerPreferencesFetcher"; +import { MultiNameInput, NameInput } from "./names"; +import { Gender, GENDERS } from "./preferences/gender"; +import features from "./preferences/features"; +import { FeatureChoicedServerData, FeatureValueInput } from "./preferences/features/base"; +import { filterMap, sortBy } from "common/collections"; +import { useRandomToggleState } from "./useRandomToggleState"; + +const CLOTHING_CELL_SIZE = 48; +const CLOTHING_SIDEBAR_ROWS = 9; + +const CLOTHING_SELECTION_CELL_SIZE = 48; +const CLOTHING_SELECTION_WIDTH = 5.4; +const CLOTHING_SELECTION_MULTIPLIER = 5.2; + +const CharacterControls = (props: { + handleRotate: () => void, + handleOpenSpecies: () => void, + gender: Gender, + setGender: (gender: Gender) => void, + showGender: boolean, +}) => { + return ( + + + + + + + + + + + {Object.entries(catalog.icons).map(([name, image], index) => { + return ( + + + + ); + })} + + + + + + ); +}; + +const GenderButton = (props: { + handleSetGender: (gender: Gender) => void, + gender: Gender, +}, context) => { + const [genderMenuOpen, setGenderMenuOpen] = useLocalState(context, "genderMenuOpen", false); + + return ( + + {[Gender.Male, Gender.Female, Gender.Other].map(gender => { + return ( + + + + ); +}; + +const createSetRandomization = ( + act: typeof sendAct, + preference: string, +) => (newSetting: RandomSetting) => { + act("set_random_preference", { + preference, + value: newSetting, + }); +}; + +const sortPreferences = sortBy<[string, unknown]>( + ([featureId, _]) => { + const feature = features[featureId]; + return feature?.name; + }); + +const PreferenceList = (props: { + act: typeof sendAct, + preferences: Record, + randomizations: Record, +}) => { + return ( + + + { + sortPreferences(Object.entries(props.preferences)) + .map(([featureId, value]) => { + const feature = features[featureId]; + const randomSetting = props.randomizations[featureId]; + + if (feature === undefined) { + return ( + + Feature {featureId} is not recognized. + + ); + } + + return ( + + + {randomSetting && ( + + + + )} + + + + + + + ); + }) + } + + + ); +}; + +export const MainPage = (props: { + openSpecies: () => void, +}, context) => { + const { act, data } = useBackend(context); + const [currentClothingMenu, setCurrentClothingMenu] + = useLocalState(context, "currentClothingMenu", null); + const [multiNameInputOpen, setMultiNameInputOpen] + = useLocalState(context, "multiNameInputOpen", false); + const [randomToggleEnabled] = useRandomToggleState(context); + + return ( + { + const currentSpeciesData = serverData && serverData.species[ + data + .character_preferences + .misc + .species + ]; + + const contextualPreferences + = data.character_preferences.secondary_features || []; + + const mainFeatures = [ + ...Object.entries(data.character_preferences.clothing), + ...Object.entries(data.character_preferences.features) + .filter(([featureName]) => { + if (!currentSpeciesData) { + return false; + } + + return currentSpeciesData + .enabled_features + .indexOf(featureName) !== -1; + }), + ]; + + const randomBodyEnabled + = (data.character_preferences.non_contextual.random_body + !== RandomSetting.Disabled) + || randomToggleEnabled; + + const getRandomization = ( + preferences: Record + ): Record => { + 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)); + + const nonContextualPreferences + = { + ...data.character_preferences.non_contextual, + }; + + if (randomBodyEnabled) { + nonContextualPreferences["random_species"] + = data.character_preferences.randomization["species"]; + } else { + // We can't use random_name/is_accessible because the + // server doesn't know whether the random toggle is on. + delete nonContextualPreferences["random_name"]; + } + + return ( + <> + {multiNameInputOpen && setMultiNameInputOpen(false)} + handleRandomizeName={preference => act("randomize_name", { + preference, + })} + handleUpdateName={(nameType, value) => act("set_preference", { + preference: nameType, + value, + })} + names={data.character_preferences.names} + />} + + + + + + { + act("rotate"); + }} + setGender={createSetPreference(act, "gender")} + showGender={ + currentSpeciesData ? !!currentSpeciesData.sexes : true + } + /> + + + + + + + + { + setMultiNameInputOpen(true); + }} + /> + + + + + + + + {mainFeatures + .map(([clothingKey, clothing]) => { + const catalog = ( + serverData + && serverData[clothingKey] as + FeatureChoicedServerData & { + name: string, + } + ); + + return catalog && ( + + { + setCurrentClothingMenu(null); + }} + handleOpen={() => { + setCurrentClothingMenu(clothingKey); + }} + handleSelect={createSetPreference(act, clothingKey)} + randomization={ + randomizationOfMainFeatures[clothingKey] + } + setRandomization={createSetRandomization( + act, + clothingKey, + )} + /> + + ); + })} + + + + + + + + + + + + + ); + }} /> + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/PageButton.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/PageButton.tsx new file mode 100644 index 0000000000000..8ac3de359759d --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/PageButton.tsx @@ -0,0 +1,30 @@ +import { InfernoNode } from "inferno"; +import { Button } from "../../components"; + +export const PageButton =

    (props: { + currentPage: P, + page: P, + otherActivePages?: P[], + + setPage: (page: P) => void, + + children?: InfernoNode, +}) => { + const pageIsActive = props.currentPage === props.page + || ( + props.otherActivePages + && props.otherActivePages.indexOf(props.currentPage) !== -1 + ); + + return ( + + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx new file mode 100644 index 0000000000000..75e688bf76f6f --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx @@ -0,0 +1,327 @@ +import { StatelessComponent } from "inferno"; +import { Box, Icon, Stack, Tooltip } from "../../components"; +import { PreferencesMenuData, Quirk } from "./data"; +import { useBackend, useLocalState } from "../../backend"; +import { ServerPreferencesFetcher } from "./ServerPreferencesFetcher"; + +const getValueClass = (value: number): string => { + if (value > 0) { + return "positive"; + } else if (value < 0) { + return "negative"; + } else { + return "neutral"; + } +}; + +const QuirkList = (props: { + quirks: [string, Quirk & { + failTooltip?: string; + }][], + onClick: (quirkName: string, quirk: Quirk) => void, +}) => { + return ( + // Stack is not used here for a variety of IE flex bugs + + {props.quirks.map(([quirkKey, quirk]) => { + const className = "PreferencesMenu__Quirks__QuirkList__quirk"; + + const child = ( + { + props.onClick(quirkKey, quirk); + }} + > + + + + + + + + + + + + + {quirk.name} + + + + {quirk.value} + + + + + + {quirk.description} + + + + + + ); + + if (quirk.failTooltip) { + return ( + + {child} + + ); + } else { + return child; + } + })} + + ); +}; + +const StatDisplay: StatelessComponent<{}> = (props) => { + return ( + + {props.children} + + ); +}; + +export const QuirksPage = (props, context) => { + const { act, data } = useBackend(context); + + const [selectedQuirks, setSelectedQuirks] = useLocalState( + context, + `selectedQuirks_${data.active_slot}`, + data.selected_quirks, + ); + + return ( + { + if (!data) { + return Loading quirks...; + } + + const { + max_positive_quirks: maxPositiveQuirks, + quirk_blacklist: quirkBlacklist, + quirk_info: quirkInfo, + } = data.quirks; + + const quirks = Object.entries(quirkInfo); + quirks.sort(([_, quirkA], [__, quirkB]) => { + if (quirkA.value === quirkB.value) { + return (quirkA.name > quirkB.name) ? 1 : -1; + } else { + return quirkA.value - quirkB.value; + } + }); + + let balance = 0; + let positiveQuirks = 0; + + for (const selectedQuirkName of selectedQuirks) { + const selectedQuirk = quirkInfo[selectedQuirkName]; + if (!selectedQuirk) { + continue; + } + + if (selectedQuirk.value > 0) { + positiveQuirks += 1; + } + + balance += selectedQuirk.value; + } + + const getReasonToNotAdd = (quirkName: string) => { + const quirk = quirkInfo[quirkName]; + + if ( + quirk.value > 0 + ) { + if (positiveQuirks >= maxPositiveQuirks) { + return "You can't have any more positive quirks!"; + } else if (balance + quirk.value > 0) { + return "You need a negative quirk to balance this out!"; + } + } + + const selectedQuirkNames = selectedQuirks.map(quirkKey => { + return quirkInfo[quirkKey].name; + }); + + for (const blacklist of quirkBlacklist) { + if (blacklist.indexOf(quirk.name) === -1) { + continue; + } + + for (const incompatibleQuirk of blacklist) { + if ( + incompatibleQuirk !== quirk.name + && selectedQuirkNames.indexOf(incompatibleQuirk) !== -1 + ) { + return `This is incompatible with ${incompatibleQuirk}!`; + } + } + } + + return undefined; + }; + + const getReasonToNotRemove = (quirkName: string) => { + const quirk = quirkInfo[quirkName]; + + if (balance - quirk.value > 0) { + return "You need to remove a positive quirk first!"; + } + + return undefined; + }; + + return ( + + + + + + Positive Quirks + + + + + + {positiveQuirks} / {maxPositiveQuirks} + + + + + + Available Quirks + + + + + { + if (getReasonToNotAdd(quirkName) !== undefined) { + return; + } + + setSelectedQuirks(selectedQuirks.concat(quirkName)); + + act("give_quirk", { quirk: quirk.name }); + }} + quirks={quirks.filter(([quirkName, _]) => { + return selectedQuirks + .indexOf(quirkName) === -1; + }).map(([quirkName, quirk]) => { + return [quirkName, { + ...quirk, + failTooltip: getReasonToNotAdd(quirkName), + }]; + })} + /> + + + + + + + + + + + + + Quirk Balance + + + + + + {balance} + + + + + + Current Quirks + + + + + { + if (getReasonToNotRemove(quirkName) !== undefined) { + return; + } + + setSelectedQuirks( + selectedQuirks + .filter(otherQuirk => quirkName !== otherQuirk), + ); + + act("remove_quirk", { quirk: quirk.name }); + }} + quirks={quirks.filter(([quirkName, _]) => { + return selectedQuirks + .indexOf(quirkName) !== -1; + }).map(([quirkName, quirk]) => { + return [quirkName, { + ...quirk, + failTooltip: getReasonToNotRemove(quirkName), + }]; + })} + /> + + + + + ); + }} + /> + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/RandomizationButton.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/RandomizationButton.tsx new file mode 100644 index 0000000000000..ea9a4a64a604a --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/RandomizationButton.tsx @@ -0,0 +1,64 @@ +import { Dropdown, Icon } from "../../components"; +import { RandomSetting } from "./data"; +import { exhaustiveCheck } from "common/exhaustive"; + +export const RandomizationButton = (props: { + dropdownProps?: Record, + setValue: (newValue: RandomSetting) => void, + value: RandomSetting, +}) => { + const { + dropdownProps = {}, + setValue, + value, + } = props; + + let color; + + switch (value) { + case RandomSetting.AntagOnly: + color = "orange"; + break; + case RandomSetting.Disabled: + color = "red"; + break; + case RandomSetting.Enabled: + color = "green"; + break; + default: + exhaustiveCheck(value); + } + + return ( + + )} + options={[ + { + displayText: "Do not randomize", + value: RandomSetting.Disabled, + }, + + { + displayText: "Always randomize", + value: RandomSetting.Enabled, + }, + + { + displayText: "Randomize when antagonist", + value: RandomSetting.AntagOnly, + }, + ]} + nochevron + onSelected={setValue} + width="auto" + /> + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/ServerPreferencesFetcher.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/ServerPreferencesFetcher.tsx new file mode 100644 index 0000000000000..d21cff506ac59 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/ServerPreferencesFetcher.tsx @@ -0,0 +1,38 @@ +import { Component, InfernoNode } from "inferno"; +import { resolveAsset } from "../../assets"; +import { fetchRetry } from "../../http"; +import { ServerData } from "./data"; + +// Cache response so it's only sent once +let fetchServerData: Promise | undefined; + +export class ServerPreferencesFetcher extends Component<{ + render: (serverData: ServerData | undefined) => InfernoNode, +}, { + serverData?: ServerData; +}> { + state = { + serverData: undefined, + }; + + componentDidMount() { + this.populateServerData(); + } + + async populateServerData() { + if (!fetchServerData) { + fetchServerData = fetchRetry(resolveAsset("preferences.json")) + .then(response => response.json()); + } + + const preferencesData: ServerData = await fetchServerData; + + this.setState({ + serverData: preferencesData, + }); + } + + render() { + return this.props.render(this.state.serverData); + } +} diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/SpeciesPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/SpeciesPage.tsx new file mode 100644 index 0000000000000..237722163e4e4 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/SpeciesPage.tsx @@ -0,0 +1,360 @@ +import { classes } from "common/react"; +import { useBackend } from "../../backend"; +import { BlockQuote, Box, Button, Divider, Icon, Section, Stack, Tooltip } from "../../components"; +import { CharacterPreview } from "./CharacterPreview"; +import { createSetPreference, Food, PreferencesMenuData, ServerData, ServerSpeciesData } from "./data"; +import { Feature, Species, fallbackSpecies } from "./preferences/species/base"; +import { ServerPreferencesFetcher } from "./ServerPreferencesFetcher"; + +const requireSpecies = require.context("./preferences/species"); + +const FOOD_ICONS = { + [Food.Cloth]: "tshirt", + [Food.Dairy]: "cheese", + [Food.Fried]: "bacon", + [Food.Fruit]: "apple-alt", + [Food.Grain]: "bread-slice", + [Food.Gross]: "trash", + [Food.Junkfood]: "pizza-slice", + [Food.Meat]: "hamburger", + [Food.Raw]: "drumstick-bite", + [Food.Sugar]: "candy-cane", + [Food.Toxic]: "biohazard", + [Food.Vegetables]: "carrot", +}; + +const FOOD_NAMES: Record = { + [Food.Cloth]: "Clothing", + [Food.Dairy]: "Dairy", + [Food.Fried]: "Fried food", + [Food.Fruit]: "Fruit", + [Food.Grain]: "Grain", + [Food.Gross]: "Gross food", + [Food.Junkfood]: "Junk food", + [Food.Meat]: "Meat", + [Food.Raw]: "Raw", + [Food.Sugar]: "Sugar", + [Food.Toxic]: "Toxic food", + [Food.Vegetables]: "Vegetables", +}; + +const IGNORE_UNLESS_LIKED: Set = new Set([ + Food.Cloth, + Food.Gross, + Food.Toxic, +]); + +const notIn = function (set: Set) { + return (value: T) => { + return !set.has(value); + }; +}; + +const FoodList = (props: { + food: Food[], + icon: string, + name: string, + className: string, +}) => { + if (props.food.length === 0) { + return null; + } + + return ( + + {props.name} + + + {props.food + .reduce((names, food) => { + const foodName = FOOD_NAMES[food]; + return foodName ? names.concat(foodName) : names; + }, []).join(", ")} + + + }> + + {props.food.map(food => { + return FOOD_ICONS[food] + && ( + + + + ); + })} + + + ); +}; + +const Diet = (props: { + likedFood: Food[], + dislikedFood: Food[], + toxicFood: Food[], +}) => { + return ( + + + + + + + + + + + + + + ); +}; + +const SpeciesFeature = (props: { + className: string, + feature: Feature, +}) => { + const { className, feature } = props; + + return ( + + {feature.name} + + {feature.description} + + }> + + + + + ); +}; + +const SpeciesFeatures = (props: { + features: Species["features"], +}) => { + const { good, neutral, bad } = props.features; + + return ( + + + + {good.map(feature => { + return ( + + + + ); + })} + + + + + {neutral.map(feature => { + return ( + + + + ); + })} + + + + {bad.map(feature => { + return ( + + + + ); + })} + + + ); +}; + +const SpeciesPageInner = (props: { + handleClose: () => void, + species: ServerData["species"], +}, context) => { + const { act, data } = useBackend(context); + const setSpecies = createSetPreference(act, "species"); + + let species: [string, Species & ServerSpeciesData][] + = Object.entries(props.species) + .map(([species, serverData]) => { + return [ + species, + { + ...serverData, + ...(requireSpecies.keys().indexOf(`./${species}`) === -1 + ? fallbackSpecies + : requireSpecies(`./${species}`).default) as Species, + }, + ]; + }); + + // Humans are always the top of the list + const humanIndex = species.findIndex(([species]) => species === "human"); + const swapWith = species[0]; + species[0] = species[humanIndex]; + species[humanIndex] = swapWith; + + const currentSpecies = species.filter(([speciesKey]) => { + return speciesKey === data.character_preferences.misc.species; + })[0][1]; + + const { lore } = currentSpecies; + + return ( + + + + + + + + + + {species.map(([speciesKey, species]) => { + return ( + + + + ); + })} + + + + + + + + +

    ) + }> +
    + {currentSpecies.description} +
    + +
    + +
    +
    + + + + + + + + + {lore && ( + +
    +
    + {lore.map((text, index) => ( + + {text} + {index !== lore.length - 1 + && (<>

    )} +
    + ))} +
    +
    +
    + )} + + + + + + ); +}; + +export const SpeciesPage = (props: { + closeSpecies: () => void, +}) => { + return ( + { + if (serverData) { + return (); + } else { + return Loading species...; + } + }} + /> + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx new file mode 100644 index 0000000000000..450bf0dc3e498 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx @@ -0,0 +1,94 @@ +import { Component, createRef, InfernoNode, RefObject } from "inferno"; +import { Button, Section, Stack } from "../../components"; +import { FlexProps } from "../../components/Flex"; + +type TabbedMenuProps = { + categoryEntries: [string, InfernoNode][], + contentProps?: FlexProps, +}; + +export class TabbedMenu extends Component { + categoryRefs: Record> = {}; + sectionRef: RefObject = createRef(); + + getCategoryRef(category: string): RefObject { + if (!this.categoryRefs[category]) { + this.categoryRefs[category] = createRef(); + } + + return this.categoryRefs[category]; + } + + render() { + return ( + + + + {this.props.categoryEntries.map(([category]) => { + return ( + + + + ); + })} + + + + + + {this.props.categoryEntries.map( + ([category, children]) => { + return ( + +
    + {children} +
    +
    + ); + } + )} +
    +
    +
    + ); + } +} diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/abductor.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/abductor.ts new file mode 100644 index 0000000000000..9f0abe6da87af --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/abductor.ts @@ -0,0 +1,24 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +const Abductor: Antagonist = { + key: "abductor", + name: "Abductor", + description: [ + multiline` + Abductors are technologically advanced alien society set on cataloging + all species in the system. Unfortunately for their subjects their methods + are quite invasive. + `, + + multiline` + You and a partner will become the abductor scientist and agent duo. + As an agent, abduct unassuming victims and bring them back to your UFO. + As a scientist, scout out victims for your agent, keep them safe, and + operate on whoever they bring back. + `, + ], + category: Category.Midround, +}; + +export default Abductor; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/blob.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/blob.ts new file mode 100644 index 0000000000000..1d2de66ab4816 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/blob.ts @@ -0,0 +1,20 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +export const BLOB_MECHANICAL_DESCRIPTION = multiline` + The blob infests the station and destroys everything in its path, including + hull, fixtures, and creatures. Spread your mass, collect resources, and + consume the entire station. Make sure to prepare your defenses, because the + crew will be alerted to your presence! +`; + +const Blob: Antagonist = { + key: "blob", + name: "Blob", + description: [ + BLOB_MECHANICAL_DESCRIPTION, + ], + category: Category.Midround, +}; + +export default Blob; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/blobinfection.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/blobinfection.ts new file mode 100644 index 0000000000000..45a8a03dc8445 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/blobinfection.ts @@ -0,0 +1,18 @@ +import { Antagonist, Category } from "../base"; +import { BLOB_MECHANICAL_DESCRIPTION } from "./blob"; +import { multiline } from "common/string"; + +const BlobInfection: Antagonist = { + key: "blobinfection", + name: "Blob Infection", + description: [ + multiline` + At any point in the middle of the shift, be strucken with an infection + that will turn you into the terrifying blob. + `, + BLOB_MECHANICAL_DESCRIPTION, + ], + category: Category.Midround, +}; + +export default BlobInfection; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/bloodbrother.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/bloodbrother.ts new file mode 100644 index 0000000000000..3a05cf1ec00ef --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/bloodbrother.ts @@ -0,0 +1,17 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +const BloodBrother: Antagonist = { + key: "bloodbrother", + name: "Blood Brother", + description: [ + multiline` + Team up with other crew members as blood brothers to combine the strengths + of your departments, break each other out of prison, and overwhelm the + station. + `, + ], + category: Category.Roundstart, +}; + +export default BloodBrother; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/changeling.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/changeling.ts new file mode 100644 index 0000000000000..114e6413850e8 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/changeling.ts @@ -0,0 +1,21 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +const Changeling: Antagonist = { + key: "changeling", + name: "Changeling", + description: [ + multiline` + A highly intelligent alien predator that is capable of altering their + shape to flawlessly resemble a human. + `, + + multiline` + Transform yourself or others into different identities, and buy from an + arsenal of biological weaponry with the DNA you collect. + `, + ], + category: Category.Roundstart, +}; + +export default Changeling; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/cultist.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/cultist.ts new file mode 100644 index 0000000000000..ea27413786a9e --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/cultist.ts @@ -0,0 +1,23 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +const Cultist: Antagonist = { + key: "cultist", + name: "Cultist", + description: [ + multiline` + The Geometer of Blood, Nar-Sie, has sent a number of her followers to + Space Station 13. As a cultist, you have an abundance of cult magics at + your disposal, something for all situations. You must work with your + brethren to summon an avatar of your eldritch goddess! + `, + + multiline` + Armed with blood magic, convert crew members to the Blood Cult, sacrifice + those who get in the way, and summon Nar-Sie. + `, + ], + category: Category.Roundstart, +}; + +export default Cultist; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/familyheadaspirant.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/familyheadaspirant.ts new file mode 100644 index 0000000000000..6ed2e8dda5756 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/familyheadaspirant.ts @@ -0,0 +1,18 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; +import { GANGSTER_MECHANICAL_DESCRIPTION } from "./gangster"; + +const FamilyHeadAspirant: Antagonist = { + key: "familyheadaspirant", + name: "Family Head Aspirant", + description: [ + multiline` + A form of family leader that can activate at any point in the middle + of the shift. + `, + GANGSTER_MECHANICAL_DESCRIPTION, + ], + category: Category.Midround, +}; + +export default FamilyHeadAspirant; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/gangster.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/gangster.ts new file mode 100644 index 0000000000000..38df184b26aad --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/gangster.ts @@ -0,0 +1,19 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +export const GANGSTER_MECHANICAL_DESCRIPTION + = multiline` + Convince people to join your family, wear your uniform, tag turf + for the family, and accomplish your family's goals. + `; + +const Gangster: Antagonist = { + key: "gangster", + name: "Family Leader", + description: [ + GANGSTER_MECHANICAL_DESCRIPTION, + ], + category: Category.Roundstart, +}; + +export default Gangster; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/headrevolutionary.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/headrevolutionary.ts new file mode 100644 index 0000000000000..ea670d854dfae --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/headrevolutionary.ts @@ -0,0 +1,20 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +export const REVOLUTIONARY_MECHANICAL_DESCRIPTION + = multiline` + Armed with a flash, convert as many people to the revolution as you can. + Kill or exile all heads of staff on the station. + `; + +const HeadRevolutionary: Antagonist = { + key: "headrevolutionary", + name: "Head Revolutionary", + description: [ + "VIVA LA REVOLUTION!", + REVOLUTIONARY_MECHANICAL_DESCRIPTION, + ], + category: Category.Roundstart, +}; + +export default HeadRevolutionary; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/heretic.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/heretic.ts new file mode 100644 index 0000000000000..611ad714eda84 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/heretic.ts @@ -0,0 +1,24 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +export const HERETIC_MECHANICAL_DESCRIPTION + = multiline` + Find hidden influences and sacrifice crew members to gain magical + powers and ascend as one of several paths. + `; + +const Heretic: Antagonist = { + key: "heretic", + name: "Heretic", + description: [ + multiline` + Forgotten, devoured, gutted. Humanity has forgotten the eldritch forces + of decay, but the mansus veil has weakened. We will make them taste fear + again... + `, + HERETIC_MECHANICAL_DESCRIPTION, + ], + category: Category.Roundstart, +}; + +export default Heretic; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/hereticsmuggler.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/hereticsmuggler.ts new file mode 100644 index 0000000000000..8667e0b199911 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/hereticsmuggler.ts @@ -0,0 +1,14 @@ +import { Antagonist, Category } from "../base"; +import { HERETIC_MECHANICAL_DESCRIPTION } from "./heretic"; + +const HereticSmuggler: Antagonist = { + key: "hereticsmuggler", + name: "Heretic Smuggler", + description: [ + "A form of heretic that can activate when joining an ongoing shift.", + HERETIC_MECHANICAL_DESCRIPTION, + ], + category: Category.Latejoin, +}; + +export default HereticSmuggler; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/loneoperative.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/loneoperative.ts new file mode 100644 index 0000000000000..b0de7f13b3e11 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/loneoperative.ts @@ -0,0 +1,19 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; +import { OPERATIVE_MECHANICAL_DESCRIPTION } from "./operative"; + +const LoneOperative: Antagonist = { + key: "loneoperative", + name: "Lone Operative", + description: [ + multiline` + A solo nuclear operative that has a higher chance of spawning the longer + the nuclear authentication disk stays in one place. + `, + + OPERATIVE_MECHANICAL_DESCRIPTION, + ], + category: Category.Midround, +}; + +export default LoneOperative; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/malfai.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/malfai.ts new file mode 100644 index 0000000000000..ed6b5d7f92300 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/malfai.ts @@ -0,0 +1,20 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +export const MALF_AI_MECHANICAL_DESCRIPTION + = multiline` + With a law zero to complete your objectives at all costs, combine your + omnipotence and malfunction modules to wreak havoc across the station. + Go delta to destroy the station and all those who opposed you. + `; + +const MalfAI: Antagonist = { + key: "malfai", + name: "Malfunctioning AI", + description: [ + MALF_AI_MECHANICAL_DESCRIPTION, + ], + category: Category.Roundstart, +}; + +export default MalfAI; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/malfaimidround.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/malfaimidround.ts new file mode 100644 index 0000000000000..1334e48379b00 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/malfaimidround.ts @@ -0,0 +1,18 @@ +import { multiline } from "common/string"; +import { Antagonist, Category } from "../base"; +import { MALF_AI_MECHANICAL_DESCRIPTION } from "./malfai"; + +const MalfAIMidround: Antagonist = { + key: "malfaimidround", + name: "Value Drifted AI", + description: [ + multiline` + A form of malfunctioning AI that is given to existing AIs in the middle + of the shift. + `, + MALF_AI_MECHANICAL_DESCRIPTION, + ], + category: Category.Midround, +}; + +export default MalfAIMidround; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/monkey.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/monkey.ts new file mode 100644 index 0000000000000..224c1d45516ad --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/monkey.ts @@ -0,0 +1,17 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +const Monkey: Antagonist = { + key: "monkey", + name: "Monkey", + description: [ + multiline` + When the round starts, become infected with Jungle Fever, a disease which + turns its victim into a monkey. These monkeys can attack humans and give + them the deadly virus. Turn the entire crew into monkeys! + `, + ], + category: Category.Roundstart, +}; + +export default Monkey; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/obsessed.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/obsessed.ts new file mode 100644 index 0000000000000..4caf77f29d6e9 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/obsessed.ts @@ -0,0 +1,17 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +const Obsessed: Antagonist = { + key: "obsessed", + name: "Obsessed", + description: [ + multiline` + You're obsessed with someone! Your obsession may begin to notice their + personal items are stolen and their coworkers have gone missing, + but will they realize they are your next victim in time? + `, + ], + category: Category.Midround, +}; + +export default Obsessed; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/operative.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/operative.ts new file mode 100644 index 0000000000000..582c74f003fbf --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/operative.ts @@ -0,0 +1,25 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +export const OPERATIVE_MECHANICAL_DESCRIPTION = multiline` + Retrieve the nuclear authentication disk, use it to activate the nuclear + fission explosive, and destroy the station. +`; + +const Operative: Antagonist = { + key: "operative", + name: "Nuclear Operative", + description: [ + multiline` + Congratulations, agent. You have been chosen to join the Syndicate + Nuclear Operative strike team. Your mission, whether or not you choose + to accept it, is to destroy Nanotrasen's most advanced research facility! + That's right, you're going to Space Station 13. + `, + + OPERATIVE_MECHANICAL_DESCRIPTION, + ], + category: Category.Roundstart, +}; + +export default Operative; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/operativemidround.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/operativemidround.ts new file mode 100644 index 0000000000000..ae23adc8151de --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/operativemidround.ts @@ -0,0 +1,18 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; +import { OPERATIVE_MECHANICAL_DESCRIPTION } from "./operative"; + +const OperativeMidround: Antagonist = { + key: "operativemidround", + name: "Nuclear Assailant", + description: [ + multiline` + A form of nuclear operative that is offered to ghosts in the middle + of the shift. + `, + OPERATIVE_MECHANICAL_DESCRIPTION, + ], + category: Category.Midround, +}; + +export default OperativeMidround; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/provocateur.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/provocateur.ts new file mode 100644 index 0000000000000..a55ab96435d33 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/provocateur.ts @@ -0,0 +1,19 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; +import { REVOLUTIONARY_MECHANICAL_DESCRIPTION } from "./headrevolutionary"; + +const Provocateur: Antagonist = { + key: "provocateur", + name: "Provocateur", + description: [ + multiline` + A form of head revolutionary that can activate when joining an ongoing + shift. + `, + + REVOLUTIONARY_MECHANICAL_DESCRIPTION, + ], + category: Category.Latejoin, +}; + +export default Provocateur; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/revenant.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/revenant.ts new file mode 100644 index 0000000000000..f351dac602a73 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/revenant.ts @@ -0,0 +1,17 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +const Revenant: Antagonist = { + key: "revenant", + name: "Revenant", + description: [ + multiline` + Become the mysterious revenant. Break windows, overload lights, and eat + the crew's life force, all while talking to your old community of + disgruntled ghosts. + `, + ], + category: Category.Midround, +}; + +export default Revenant; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/sentientdisease.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/sentientdisease.ts new file mode 100644 index 0000000000000..4691fda264e4a --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/sentientdisease.ts @@ -0,0 +1,16 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +const SentientDisease: Antagonist = { + key: "sentientdisease", + name: "Sentient Disease", + description: [ + multiline` + Mutate and spread yourself and infect as much of the crew as possible + with a deadly plague of your own creation. + `, + ], + category: Category.Midround, +}; + +export default SentientDisease; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/spacedragon.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/spacedragon.ts new file mode 100644 index 0000000000000..f4bebfcf50e65 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/spacedragon.ts @@ -0,0 +1,16 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +const SpaceDragon: Antagonist = { + key: "spacedragon", + name: "Space Dragon", + description: [ + multiline` + Become a ferocious space dragon. Breathe fire, summon an army of space + carps, crush walls, and terrorize the station. + `, + ], + category: Category.Midround, +}; + +export default SpaceDragon; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/spaceninja.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/spaceninja.ts new file mode 100644 index 0000000000000..971fe81606195 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/spaceninja.ts @@ -0,0 +1,24 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +const SpaceNinja: Antagonist = { + key: "spaceninja", + name: "Space Ninja", + description: [ + multiline` + The Spider Clan practice a sort of augmentation of human flesh in order to + achieve a more perfect state of being and follow Postmodern Space Bushido. + `, + + multiline` + Become a conniving space ninja, equipped with a katana, gloves to hack + into airlocks and APCs, a suit to make you go near-invisible, + as well as a variety of abilities in your kit. Hack into arrest consoles + to mark everyone as arrest, and even hack into communication consoles to + summon more threats to cause chaos on the station! + `, + ], + category: Category.Midround, +}; + +export default SpaceNinja; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/swarmer.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/swarmer.ts new file mode 100644 index 0000000000000..9b6696e261b58 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/swarmer.ts @@ -0,0 +1,22 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +const Swarmer: Antagonist = { + key: "swarmer", + name: "Swarmer", + description: [ + multiline` + A swarmer is a small robot that replicates itself autonomously with + nearby given materials and prepare structures that they come + across for the following invasion force. + `, + + multiline` + Consume machines, structures, walls, anything to get materials. Replicate + as many swarmers as you can to repeat the process. + `, + ], + category: Category.Midround, +}; + +export default Swarmer; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/syndicateinfiltrator.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/syndicateinfiltrator.ts new file mode 100644 index 0000000000000..2bcafe666f8e9 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/syndicateinfiltrator.ts @@ -0,0 +1,15 @@ +import { Antagonist, Category } from "../base"; +import { TRAITOR_MECHANICAL_DESCRIPTION } from "./traitor"; + +const SyndicateInfiltrator: Antagonist = { + key: "syndicateinfiltrator", + name: "Syndicate Infiltrator", + description: [ + "A form of traitor that can activate when joining an ongoing shift.", + TRAITOR_MECHANICAL_DESCRIPTION, + ], + category: Category.Latejoin, + priority: -1, +}; + +export default SyndicateInfiltrator; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/syndicatesleeperagent.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/syndicatesleeperagent.ts new file mode 100644 index 0000000000000..bc3fb9d44f14e --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/syndicatesleeperagent.ts @@ -0,0 +1,19 @@ +import { Antagonist, Category } from "../base"; +import { TRAITOR_MECHANICAL_DESCRIPTION } from "./traitor"; +import { multiline } from "common/string"; + +const SyndicateSleeperAgent: Antagonist = { + key: "syndicatesleeperagent", + name: "Syndicate Sleeper Agent", + description: [ + multiline` + A form of traitor that can activate at any point in the middle + of the shift. + `, + TRAITOR_MECHANICAL_DESCRIPTION, + ], + category: Category.Midround, + priority: -1, +}; + +export default SyndicateSleeperAgent; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/traitor.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/traitor.ts new file mode 100644 index 0000000000000..9f11609f29ffb --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/traitor.ts @@ -0,0 +1,25 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +export const TRAITOR_MECHANICAL_DESCRIPTION + = multiline` + Start with a set of sinister objectives and an uplink to purchase + items to get the job done. + `; + +const Traitor: Antagonist = { + key: "traitor", + name: "Traitor", + description: [ + multiline` + An unpaid debt. A score to be settled. Maybe you were just in the wrong + place at the wrong time. Whatever the reasons, you were selected to + infiltrate Space Station 13. + `, + TRAITOR_MECHANICAL_DESCRIPTION, + ], + category: Category.Roundstart, + priority: -1, +}; + +export default Traitor; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/wizard.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/wizard.ts new file mode 100644 index 0000000000000..0455235cb5865 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/wizard.ts @@ -0,0 +1,20 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +export const WIZARD_MECHANICAL_DESCRIPTION + = multiline` + Choose between a variety of powerful spells in order to cause chaos + among Space Station 13. + `; + +const Wizard: Antagonist = { + key: "wizard", + name: "Wizard", + description: [ + `"GREETINGS. WE'RE THE WIZARDS OF THE WIZARD'S FEDERATION."`, + WIZARD_MECHANICAL_DESCRIPTION, + ], + category: Category.Roundstart, +}; + +export default Wizard; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/wizardmidround.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/wizardmidround.ts new file mode 100644 index 0000000000000..3a1f88e8f7acb --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/wizardmidround.ts @@ -0,0 +1,14 @@ +import { Antagonist, Category } from "../base"; +import { WIZARD_MECHANICAL_DESCRIPTION } from "./wizard"; + +const WizardMidround: Antagonist = { + key: "wizardmidround", + name: "Wizard (Midround)", + description: [ + "A form of wizard that is offered to ghosts in the middle of the shift.", + WIZARD_MECHANICAL_DESCRIPTION, + ], + category: Category.Midround, +}; + +export default WizardMidround; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/xenomorph.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/xenomorph.ts new file mode 100644 index 0000000000000..82e75ce7d8e5e --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/xenomorph.ts @@ -0,0 +1,16 @@ +import { Antagonist, Category } from "../base"; +import { multiline } from "common/string"; + +const Xenomorph: Antagonist = { + key: "xenomorph", + name: "Xenomorph", + description: [ + multiline` + Become the extraterrestrial xenomorph. Start as a larva, and progress + your way up the caste, including even the Queen! + `, + ], + category: Category.Midround, +}; + +export default Xenomorph; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/base.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/base.ts new file mode 100644 index 0000000000000..a91b6af878133 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/base.ts @@ -0,0 +1,35 @@ +/** + * This folder represents the antagonists you can choose in the preferences + * menu. + * + * Every file in this folder represents one antagonist. + * + * For example "Syndicate Sleeper Agent" -> syndicatesleeperagent.ts + * + * "Antagonist" in this context actually means ruleset. + * This is an important distinction--it means that players can choose to be + * a roundstart traitor, but not a latejoin traitor. + * + * Icons are generated from the antag datums themselves, provided by the + * `antag_datum` variable on the /datum/dynamic_ruleset. + * + * The icon used is whatever the return value of get_preview_icon() is. + * Most antagonists, unless they want an especially cool effect, can simply + * set preview_outfit to some typepath representing their character. + */ + +export type Antagonist = { + // the antag_flag, made lowercase, and with non-alphanumerics removed. + key: string; + + name: string; + description: string[]; + category: Category; + priority?: number; +}; + +export enum Category { + Roundstart, + Midround, + Latejoin, +} diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts new file mode 100644 index 0000000000000..f677e706d2822 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts @@ -0,0 +1,155 @@ +import { BooleanLike } from "common/react"; +import { sendAct } from "../../backend"; +import { Gender } from "./preferences/gender"; + +export enum Food { + Alcohol = "ALCOHOL", + Breakfast = "BREAKFAST", + Cloth = "CLOTH", + Dairy = "DAIRY", + Fried = "FRIED", + Fruit = "FRUIT", + Grain = "GRAIN", + Gross = "GROSS", + Junkfood = "JUNKFOOD", + Meat = "MEAT", + Pineapple = "PINEAPPLE", + Raw = "RAW", + Sugar = "SUGAR", + Toxic = "TOXIC", + Vegetables = "VEGETABLES", +} + +export enum JobPriority { + Low = 1, + Medium = 2, + High = 3, +} + +export type Name = { + can_randomize: BooleanLike; + explanation: string; + group: string; +}; + +export type ServerSpeciesData = { + name: string; + icon: string; + + use_skintones: BooleanLike; + sexes: BooleanLike; + + enabled_features: string[]; + + liked_food: Food[]; + disliked_food: Food[]; + toxic_food: Food[]; +}; + +export type Quirk = { + description: string; + icon: string; + name: string; + value: number; +}; + +export type QuirkInfo = { + max_positive_quirks: number; + quirk_info: Record; + quirk_blacklist: string[][]; +}; + +export enum RandomSetting { + AntagOnly = 1, + Disabled = 2, + Enabled = 3, +} + +export enum JoblessRole { + BeOverflow = 1, + BeRandomJob = 2, + ReturnToLobby = 3, +} + +export enum GamePreferencesSelectedPage { + Settings, + Keybindings, +} + +export const createSetPreference = ( + act: typeof sendAct, + preference: string +) => (value: unknown) => { + act("set_preference", { + preference, + value, + }); +}; + +export enum Window { + Character = 0, + Game = 1, + Keybindings = 2, +} + +export type PreferencesMenuData = { + character_preview_view: string; + character_profiles: (string | null)[]; + + character_preferences: { + clothing: Record; + features: Record; + game_preferences: Record; + non_contextual: { + random_body: RandomSetting, + [otherKey: string]: unknown; + }; + secondary_features: Record; + supplemental_features: Record; + + names: Record; + + misc: { + gender: Gender; + joblessrole: JoblessRole; + species: string; + }; + + randomization: Record; + }; + + content_unlocked: BooleanLike, + + job_bans?: string[]; + job_days_left?: Record; + job_required_experience?: Record; + job_preferences: Record; + + keybindings: Record; + overflow_role: string; + selected_quirks: string[]; + + antag_bans?: string[]; + antag_days_left?: Record; + selected_antags: string[]; + + active_slot: number; + name_to_use: string; + + window: Window; +}; + +export type ServerData = { + names: { + types: Record; + }; + quirks: QuirkInfo, + random: { + randomizable: string[]; + }; + species: Record; + [otheyKey: string]: unknown; +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/index.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/index.tsx new file mode 100644 index 0000000000000..f7b3f87c8a0ad --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/index.tsx @@ -0,0 +1,24 @@ +import { exhaustiveCheck } from "common/exhaustive"; +import { useBackend } from "../../backend"; +import { GamePreferencesSelectedPage, PreferencesMenuData, Window } from "./data"; +import { CharacterPreferenceWindow } from "./CharacterPreferenceWindow"; +import { GamePreferenceWindow } from "./GamePreferenceWindow"; + +export const PreferencesMenu = (props, context) => { + const { data } = useBackend(context); + + const window = data.window; + + switch (window) { + case Window.Character: + return ; + case Window.Game: + return ; + case Window.Keybindings: + return (); + default: + exhaustiveCheck(window); + } +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/base.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/base.ts new file mode 100644 index 0000000000000..ced244afee684 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/base.ts @@ -0,0 +1,7 @@ +import { Department } from "./departments"; + +export type Job = { + name: string; + description: string; + department: Department; +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/departments.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/departments.ts new file mode 100644 index 0000000000000..bbd7aa9325fb6 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/departments.ts @@ -0,0 +1,40 @@ +export type Department = { + head?: string; +}; + +export const Assistant: Department = { + // "Assistant" is not the head of its own department, as otherwise + // it would show as large and bold. +}; + +export const Captain: Department = { + head: "Captain", +}; + +export const Cargo: Department = { + head: "Quartermaster", +}; + +export const Engineering: Department = { + head: "Chief Engineer", +}; + +export const Medical: Department = { + head: "Chief Medical Officer", +}; + +export const Security: Department = { + head: "Head of Security", +}; + +export const Service: Department = { + head: "Head of Personnel", +}; + +export const Science: Department = { + head: "Research Director", +}; + +export const Silicon: Department = { + head: "AI", +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/ai.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/ai.ts new file mode 100644 index 0000000000000..eaf932b0b6d9f --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/ai.ts @@ -0,0 +1,10 @@ +import { Job } from "../base"; +import { Silicon } from "../departments"; + +const AI: Job = { + name: "AI", + description: "Assist the crew, follow your laws, coordinate your cyborgs.", + department: Silicon, +}; + +export default AI; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/assistant.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/assistant.ts new file mode 100644 index 0000000000000..66d034f1c3a38 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/assistant.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Assistant as DepartmentAssistant } from "../departments"; + +const Assistant: Job = { + name: "Assistant", + description: "Get your space legs, assist people, ask the HoP to \ + give you a job.", + department: DepartmentAssistant, +}; + +export default Assistant; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/atmospheric_technician.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/atmospheric_technician.ts new file mode 100644 index 0000000000000..9b9f17199e2f0 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/atmospheric_technician.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Engineering } from "../departments"; + +const AtmosphericTechnician: Job = { + name: "Atmospheric Technician", + description: "Ensure the air is breathable on the station, fill oxygen \ + tanks, fight fires, purify the air.", + department: Engineering, +}; + +export default AtmosphericTechnician; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/bartender.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/bartender.ts new file mode 100644 index 0000000000000..0d5670f89f908 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/bartender.ts @@ -0,0 +1,10 @@ +import { Job } from "../base"; +import { Service } from "../departments"; + +const Bartender: Job = { + name: "Bartender", + description: "Serve booze, mix drinks, keep the crew drunk.", + department: Service, +}; + +export default Bartender; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/botanist.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/botanist.ts new file mode 100644 index 0000000000000..5f00a5faefacb --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/botanist.ts @@ -0,0 +1,10 @@ +import { Job } from "../base"; +import { Service } from "../departments"; + +const Botanist: Job = { + name: "Botanist", + description: "Grow plants for the cook, for medicine, and for recreation.", + department: Service, +}; + +export default Botanist; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/captain.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/captain.ts new file mode 100644 index 0000000000000..893b7e4907c2c --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/captain.ts @@ -0,0 +1,12 @@ +import { Job } from "../base"; +import { Captain as DepartmentCaptain } from "../departments"; + +const Captain: Job = { + name: "Captain", + description: "Be responsible for the station, manage your Heads of Staff, \ + keep the crew alive, be prepared to do anything and everything or die \ + horribly trying.", + department: DepartmentCaptain, +}; + +export default Captain; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/cargo_technician.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/cargo_technician.ts new file mode 100644 index 0000000000000..8666c6e1fe7d6 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/cargo_technician.ts @@ -0,0 +1,12 @@ +import { Job } from "../base"; +import { Cargo } from "../departments"; + +const CargoTechnician: Job = { + name: "Cargo Technician", + description: "Distribute supplies to the departments that ordered them, \ + collect empty crates, load and unload the supply shuttle, \ + ship bounty cubes.", + department: Cargo, +}; + +export default CargoTechnician; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chaplain.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chaplain.ts new file mode 100644 index 0000000000000..da105113c0cbe --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chaplain.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Service } from "../departments"; + +const Chaplain: Job = { + name: "Chaplain", + description: "Hold services and funerals, cremate people, preach your \ + religion, protect the crew against cults.", + department: Service, +}; + +export default Chaplain; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chemist.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chemist.ts new file mode 100644 index 0000000000000..f53bed855250c --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chemist.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Medical } from "../departments"; + +const Chemist: Job = { + name: "Chemist", + description: "Supply the doctors with chemicals, make medicine, as well as \ + less likable substances in the comfort of a fully reinforced room.", + department: Medical, +}; + +export default Chemist; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chief_engineer.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chief_engineer.ts new file mode 100644 index 0000000000000..177c31bd63c33 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chief_engineer.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Engineering } from "../departments"; + +const ChiefEngineer: Job = { + name: "Chief Engineer", + description: "Coordinate engineering, ensure equipment doesn't get stolen, \ + make sure the Supermatter doesn't blow up, maintain telecommunications.", + department: Engineering, +}; + +export default ChiefEngineer; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chief_medical_officer.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chief_medical_officer.ts new file mode 100644 index 0000000000000..15a152ed6ac9b --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chief_medical_officer.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Medical } from "../departments"; + +const ChiefMedicalOfficer: Job = { + name: "Chief Medical Officer", + description: "Coordinate doctors and other medbay employees, ensure they \ + know how to save lives, check for injuries on the crew monitor.", + department: Medical, +}; + +export default ChiefMedicalOfficer; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/clown.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/clown.ts new file mode 100644 index 0000000000000..253a6aa3c1be4 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/clown.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Service } from "../departments"; + +const Clown: Job = { + name: "Clown", + description: "Entertain the crew, make bad jokes, go on a holy quest to find \ + bananium, HONK!", + department: Service, +}; + +export default Clown; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/cook.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/cook.ts new file mode 100644 index 0000000000000..d072cb193e413 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/cook.ts @@ -0,0 +1,10 @@ +import { Job } from "../base"; +import { Service } from "../departments"; + +const Cook: Job = { + name: "Cook", + description: "Serve food, cook meat, keep the crew fed.", + department: Service, +}; + +export default Cook; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/curator.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/curator.ts new file mode 100644 index 0000000000000..454c93fb426f5 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/curator.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Service } from "../departments"; + +const Curator: Job = { + name: "Curator", + description: "Read and write books and hand them to people, stock \ + bookshelves, report on station news.", + department: Service, +}; + +export default Curator; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/cyborg.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/cyborg.ts new file mode 100644 index 0000000000000..4969aaa319989 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/cyborg.ts @@ -0,0 +1,10 @@ +import { Job } from "../base"; +import { Silicon } from "../departments"; + +const Cyborg: Job = { + name: "Cyborg", + description: "Assist the crew, follow your laws, obey your AI.", + department: Silicon, +}; + +export default Cyborg; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/detective.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/detective.ts new file mode 100644 index 0000000000000..408dcb0978f48 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/detective.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Security } from "../departments"; + +const Detective: Job = { + name: "Detective", + description: "Investigate crimes, gather evidence, perform interrogations, \ + look badass, smoke cigarettes.", + department: Security, +}; + +export default Detective; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/geneticist.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/geneticist.ts new file mode 100644 index 0000000000000..e36e08c311627 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/geneticist.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Science } from "../departments"; + +const Geneticist: Job = { + name: "Geneticist", + description: "Alter genomes, turn monkeys into humans (and vice-versa), make \ + DNA backups so they can get stolen.", + department: Science, +}; + +export default Geneticist; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/head_of_personnel.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/head_of_personnel.ts new file mode 100644 index 0000000000000..9f06282d3469c --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/head_of_personnel.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Service } from "../departments"; + +const HeadOfPersonnel: Job = { + name: "Head of Personnel", + description: "Alter access on ID cards, manage civil and supply departments, \ + protect Ian, run the station when the captain dies.", + department: Service, +}; + +export default HeadOfPersonnel; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/head_of_security.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/head_of_security.ts new file mode 100644 index 0000000000000..56a96e80470d1 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/head_of_security.ts @@ -0,0 +1,12 @@ +import { Job } from "../base"; +import { Security } from "../departments"; + +const HeadOfSecurity: Job = { + name: "Head of Security", + description: "Coordinate security personnel, ensure they are not corrupt, \ + make sure every department is protected, save the Virologist when he gets \ + mauled by monkeys.", + department: Security, +}; + +export default HeadOfSecurity; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/janitor.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/janitor.ts new file mode 100644 index 0000000000000..aa64e0687c512 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/janitor.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Service } from "../departments"; + +const Janitor: Job = { + name: "Janitor", + description: "Clean up trash and blood. Replace broken lights. \ + Slip people over.", + department: Service, +}; + +export default Janitor; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/lawyer.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/lawyer.ts new file mode 100644 index 0000000000000..559b82b37e0bd --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/lawyer.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Security } from "../departments"; + +const Lawyer: Job = { + name: "Lawyer", + description: "Advocate for prisoners, create law-binding contracts, \ + ensure Security is following protocol and Space Law.", + department: Security, +}; + +export default Lawyer; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/medical_doctor.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/medical_doctor.ts new file mode 100644 index 0000000000000..b248624b33a58 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/medical_doctor.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Medical } from "../departments"; + +const MedicalDoctor: Job = { + name: "Medical Doctor", + description: "Save lives, run around the station looking for victims, \ + scan everyone in sight", + department: Medical, +}; + +export default MedicalDoctor; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/mime.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/mime.ts new file mode 100644 index 0000000000000..8eca6aa19a310 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/mime.ts @@ -0,0 +1,10 @@ +import { Job } from "../base"; +import { Service } from "../departments"; + +const Mime: Job = { + name: "Mime", + description: "...", + department: Service, +}; + +export default Mime; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/paramedic.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/paramedic.ts new file mode 100644 index 0000000000000..88a4450232a46 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/paramedic.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Medical } from "../departments"; + +const Paramedic: Job = { + name: "Paramedic", + description: "Run around the station looking for patients, respond to \ + emergencies, give patients a roller bed ride to medbay.", + department: Medical, +}; + +export default Paramedic; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/prisoner.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/prisoner.ts new file mode 100644 index 0000000000000..82308a9b1b920 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/prisoner.ts @@ -0,0 +1,10 @@ +import { Job } from "../base"; +import { Security } from "../departments"; + +const Prisoner: Job = { + name: "Prisoner", + description: "Keep yourself occupied while in permabrig.", + department: Security, +}; + +export default Prisoner; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/psychologist.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/psychologist.ts new file mode 100644 index 0000000000000..f6233d82b09f0 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/psychologist.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Medical } from "../departments"; + +const Psychologist: Job = { + name: "Psychologist", + description: "Advocate sanity, self-esteem, and teamwork in a station \ + staffed with headcases.", + department: Medical, +}; + +export default Psychologist; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/quartermaster.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/quartermaster.ts new file mode 100644 index 0000000000000..05f266fba6ff5 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/quartermaster.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Cargo } from "../departments"; + +const Quartermaster: Job = { + name: "Quartermaster", + description: "Coordinate cargo technicians and shaft miners, assist with \ + economical purchasing.", + department: Cargo, +}; + +export default Quartermaster; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/research_director.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/research_director.ts new file mode 100644 index 0000000000000..6ecce763ed8b9 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/research_director.ts @@ -0,0 +1,12 @@ +import { Job } from "../base"; +import { Science } from "../departments"; + +const ResearchDirector: Job = { + name: "Research Director", + description: "Supervise research efforts, ensure Robotics is in working \ + order, make sure the AI and its Cyborgs aren't rogue, replacing them if \ + they are", + department: Science, +}; + +export default ResearchDirector; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/roboticist.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/roboticist.ts new file mode 100644 index 0000000000000..64cf661eae9c1 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/roboticist.ts @@ -0,0 +1,10 @@ +import { Job } from "../base"; +import { Science } from "../departments"; + +const Roboticist: Job = { + name: "Roboticist", + description: "Build and repair the AI and cyborgs, create mechs.", + department: Science, +}; + +export default Roboticist; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/scientist.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/scientist.ts new file mode 100644 index 0000000000000..9f140ac954f25 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/scientist.ts @@ -0,0 +1,10 @@ +import { Job } from "../base"; +import { Science } from "../departments"; + +const Scientist: Job = { + name: "Scientist", + description: "Do experiments, perform research, feed the slimes, make bombs.", + department: Science, +}; + +export default Scientist; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/security_officer.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/security_officer.ts new file mode 100644 index 0000000000000..93117dd3b87fa --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/security_officer.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Security } from "../departments"; + +const SecurityOfficer: Job = { + name: "Security Officer", + description: "Protect company assets, follow the Standard Operating \ + Procedure, eat donuts", + department: Security, +}; + +export default SecurityOfficer; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/shaft_miner.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/shaft_miner.ts new file mode 100644 index 0000000000000..3886670726282 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/shaft_miner.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Cargo } from "../departments"; + +const ShaftMiner: Job = { + name: "Shaft Miner", + description: "Mine ores and process them into useful materials. \ + Kill hostile wildlife. Go on an adventure in mysterious ruins.", + department: Cargo, +}; + +export default ShaftMiner; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/station_engineer.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/station_engineer.ts new file mode 100644 index 0000000000000..4280c83373599 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/station_engineer.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Engineering } from "../departments"; + +const StationEngineer: Job = { + name: "Station Engineer", + description: "Start the Supermatter, wire the solars, repair station hull \ + and wiring damage.", + department: Engineering, +}; + +export default StationEngineer; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/virologist.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/virologist.ts new file mode 100644 index 0000000000000..13a5be666503e --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/virologist.ts @@ -0,0 +1,11 @@ +import { Job } from "../base"; +import { Medical } from "../departments"; + +const Virologist: Job = { + name: "Virologist", + description: "Study the effects of various diseases and synthesize a \ + vaccine for them. Engineer beneficial viruses.", + department: Medical, +}; + +export default Virologist; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/warden.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/warden.ts new file mode 100644 index 0000000000000..e8ac63fe466c9 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/warden.ts @@ -0,0 +1,12 @@ +import { Job } from "../base"; +import { Security } from "../departments"; + +const Warden: Job = { + name: "Warden", + description: "Watch over the Brig and Prison Wing, release prisoners when \ + their time is up, issue equipment to security, be a security officer when \ + they all eventually die.", + department: Security, +}; + +export default Warden; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx new file mode 100644 index 0000000000000..8f798394491fa --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx @@ -0,0 +1,237 @@ +import { binaryInsertWith, sortBy } from "common/collections"; +import { useLocalState } from "../../backend"; +import { Box, Button, FitText, Icon, Input, LabeledList, Modal, Section, Stack, TrackOutsideClicks } from "../../components"; +import { Name } from "./data"; +import { ServerPreferencesFetcher } from "./ServerPreferencesFetcher"; + +type NameWithKey = { + key: string, + name: Name, +}; + +const binaryInsertName = binaryInsertWith(({ key }) => key); + +const sortNameWithKeyEntries = sortBy<[string, NameWithKey[]]>( + ([key]) => key, +); + +export const MultiNameInput = (props: { + handleClose: () => void, + handleRandomizeName: (nameType: string) => void, + handleUpdateName: (nameType: string, value: string) => void, + names: Record, +}, context) => { + const [currentlyEditingName, setCurrentlyEditingName] + = useLocalState(context, "currentlyEditingName", null); + + return ( + { + if (!data) { + return null; + } + + const namesIntoGroups: Record = {}; + + for (const [key, name] of Object.entries(data.names.types)) { + namesIntoGroups[name.group] = binaryInsertName( + namesIntoGroups[name.group] || [], + { + key, + name, + } + ); + } + + return ( + + +
    + Close + + )} + title="Alternate names" + > + + {sortNameWithKeyEntries(Object.entries(namesIntoGroups)) + .map(([_, names], index, collection) => ( + <> + {names.map(({ key, name }) => { + let content; + + if (currentlyEditingName === key) { + const updateName = (event, value) => { + props.handleUpdateName( + key, + value, + ); + + setCurrentlyEditingName(null); + }; + + content = ( + { + setCurrentlyEditingName(null); + }} + value={props.names[key]} + /> + ); + } else { + content = ( + + ); + } + + return ( + + + + {content} + + + {!!name.can_randomize && ( + +
    +
    +
    + ); + }} /> + ); +}; + +export const NameInput = (props: { + handleUpdateName: (name: string) => void, + name: string, + openMultiNameInput: () => void, +}, context) => { + const [lastNameBeforeEdit, setLastNameBeforeEdit] + = useLocalState(context, "lastNameBeforeEdit", null); + const editing = lastNameBeforeEdit === props.name; + + const updateName = (e, value) => { + setLastNameBeforeEdit(null); + props.handleUpdateName(value); + }; + + return ( + + + ) : null} /> + + + ); +}; + + diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx new file mode 100644 index 0000000000000..a582ac539ccd4 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx @@ -0,0 +1,333 @@ +import { sortBy, sortStrings } from "common/collections"; +import { BooleanLike, classes } from "common/react"; +import { ComponentType, createComponentVNode, InfernoNode } from "inferno"; +import { VNodeFlags } from "inferno-vnode-flags"; +import { sendAct, useBackend, useLocalState } from "../../../../backend"; +import { Box, Button, Dropdown, NumberInput, Stack } from "../../../../components"; +import { createSetPreference, PreferencesMenuData } from "../../data"; +import { ServerPreferencesFetcher } from "../../ServerPreferencesFetcher"; + +export const sortChoices = sortBy<[string, InfernoNode]>(([name]) => name); + +export type Feature< + TReceiving, + TSending = TReceiving, + TServerData = undefined, +> = { + name: string; + component: FeatureValue< + TReceiving, + TSending, + TServerData + >; + category?: string; + description?: string; +}; + +/** + * Represents a preference. + * TReceiving = The type you will be receiving + * TSending = The type you will be sending + * TServerData = The data the server sends through preferences.json + */ +type FeatureValue< + TReceiving, + TSending = TReceiving, + TServerData = undefined, +> + = ComponentType>; + +export type FeatureValueProps< + TReceiving, + TSending = TReceiving, + TServerData = undefined, +> = { + act: typeof sendAct, + featureId: string, + handleSetValue: (newValue: TSending) => void, + serverData: TServerData | undefined, + shrink?: boolean, + value: TReceiving, +}; + +export const FeatureColorInput = (props: FeatureValueProps) => { + return ( + + ); +}; + +export type FeatureToggle = Feature; + +export const CheckboxInput = ( + props: FeatureValueProps +) => { + return ( { + props.handleSetValue(!props.value); + }} + />); +}; + +export const CheckboxInputInverse = ( + props: FeatureValueProps +) => { + return ( { + props.handleSetValue(!props.value); + }} + />); +}; + +export const createDropdownInput = ( + // Map of value to display texts + choices: Record, + dropdownProps?: Record, +): FeatureValue => { + return (props: FeatureValueProps) => { + return ( { + return { + displayText: label, + value: dataValue, + }; + })} + {...dropdownProps} + />); + }; +}; + +export type FeatureChoicedServerData = { + choices: string[]; + display_names?: Record; + icons?: Record; +}; + +export type FeatureChoiced = Feature; + +const capitalizeFirstLetter = (text: string) => ( + text.toString().charAt(0).toUpperCase() + text.toString().slice(1) +); + +export const StandardizedDropdown = (props: { + choices: string[], + disabled?: boolean, + displayNames: Record, + onSetValue: (newValue: string) => void, + value: string, +}) => { + const { + choices, + disabled, + displayNames, + onSetValue, + value, + } = props; + + return ( { + return { + displayText: displayNames[choice], + value: choice, + }; + }) + } + />); +}; + +export const FeatureDropdownInput = ( + props: FeatureValueProps & { + disabled?: boolean, + }, +) => { + const serverData = props.serverData; + if (!serverData) { + return null; + } + + const displayNames = serverData.display_names + || Object.fromEntries( + serverData.choices.map(choice => [choice, capitalizeFirstLetter(choice)]) + ); + + return (); +}; + +export type FeatureWithIcons = Feature< + { value: T }, + T, + FeatureChoicedServerData +>; + +export const FeatureIconnedDropdownInput = ( + props: FeatureValueProps<{ + value: string, + }, string, FeatureChoicedServerData>, +) => { + const serverData = props.serverData; + if (!serverData) { + return null; + } + + const icons = serverData.icons; + + const textNames = serverData.display_names + || Object.fromEntries( + serverData.choices.map(choice => [choice, capitalizeFirstLetter(choice)]) + ); + + const displayNames = Object.fromEntries( + Object.entries(textNames).map(([choice, textName]) => { + let element: InfernoNode = textName; + + if (icons && icons[choice]) { + const icon = icons[choice]; + element = ( + + + + + + + {element} + + + ); + } + + return [choice, element]; + }) + ); + + return (); +}; + + +export type FeatureNumericData = { + minimum: number, + maximum: number, + step: number, +} + +export type FeatureNumeric = Feature; + +export const FeatureNumberInput = ( + props: FeatureValueProps +) => { + if (!props.serverData) { + return Loading...; + } + + return ( { + props.handleSetValue(value); + }} + minValue={props.serverData.minimum} + maxValue={props.serverData.maximum} + step={props.serverData.step} + value={props.value} + />); +}; + +export const FeatureValueInput = (props: { + feature: Feature, + featureId: string, + shrink?: boolean, + value: unknown, + + act: typeof sendAct, +}, context) => { + const { data } = useBackend(context); + + const feature = props.feature; + + const [predictedValue, setPredictedValue] = useLocalState( + context, + `${props.featureId}_predictedValue_${data.active_slot}`, + props.value, + ); + + const changeValue = (newValue: unknown) => { + setPredictedValue(newValue); + createSetPreference(props.act, props.featureId)(newValue); + }; + + return ( + { + return createComponentVNode( + VNodeFlags.ComponentUnknown, + feature.component, + { + act: props.act, + featureId: props.featureId, + serverData: serverData && serverData[props.featureId], + shrink: props.shrink, + + handleSetValue: changeValue, + value: predictedValue, + }); + }} + /> + ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/age.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/age.tsx new file mode 100644 index 0000000000000..41889a1fc685b --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/age.tsx @@ -0,0 +1,6 @@ +import { Feature, FeatureNumberInput } from "../base"; + +export const age: Feature = { + name: "Age", + component: FeatureNumberInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/ai_core_display.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/ai_core_display.tsx new file mode 100644 index 0000000000000..b67e62dbe8d50 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/ai_core_display.tsx @@ -0,0 +1,6 @@ +import { FeatureIconnedDropdownInput, FeatureWithIcons } from "../base"; + +export const preferred_ai_core_display: FeatureWithIcons = { + name: "AI core display", + component: FeatureIconnedDropdownInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/body_type.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/body_type.tsx new file mode 100644 index 0000000000000..9ae25fa1486f7 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/body_type.tsx @@ -0,0 +1,6 @@ +import { FeatureChoiced, FeatureDropdownInput } from "../base"; + +export const body_type: FeatureChoiced = { + name: "Body type", + component: FeatureDropdownInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/pda.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/pda.tsx new file mode 100644 index 0000000000000..823459a2a212d --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/pda.tsx @@ -0,0 +1,15 @@ +import { Feature, FeatureColorInput, FeatureDropdownInput } from "../base"; + +export const pda_color: Feature = { + name: "PDA color", + category: "GAMEPLAY", + description: "The background color of your PDA.", + component: FeatureColorInput, +}; + +export const pda_style: Feature = { + name: "PDA style", + category: "GAMEPLAY", + description: "The style of your equipped PDA. Changes font.", + component: FeatureDropdownInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/phobia.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/phobia.tsx new file mode 100644 index 0000000000000..04d91daea06d9 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/phobia.tsx @@ -0,0 +1,6 @@ +import { FeatureChoiced, FeatureDropdownInput } from "../base"; + +export const phobia: FeatureChoiced = { + name: "Phobia", + component: FeatureDropdownInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/playtime_reward_cloak.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/playtime_reward_cloak.tsx new file mode 100644 index 0000000000000..af24a49b7f4df --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/playtime_reward_cloak.tsx @@ -0,0 +1,7 @@ +import { CheckboxInput, FeatureToggle } from "../base"; + +export const playtime_reward_cloak: FeatureToggle = { + name: "Don gamer cloak", + component: CheckboxInput, +}; + diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/security_department.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/security_department.tsx new file mode 100644 index 0000000000000..118f2cf467859 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/security_department.tsx @@ -0,0 +1,6 @@ +import { FeatureChoiced, FeatureDropdownInput } from "../base"; + +export const prefered_security_department: FeatureChoiced = { + name: "Security department", + component: FeatureDropdownInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skin_tone.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skin_tone.tsx new file mode 100644 index 0000000000000..40856fed36aa8 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skin_tone.tsx @@ -0,0 +1,63 @@ +import { sortBy } from "common/collections"; +import { Box, Stack } from "../../../../../components"; +import { Feature, FeatureChoicedServerData, FeatureValueProps, StandardizedDropdown } from "../base"; + +type HexValue = { + lightness: number, + value: string, +}; + +type SkinToneServerData = FeatureChoicedServerData & { + display_names: NonNullable, + to_hex: Record, +}; + +const sortHexValues + = sortBy<[string, HexValue]>(([_, hexValue]) => -hexValue.lightness); + +export const skin_tone: Feature = { + name: "Skin tone", + component: (props: FeatureValueProps) => { + const { + handleSetValue, + serverData, + value, + } = props; + + if (!serverData) { + return null; + } + + return ( + key)} + displayNames={Object.fromEntries( + Object.entries(serverData.display_names) + .map(([key, displayName]) => { + const hexColor = serverData.to_hex[key]; + + return [key, ( + + + + + + + {displayName} + + + )]; + }) + )} + onSetValue={handleSetValue} + value={value} + /> + ); + }, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/uplink_loc.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/uplink_loc.tsx new file mode 100644 index 0000000000000..459892597089c --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/uplink_loc.tsx @@ -0,0 +1,6 @@ +import { FeatureChoiced, FeatureDropdownInput } from "../base"; + +export const uplink_loc: FeatureChoiced = { + name: "Uplink Spawn Location", + component: FeatureDropdownInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/admin.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/admin.tsx new file mode 100644 index 0000000000000..b59aa47fbae1d --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/admin.tsx @@ -0,0 +1,15 @@ +import { FeatureColorInput, Feature, FeatureDropdownInput } from "../base"; + +export const asaycolor: Feature = { + name: "Admin chat color", + category: "ADMIN", + description: "The color of your messages in Adminsay.", + component: FeatureColorInput, +}; + +export const brief_outfit: Feature = { + name: "Brief outfit", + category: "ADMIN", + description: "The outfit to gain when spawning as the briefing officer.", + component: FeatureDropdownInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ambient_occlusion.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ambient_occlusion.tsx new file mode 100644 index 0000000000000..4884c07377fb6 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ambient_occlusion.tsx @@ -0,0 +1,8 @@ +import { CheckboxInput, FeatureToggle } from "../base"; + +export const ambientocclusion: FeatureToggle = { + name: "Enable ambient occlusion", + category: "GAMEPLAY", + description: "Enable ambient occlusion, light shadows around characters.", + component: CheckboxInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/auto_fit_viewport.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/auto_fit_viewport.tsx new file mode 100644 index 0000000000000..5339de43d15ae --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/auto_fit_viewport.tsx @@ -0,0 +1,7 @@ +import { CheckboxInput, FeatureToggle } from "../base"; + +export const auto_fit_viewport: FeatureToggle = { + name: "Auto fit viewport", + category: "UI", + component: CheckboxInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/broadcast_login_logout.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/broadcast_login_logout.tsx new file mode 100644 index 0000000000000..a997b265f64a2 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/broadcast_login_logout.tsx @@ -0,0 +1,11 @@ +import { multiline } from "common/string"; +import { CheckboxInput, FeatureToggle } from "../base"; + +export const broadcast_login_logout: FeatureToggle = { + name: "Broadcast login/logout", + category: "GAMEPLAY", + description: multiline` + When enabled, disconnecting and reconnecting will announce to deadchat. + `, + component: CheckboxInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/buttons_locked.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/buttons_locked.tsx new file mode 100644 index 0000000000000..67849342094d0 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/buttons_locked.tsx @@ -0,0 +1,8 @@ +import { CheckboxInput, FeatureToggle } from "../base"; + +export const buttons_locked: FeatureToggle = { + name: "Lock action buttons", + category: "GAMEPLAY", + description: "When enabled, action buttons will be locked in place.", + component: CheckboxInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/darkened_flash.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/darkened_flash.tsx new file mode 100644 index 0000000000000..706b178770599 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/darkened_flash.tsx @@ -0,0 +1,12 @@ +import { multiline } from "common/string"; +import { CheckboxInput, FeatureToggle } from "../base"; + +export const darkened_flash: FeatureToggle = { + name: "Enable darkened flashes", + category: "GAMEPLAY", + description: multiline` + When toggled, being flashed will show a dark screen rather than a + bright one. + `, + component: CheckboxInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/fps.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/fps.tsx new file mode 100644 index 0000000000000..55c091a9714fa --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/fps.tsx @@ -0,0 +1,56 @@ +import { Dropdown, NumberInput, Stack } from "../../../../../components"; +import { Feature, FeatureNumericData, FeatureValueProps } from "../base"; + +type FpsServerData = FeatureNumericData & { + recommended_fps: number; +} + +const FpsInput = ( + props: FeatureValueProps +) => { + const { handleSetValue, serverData } = props; + + let recommened = `Recommended`; + if (serverData) { + recommened += ` (${serverData.recommended_fps})`; + } + + return ( + + + { + if (value === recommened) { + handleSetValue(-1); + } else { + handleSetValue(serverData?.recommended_fps || 60); + } + }} + width="100%" + options={[ + recommened, + "Custom", + ]} + /> + + + + {serverData && props.value !== -1 && ( { + props.handleSetValue(value); + }} + minValue={1} + maxValue={serverData.maximum} + value={props.value} + />)} + + + ); +}; + +export const clientfps: Feature = { + name: "FPS", + category: "GAMEPLAY", + component: FpsInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx new file mode 100644 index 0000000000000..c9dc71ab1fa99 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx @@ -0,0 +1,139 @@ +import { multiline } from "common/string"; +import { CheckboxInput, FeatureChoiced, FeatureChoicedServerData, FeatureDropdownInput, FeatureToggle, FeatureValueProps } from "../base"; +import { Box, Dropdown, Flex } from "../../../../../components"; +import { classes } from "common/react"; +import { InfernoNode } from "inferno"; +import { binaryInsertWith } from "common/collections"; +import { useBackend } from "../../../../../backend"; +import { PreferencesMenuData } from "../../../data"; + +export const ghost_accs: FeatureChoiced = { + name: "Ghost accessories", + category: "GHOST", + description: "Determines what adjustments your ghost will have.", + component: FeatureDropdownInput, +}; + +const insertGhostForm + = binaryInsertWith<{ + displayText: InfernoNode, + value: string, + }>(({ value }) => value); + +const GhostFormInput = ( + props: FeatureValueProps, + context, +) => { + const { data } = useBackend(context); + + const serverData = props.serverData; + if (!serverData) { + return; + } + + const displayNames = serverData.display_names; + if (!displayNames) { + return ( + + No display names for ghost_form! + + ); + } + + const displayTexts = {}; + let options: { + displayText: InfernoNode, + value: string, + }[] = []; + + for (const [name, displayName] of Object.entries(displayNames)) { + const displayText = ( + + + + + + + {displayName} + + + ); + + displayTexts[name] = displayText; + + const optionEntry = { + displayText, + value: name, + }; + + // Put the default ghost on top + if (name === "ghost") { + options.unshift(optionEntry); + } else { + options = insertGhostForm(options, optionEntry); + } + } + + return (); +}; + +export const ghost_form: FeatureChoiced = { + name: "Ghosts form", + category: "GHOST", + description: "The appearance of your ghost. Requires BYOND membership.", + component: GhostFormInput, +}; + +export const ghost_hud: FeatureToggle = { + name: "Ghost HUD", + category: "GHOST", + description: "Enable HUD buttons for ghosts.", + component: CheckboxInput, +}; + +export const ghost_orbit: FeatureChoiced = { + name: "Ghost orbit", + category: "GHOST", + description: multiline` + The shape in which your ghost will orbit. + Requires BYOND membership. + `, + component: ( + props: FeatureValueProps, + context, + ) => { + const { data } = useBackend(context); + + return (); + }, +}; + +export const ghost_others: FeatureChoiced = { + name: "Ghosts of others", + category: "GHOST", + description: multiline` + Do you want the ghosts of others to show up as their own setting, as + their default sprites, or always as the default white ghost? + `, + component: FeatureDropdownInput, +}; + +export const inquisitive_ghost: FeatureToggle = { + name: "Ghost inquisitiveness", + category: "GHOST", + description: "Clicking on something as a ghost will examine it.", + component: CheckboxInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/hotkeys.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/hotkeys.tsx new file mode 100644 index 0000000000000..73cc3c9de16e9 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/hotkeys.tsx @@ -0,0 +1,8 @@ +import { CheckboxInputInverse, FeatureToggle } from "../base"; + +export const hotkeys: FeatureToggle = { + name: "Classic hotkeys", + category: "GAMEPLAY", + description: "When enabled, will revert to the legacy hotkeys, using the input bar rather than popups.", + component: CheckboxInputInverse, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/item_outlines.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/item_outlines.tsx new file mode 100644 index 0000000000000..3877da11007b9 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/item_outlines.tsx @@ -0,0 +1,8 @@ +import { CheckboxInput, FeatureToggle } from "../base"; + +export const itemoutline_pref: FeatureToggle = { + name: "Item outlines", + category: "GAMEPLAY", + description: "When enabled, hovering over items will outline them.", + component: CheckboxInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/legacy_chat_toggles.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/legacy_chat_toggles.tsx new file mode 100644 index 0000000000000..dc359e57d4597 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/legacy_chat_toggles.tsx @@ -0,0 +1,89 @@ +import { multiline } from "common/string"; +import { FeatureToggle, CheckboxInput } from "../base"; + +export const chat_bankcard: FeatureToggle = { + name: "Enable income updates", + category: "CHAT", + description: "Receive notifications for your bank account.", + component: CheckboxInput, +}; + +export const chat_dead: FeatureToggle = { + name: "Enable deadchat", + category: "ADMIN", + component: CheckboxInput, +}; + +export const chat_ghostears: FeatureToggle = { + name: "Hear all messages", + category: "GHOST", + description: multiline` + When enabled, you will be able to hear all speech as a ghost. + When disabled, you will only be able to hear nearby speech. + `, + component: CheckboxInput, +}; + +export const chat_ghostlaws: FeatureToggle = { + name: "Enable law change updates", + category: "GHOST", + description: "When enabled, be notified of any new law changes as a ghost.", + component: CheckboxInput, +}; + +export const chat_ghostpda: FeatureToggle = { + name: "Enable PDA notifications", + category: "GHOST", + description: "When enabled, be notified of any PDA messages as a ghost.", + component: CheckboxInput, +}; + +export const chat_ghostradio: FeatureToggle = { + name: "Enable radio", + category: "GHOST", + description: "When enabled, be notified of any radio messages as a ghost.", + component: CheckboxInput, +}; + +export const chat_ghostsight: FeatureToggle = { + name: "See all emotes", + category: "GHOST", + description: "When enabled, see all emotes as a ghost.", + component: CheckboxInput, +}; + +export const chat_ghostwhisper: FeatureToggle = { + name: "See all whispers", + category: "GHOST", + description: multiline` + When enabled, you will be able to hear all whispers as a ghost. + When disabled, you will only be able to hear nearby whispers. + `, + component: CheckboxInput, +}; + +export const chat_login_logout: FeatureToggle = { + name: "See login/logout messages", + category: "GHOST", + description: "When enabled, be notified when a player logs in or out.", + component: CheckboxInput, +}; + +export const chat_ooc: FeatureToggle = { + name: "Enable OOC", + category: "CHAT", + component: CheckboxInput, +}; + +export const chat_prayer: FeatureToggle = { + name: "Listen to prayers", + category: "ADMIN", + component: CheckboxInput, +}; + +export const chat_pullr: FeatureToggle = { + name: "Enable pull request notifications", + category: "CHAT", + description: "Be notified when a pull request is made, closed, or merged.", + component: CheckboxInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/legacy_toggles.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/legacy_toggles.tsx new file mode 100644 index 0000000000000..6b6c44661e5c9 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/legacy_toggles.tsx @@ -0,0 +1,153 @@ +import { multiline } from "common/string"; +import { FeatureToggle, CheckboxInput, CheckboxInputInverse } from "../base"; + +export const admin_ignore_cult_ghost: FeatureToggle = { + name: "Prevent being summoned as a cult ghost", + category: "ADMIN", + description: multiline` + When enabled and observing, prevents Spirit Realm from forcing you + into a cult ghost. + `, + component: CheckboxInput, +}; + +export const announce_login: FeatureToggle = { + name: "Announce login", + category: "ADMIN", + description: "Admins will be notified when you login.", + component: CheckboxInput, +}; + +export const combohud_lighting: FeatureToggle = { + name: "Enable fullbright Combo HUD", + category: "ADMIN", + component: CheckboxInput, +}; + +export const deadmin_always: FeatureToggle = { + name: "Auto deadmin - Always", + category: "ADMIN", + description: "When enabled, you will automatically deadmin.", + component: CheckboxInput, +}; + +export const deadmin_antagonist: FeatureToggle = { + name: "Auto deadmin - Antagonist", + category: "ADMIN", + description: "When enabled, you will automatically deadmin as an antagonist.", + component: CheckboxInput, +}; + +export const deadmin_position_head: FeatureToggle = { + name: "Auto deadmin - Head of Staff", + category: "ADMIN", + description: "When enabled, you will automatically deadmin as a head of staff.", + component: CheckboxInput, +}; + +export const deadmin_position_security: FeatureToggle = { + name: "Auto deadmin - Security", + category: "ADMIN", + description: "When enabled, you will automatically deadmin as a member of security.", + component: CheckboxInput, +}; + +export const deadmin_position_silicon: FeatureToggle = { + name: "Auto deadmin - Silicon", + category: "ADMIN", + description: "When enabled, you will automatically deadmin as a silicon.", + component: CheckboxInput, +}; + +export const disable_arrivalrattle: FeatureToggle = { + name: "Notify for new arrivals", + category: "GHOST", + description: "When enabled, you will be notified as a ghost for new crew.", + component: CheckboxInputInverse, +}; + +export const disable_deathrattle: FeatureToggle = { + name: "Notify for deaths", + category: "GHOST", + description: "When enabled, you will be notified as a ghost whenever someone dies.", + component: CheckboxInputInverse, +}; + +export const member_public: FeatureToggle = { + name: "Publicize BYOND membership", + category: "CHAT", + description: "When enabled, a BYOND logo will be shown next to your name in OOC.", + component: CheckboxInput, +}; + +export const sound_adminhelp: FeatureToggle = { + name: "Enable adminhelp sounds", + category: "ADMIN", + component: CheckboxInput, +}; + +export const sound_ambience: FeatureToggle = { + name: "Enable ambience", + category: "SOUND", + component: CheckboxInput, +}; + +export const sound_announcements: FeatureToggle = { + name: "Enable announcement sounds", + category: "SOUND", + description: "When enabled, hear sounds for command reports, notices, etc.", + component: CheckboxInput, +}; + +export const sound_combatmode: FeatureToggle = { + name: "Enable combat mode sound", + category: "SOUND", + description: "When enabled, hear sounds when toggling combat mode.", + component: CheckboxInput, +}; + +export const sound_endofround: FeatureToggle = { + name: "Enable end of round sounds", + category: "SOUND", + description: "When enabled, hear a sound when the server is rebooting.", + component: CheckboxInput, +}; + +export const sound_instruments: FeatureToggle = { + name: "Enable instruments", + category: "SOUND", + description: "When enabled, be able hear instruments in game.", + component: CheckboxInput, +}; + +export const sound_lobby: FeatureToggle = { + name: "Enable lobby music", + category: "SOUND", + component: CheckboxInput, +}; + +export const sound_midi: FeatureToggle = { + name: "Enable admin music", + category: "SOUND", + description: "When enabled, admins will be able to play music to you.", + component: CheckboxInput, +}; + +export const sound_prayers: FeatureToggle = { + name: "Enable prayer sound", + category: "ADMIN", + component: CheckboxInput, +}; + +export const sound_ship_ambience: FeatureToggle = { + name: "Enable ship ambience", + category: "SOUND", + component: CheckboxInput, +}; + +export const split_admin_tabs: FeatureToggle = { + name: "Split admin tabs", + category: "ADMIN", + description: "When enabled, will split the 'Admin' panel into several tabs.", + component: CheckboxInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ooc.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ooc.tsx new file mode 100644 index 0000000000000..e4f5c5466f0e3 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ooc.tsx @@ -0,0 +1,8 @@ +import { FeatureColorInput, Feature } from "../base"; + +export const ooccolor: Feature = { + name: "OOC color", + category: "CHAT", + description: "The color of your OOC messages.", + component: FeatureColorInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/parallax.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/parallax.tsx new file mode 100644 index 0000000000000..1fbb2230ff202 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/parallax.tsx @@ -0,0 +1,7 @@ +import { Feature, FeatureDropdownInput } from "../base"; + +export const parallax: Feature = { + name: "Parallax (fancy space)", + category: "GAMEPLAY", + component: FeatureDropdownInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/pixel_size.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/pixel_size.tsx new file mode 100644 index 0000000000000..bb07453e340f1 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/pixel_size.tsx @@ -0,0 +1,13 @@ +import { createDropdownInput, Feature } from "../base"; + +export const pixel_size: Feature = { + name: "Pixel Scaling", + category: "UI", + component: createDropdownInput({ + 0: "Stretch to fit", + 1: "Pixel Perfect 1x", + 1.5: "Pixel Perfect 1.5x", + 2: "Pixel Perfect 2x", + 3: "Pixel Perfect 3x", + }), +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/preferred_map.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/preferred_map.tsx new file mode 100644 index 0000000000000..3acfad00445a3 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/preferred_map.tsx @@ -0,0 +1,13 @@ +import { multiline } from "common/string"; +import { Feature, FeatureDropdownInput } from "../base"; + +export const preferred_map: Feature = { + name: "Preferred map", + category: "GAMEPLAY", + description: multiline` + During map rotation, prefer this map be chosen. + This does not affect the map vote, only random rotation when a vote + is not held. + `, + component: FeatureDropdownInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/runechat.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/runechat.tsx new file mode 100644 index 0000000000000..d85a814d8addf --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/runechat.tsx @@ -0,0 +1,29 @@ +import { CheckboxInput, FeatureNumberInput, FeatureNumeric, FeatureToggle } from "../base"; + +export const chat_on_map: FeatureToggle = { + name: "Enable Runechat", + category: "RUNECHAT", + description: "Chat messages will show above heads.", + component: CheckboxInput, +}; + +export const see_chat_non_mob: FeatureToggle = { + name: "Enable Runechat on objects", + category: "RUNECHAT", + description: "Chat messages will show above objects when they speak.", + component: CheckboxInput, +}; + +export const see_rc_emotes: FeatureToggle = { + name: "Enable Runechat emotes", + category: "RUNECHAT", + description: "Emotes will show above heads.", + component: CheckboxInput, +}; + +export const max_chat_length: FeatureNumeric = { + name: "Max chat length", + category: "RUNECHAT", + description: "The maximum length a Runechat message will show as.", + component: FeatureNumberInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/scaling_method.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/scaling_method.tsx new file mode 100644 index 0000000000000..114cc80ef5e8e --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/scaling_method.tsx @@ -0,0 +1,11 @@ +import { createDropdownInput, Feature } from "../base"; + +export const scaling_method: Feature = { + name: "Scaling method", + category: "UI", + component: createDropdownInput({ + blur: "Bilinear", + distort: "Nearest Neighbor", + normal: "Point Sampling", + }), +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/screentips.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/screentips.tsx new file mode 100644 index 0000000000000..5d8575dd280eb --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/screentips.tsx @@ -0,0 +1,20 @@ +import { multiline } from "common/string"; +import { FeatureColorInput, Feature, FeatureToggle, CheckboxInput } from "../base"; + +export const screentip_color: Feature = { + name: "Screentips color", + category: "UI", + description: multiline` + The color of screen tips, the text you see when hovering over something. + `, + component: FeatureColorInput, +}; + +export const screentip_pref: FeatureToggle = { + name: "Enable screentips", + category: "UI", + description: multiline` + Enables screen tips, the text you see when hovering over something. + `, + component: CheckboxInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tgui.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tgui.tsx new file mode 100644 index 0000000000000..f436e9d45a59d --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tgui.tsx @@ -0,0 +1,15 @@ +import { CheckboxInput, FeatureToggle } from "../base"; + +export const tgui_fancy: FeatureToggle = { + name: "Enable fancy tgui", + category: "UI", + description: "Makes tgui windows look better, at the cost of compatibility.", + component: CheckboxInput, +}; + +export const tgui_lock: FeatureToggle = { + name: "Lock tgui to main monitor", + category: "UI", + description: "Locks tgui windows to your main monitor.", + component: CheckboxInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tooltips.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tooltips.tsx new file mode 100644 index 0000000000000..d3147a01a1f20 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tooltips.tsx @@ -0,0 +1,20 @@ +import { multiline } from "common/string"; +import { CheckboxInput, Feature, FeatureNumberInput, FeatureToggle } from "../base"; + +export const enable_tips: FeatureToggle = { + name: "Enable tooltips", + category: "TOOLTIPS", + description: multiline` + Do you want to see tooltips when hovering over items? + `, + component: CheckboxInput, +}; + +export const tip_delay: Feature = { + name: "Tooltip delay (in milliseconds)", + category: "TOOLTIPS", + description: multiline` + How long should it take to see a tooltip when hovering over items? + `, + component: FeatureNumberInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ui_style.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ui_style.tsx new file mode 100644 index 0000000000000..83a315bccac18 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ui_style.tsx @@ -0,0 +1,63 @@ +import { classes } from "common/react"; +import { FeatureChoiced, FeatureChoicedServerData, FeatureValueProps, sortChoices } from "../base"; +import { Box, Dropdown, Stack } from "../../../../../components"; + +const UIStyleInput = ( + props: FeatureValueProps +) => { + const { serverData, value } = props; + if (!serverData) { + return null; + } + + const { icons } = serverData; + + if (!icons) { + return (ui_style had no icons!); + } + + const choices = Object.fromEntries( + Object.entries(icons) + .map(([name, icon]) => { + return [name, ( + + + + + + + {name} + + + )]; + }) + ); + + return ( + { + return { + displayText: label, + value: dataValue, + }; + })} + /> + ); +}; + +export const UI_style: FeatureChoiced = { + name: "UI Style", + category: "UI", + component: UIStyleInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/widescreen.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/widescreen.tsx new file mode 100644 index 0000000000000..e4d7595775ee0 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/widescreen.tsx @@ -0,0 +1,7 @@ +import { CheckboxInput, FeatureToggle } from "../base"; + +export const widescreenpref: FeatureToggle = { + name: "Enable widescreen", + category: "UI", + component: CheckboxInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/window_flashing.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/window_flashing.tsx new file mode 100644 index 0000000000000..58f32c9476885 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/window_flashing.tsx @@ -0,0 +1,12 @@ +import { multiline } from "common/string"; +import { CheckboxInput, FeatureToggle } from "../base"; + +export const windowflashing: FeatureToggle = { + name: "Enable window flashing", + category: "UI", + description: multiline` + When toggled, some important events will make your game icon flash on your + task tray. + `, + component: CheckboxInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/index.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/index.ts new file mode 100644 index 0000000000000..d487a282e58cb --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/index.ts @@ -0,0 +1,23 @@ +// Unlike species and others, feature files export arrays of features +// rather than individual ones. This is because a lot of features are +// extremely small, and so it's easier for everyone to just combine them +// together. +// This still helps to prevent the server from needing to send client UI data +import { Feature } from "./base"; + +// while also preventing downstreams from needing to mutate existing files. +const features: Record> = {}; + +const requireFeature = require.context("./", true, /.tsx$/); + +for (const key of requireFeature.keys()) { + if (key === "index" || key === "base") { + continue; + } + + for (const [featureKey, feature] of Object.entries(requireFeature(key))) { + features[featureKey] = feature as Feature; + } +} + +export default features; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/randomization.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/randomization.tsx new file mode 100644 index 0000000000000..3d47560edf9e5 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/randomization.tsx @@ -0,0 +1,90 @@ +import { useBackend } from "../../../../backend"; +import { Button, Stack } from "../../../../components"; +import { PreferencesMenuData, RandomSetting } from "../../data"; +import { RandomizationButton } from "../../RandomizationButton"; +import { useRandomToggleState } from "../../useRandomToggleState"; +import { CheckboxInput, Feature, FeatureToggle } from "./base"; + +export const random_body: Feature = { + name: "Random body", + component: (props, context) => { + const [randomToggle, setRandomToggle] = useRandomToggleState(context); + + return ( + + + props.handleSetValue(newValue)} + value={props.value} + /> + + + { + randomToggle + ? ( + <> + + + + + + + + + ) + : ( + + + + ) + } + + + ); + }, +}; + +export const random_hardcore: FeatureToggle = { + name: "Hardcore random", + component: CheckboxInput, +}; + +export const random_name: Feature = { + name: "Random name", + component: (props, context) => { + return ( + props.handleSetValue(value)} + value={props.value} + /> + ); + }, +}; + +export const random_species: Feature = { + name: "Random species", + component: (props, context) => { + const { act, data } = useBackend(context); + + const species = data.character_preferences.randomization["species"]; + + return ( + act("set_random_preference", { + preference: "species", + value: newValue, + })} + value={species || RandomSetting.Disabled} + /> + ); + }, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx new file mode 100644 index 0000000000000..aafa1588fde8d --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx @@ -0,0 +1,51 @@ +import { FeatureColorInput, Feature, FeatureChoiced, FeatureDropdownInput } from "./base"; + +export const eye_color: Feature = { + name: "Eye color", + component: FeatureColorInput, +}; + +export const facial_hair_color: Feature = { + name: "Facial hair color", + component: FeatureColorInput, +}; + +export const hair_color: Feature = { + name: "Hair color", + component: FeatureColorInput, +}; + +export const feature_human_ears: FeatureChoiced = { + name: "Ears", + component: FeatureDropdownInput, +}; + +export const feature_human_tail: FeatureChoiced = { + name: "Tail", + component: FeatureDropdownInput, +}; + +export const feature_lizard_legs: FeatureChoiced = { + name: "Legs", + component: FeatureDropdownInput, +}; + +export const feature_lizard_spines: FeatureChoiced = { + name: "Spines", + component: FeatureDropdownInput, +}; + +export const feature_lizard_tail: FeatureChoiced = { + name: "Tail", + component: FeatureDropdownInput, +}; + +export const feature_mcolor: Feature = { + name: "Mutant color", + component: FeatureColorInput, +}; + +export const underwear_color: Feature = { + name: "Underwear color", + component: FeatureColorInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/gender.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/gender.ts new file mode 100644 index 0000000000000..787a3c900cb04 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/gender.ts @@ -0,0 +1,22 @@ +export enum Gender { + Male = "male", + Female = "female", + Other = "plural", +} + +export const GENDERS = { + [Gender.Male]: { + icon: "male", + text: "Male", + }, + + [Gender.Female]: { + icon: "female", + text: "Female", + }, + + [Gender.Other]: { + icon: "tg-non-binary", + text: "Other", + }, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/base.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/base.ts new file mode 100644 index 0000000000000..7f99548c5b500 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/base.ts @@ -0,0 +1,33 @@ +export type Species = { + description: string; + features: { + good: Feature[], + neutral: Feature[], + bad: Feature[], + }; + lore?: string[]; +}; + +export type Feature = { + icon: string; + name: string; + description: string; +}; + +export const fallbackSpecies: Species = { + description: "No description! File a bug report!", + features: { + good: [], + neutral: [], + bad: [], + }, +}; + +export const createLanguagePerk = (language: string): Feature => { + return { + icon: "comment", + name: "Native Speaker", + description: + `Alongside Galactic Common, gain the ability to speak ${language}.`, + }; +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/ethereal.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/ethereal.ts new file mode 100644 index 0000000000000..2ff0f9cc89176 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/ethereal.ts @@ -0,0 +1,64 @@ +import { createLanguagePerk, Species } from "./base"; + +const Ethereal: Species = { + description: "Coming from the planet of Sprout, the theocratic ethereals are \ + separated socially by caste, and espouse a dogma of aiding the weak and \ + downtrodden.", + features: { + good: [{ + icon: "bolt", + name: "Shockingly Tasty", + description: "Ethereals can feed on electricity from APCs, and do not \ + otherwise need to eat.", + }, { + icon: "lightbulb", + name: "Disco Ball", + description: "Ethereals passively generate their own light.", + }, { + icon: "shield-alt", + name: "Shock Resistance", + description: "Ethereals are less affected by shocks.", + }, { + icon: "temperature-high", + name: "Heat Resistance", + description: "Ethereals have much better tolerance for high \ + temperatures.", + }, createLanguagePerk("Voltaic")], + neutral: [{ + icon: "tint", + name: "Liquid Electricity", + description: "Ethereals have liquid electricity instead of blood. \ + Great for them, horrid for anyone else. Can make receiving medical \ + treatment harder.", + }, { + icon: "fire", + name: "Flaming Punch", + description: "Ethereals deal burn damage when punching instead of \ + brute damage.", + }, { + icon: "gem", + name: "Crystal Core", + description: "The hearts of ethereals will protect them in a cystal when \ + they die, reviving them with a permanent brain trauma.", + }], + bad: [{ + icon: "biohazard", + name: "Starving Artist", + description: "Ethereals take toxin damage while starving.", + }, { + icon: "fist-raised", + name: "Brutal Weakness", + description: "Ethereals are weak to brute damage.", + }, { + icon: "temperature-low", + name: "Cold Weakness", + description: "Ethereals have much lower tolerance for cold \ + temperatures.", + }], + }, + lore: [ + "Ethereals are a species native to the planet Sprout. When they were originally discovered, they were at a medieval level of technological progression, but due to their natural acclimation with electricity, they felt easy among the large NanoTrasen installations.", + ], +}; + +export default Ethereal; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/felinid.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/felinid.ts new file mode 100644 index 0000000000000..4ab3ff566bd69 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/felinid.ts @@ -0,0 +1,28 @@ +import { createLanguagePerk, Species } from "./base"; + +const Felinid: Species = { + description: "Felinids are one of the many types of bespoke genetic \ + modifications to come of humanity's mastery of genetic science, and are \ + also one of the most common. Meow?", + features: { + good: [{ + icon: "grin-tongue", + name: "Grooming", + description: "Felinids can lick wounds to reduce bleeding.", + }, createLanguagePerk("Nekomimetic")], + neutral: [], + bad: [{ + icon: "assistive-listening-systems", + name: "Sensitive Hearing", + description: "Felinids are more sensitive to loud sounds, such as \ + flashbangs.", + }], + }, + lore: [ + "Bio-engineering at its felinest, felinids are the peak example of humanity's mastery of genetic code. One of many \"animalid\" variants, felinids are the most popular and common, as well as one of the biggest points of contention in genetic-modification.", + "Body modders were eager to splice human and feline DNA in search of the holy trifecta: ears, eyes, and tail. These traits were in high demand, with the corresponding side effects of vocal and neurochemical changes being seen as a minor inconvenience.", + "Sadly for the felinids, they were not minor inconveniences. Shunned as subhuman and monstrous by many, felinids (and other animalids) sought their greener pastures out in the colonies, cloistering in communities of their own kind. As a result, outer human space has a high animalid population.", + ], +}; + +export default Felinid; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/human.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/human.ts new file mode 100644 index 0000000000000..30b1c28853b41 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/human.ts @@ -0,0 +1,30 @@ +import { Species } from "./base"; + +const Human: Species = { + description: "Humans are the dominant species in the known galaxy, their \ + kind extend from old Earth to the edges of known space.", + features: { + good: [{ + icon: "robot", + name: "Asimov Superiority", + description: "The AI and their cyborgs are, by default, subservient only \ + to humans. As a human, silicons are required to both protect and obey \ + you.", + }, { + icon: "bullhorn", + name: "Chain of Command", + description: "Nanotrasen only recognizes humans for command roles, such \ + as Captain.", + }], + neutral: [], + bad: [], + }, + lore: [ + "These primate-descended creatures, originating from the mostly harmless Earth, have long-since outgrown their home and semi-benign designation. The space age has taken humans out of their solar system and into the galaxy-at-large.", + "In traditional human fashion, this near-record pace from terra firma to the final frontier spat in the face of other races they now shared a stage with. This included the lizards - if anyone was offended by these upstarts, it was certainly lizardkind.", + "Humanity never managed to find the kind of peace to fully unite under one banner like other species. The pencil and paper pushing of the UN bureaucrat lives on in the mosaic that is TerraGov; a composite of the nation-states that still live on in human society.", + "The human spirit of opportunity and enterprise continues on in its peak form: the hypercorporation. Acting outside of TerraGov's influence, literally and figuratively, hypercorporations buy the senate votes they need and establish territory far past the Earth Government's reach. In hypercorporation territory company policy is law, giving new meaning to \"employee termination\".", + ], +}; + +export default Human; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/lizard.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/lizard.ts new file mode 100644 index 0000000000000..0345b469dab97 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/lizard.ts @@ -0,0 +1,32 @@ +import { createLanguagePerk, Species } from "./base"; + +const Lizard: Species = { + description: "The militaristic hail originally from Tizira, but have grown \ + throughout their centuries in the stars to possess a large spacefaring \ + empire: though now they must contend with their younger, more \ + technologically advanced human neighbours.", + features: { + good: [createLanguagePerk("Draconic")], + neutral: [{ + icon: "thermometer-empty", + name: "Cold-blooded", + description: "Higher tolerance for high temperatures, but lower \ + tolerance for cold temperatures.", + }], + bad: [{ + icon: "tint", + name: "Exotic Blood", + description: "Lizards have a unique \"L\" type blood, which can make \ + receiving medical treatment more difficult.", + }], + }, + lore: [ + "The face of conspiracy theory was changed forever the day mankind met the lizards.", + "Hailing from the arid world of Tizira, lizards were travelling the stars back when mankind was first discovering how neat trains could be. However, much like the space-fable of the space-tortoise and space-hare, lizards have rejected their kin's motto of \"slow and steady\" in favor of resting on their laurels and getting completely surpassed by 'bald apes', due in no small part to their lack of access to plasma.", + "The history between lizards and humans has resulted in many conflicts that lizards ended on the losing side of, with the finale being an explosive remodeling of their moon. Today's lizard-human relations are seeing the continuance of a record period of peace.", + "Lizard culture is inherently militaristic, though the influence the military has on lizard culture begins to lessen the further colonies lie from their homeworld - with some distanced colonies finding themselves subsumed by the cultural practices of other species nearby.", + "On their homeworld, lizards celebrate their 16th birthday by enrolling in a mandatory 5 year military tour of duty. Roles range from combat to civil service and everything in between. As the old slogan goes: \"Your place will be found!\"", + ], +}; + +export default Lizard; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/moth.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/moth.ts new file mode 100644 index 0000000000000..338c650ddbb03 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/moth.ts @@ -0,0 +1,38 @@ +import { createLanguagePerk, Species } from "./base"; + +const Moth: Species = { + description: "Hailing from a planet that was lost long ago, the moths travel \ + the galaxy as a nomadic people aboard a colossal fleet of ships, seeking a \ + new homeland.", + features: { + good: [{ + icon: "feather-alt", + name: "Precious Wings", + description: "Moths can fly in pressurized, zero-g environments using \ + their wings.", + }, { + icon: "tshirt", + name: "Meal Plan", + description: "Moths can eat clothes for nourishment.", + }, createLanguagePerk("Moffic")], + neutral: [], + bad: [{ + icon: "fire", + name: "Ablazed Wings", + description: "Moth wings are fragile, and can be easily burnt off.", + }, { + icon: "sun", + name: "Bright Lights", + description: "Moths need an extra layer of flash protection to protect \ + themselves, such as against security officers or when welding. Welding \ + masks will work.", + }], + }, + lore: [ + "Their homeworld lost to the ages, the moths live aboard the Grand Nomad Fleet. Made up of what could be found, bartered, repaired, or stolen the armada is a colossal patchwork built on a history of politely flagging travelers down and taking their things. Occasionally a moth will decide to leave the fleet, usually to strike out for fortunes to send back home.", + "Nomadic life produces a tight-knit culture, with moths valuing their friends, family, and vessels highly. Moths are gregarious by nature and do best in communal spaces. This has served them well on the galactic stage, maintaining a friendly and personable reputation even in the face of hostile encounters. It seems that the galaxy has come to accept these former pirates.", + "Surprisingly, living together in a giant fleet hasn't flattened variance in dialect and culture. These differences are welcomed and encouraged within the fleet for the variety that they bring.", + ], +}; + +export default Moth; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/plasmaman.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/plasmaman.ts new file mode 100644 index 0000000000000..34a7bee72d240 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/plasmaman.ts @@ -0,0 +1,71 @@ +import { createLanguagePerk, Species } from "./base"; + +const Plasmaman: Species = { + description: "Found on the Icemoon of Freyja, plasmamen consist of colonial \ + fungal organisms which together form a sentient being. In human space, \ + they're usually attached to skeletons to afford a human touch.", + features: { + good: [{ + icon: "shield-alt", + name: "Protected", + description: "Plasmamen are immune to radiation, poisons, and most \ + diseases.", + }, { + icon: "tint-slash", + name: "Bloodletted", + description: "Plasmamen do not have blood.", + }, { + icon: "bone", + name: "Wound Resistance", + description: "Plasmamen have higher tolerance for damage that would \ + wound others.", + }, { + icon: "temperature-low", + name: "Cold Resistance", + description: "Plasmamen have a higher resistance to cold temperatures.", + }, { + icon: "wind", + name: "Plasma Healing", + description: "Plasmamen can heal wounds by consuming plasma.", + }, { + icon: "hard-hat", + name: "Protective Helmet", + description: "Plasmamen's helmets provide them shielding from the \ + flashes of welding, as well as a flashlight.", + }, createLanguagePerk("Calcic")], + neutral: [], + bad: [{ + icon: "fire", + name: "Human* Torch", + description: "Plasmamen instantly ignite when their body makes contact \ + with oxygen.", + }, { + icon: "wind", + name: "Plasma Breathing", + description: "Plasmamen must breathe plasma to survive. You receive a \ + tank when you arrive.", + }, { + icon: "temperature-high", + name: "Heat Weakness", + description: "Plasmamen have a lower resistance to high temperatures.", + }, { + icon: "fist-raised", + name: "Total Weakness", + description: "Plasmamen take more burn and brute damage.", + }, { + icon: "briefcase-medical", + name: "An Apple a Day", + description: "Plasmamen take specialized medical knowledge to be \ + treated. Do not expect speedy revival, if you are lucky enough to get \ + one at all.", + }], + }, + lore: [ + "A confusing species, plasmamen are truly \"a fungus among us\". What appears to be a singular being is actually a colony of millions of organisms surrounding a found (or provided) skeletal structure.", + "Originally discovered by NT when a researcher fell into an open tank of liquid plasma, the previously unnoticed fungal colony overtook the body creating the first \"true\" plasmaman. The process has since been streamlined via generous donations of convict corpses and plasmamen have been deployed en masse throughout NT to bolster the workforce.", + "New to the galactic stage, plasmamen are a blank slate. Their appearance, generally regarded as \"ghoulish\", inspires a lot of apprehension in their crewmates. It might be the whole \"flammable purple skeleton\" thing.", + "The colonids that make up plasmamen require the plasma-rich atmosphere they evolved in. Their psuedo-nervous system runs with externalized electrical impulses that immediately ignite their plasma-based bodies when oxygen is present.", + ], +}; + +export default Plasmaman; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/useRandomToggleState.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/useRandomToggleState.ts new file mode 100644 index 0000000000000..6721ee70cf5c4 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/useRandomToggleState.ts @@ -0,0 +1,4 @@ +import { useLocalState } from "../../backend"; + +export const useRandomToggleState + = context => useLocalState(context, "randomToggle", false); diff --git a/tgui/packages/tgui/interfaces/ScannerGate.js b/tgui/packages/tgui/interfaces/ScannerGate.js index 9bc0bb8f7cb54..f9e15d49c2e2d 100644 --- a/tgui/packages/tgui/interfaces/ScannerGate.js +++ b/tgui/packages/tgui/interfaces/ScannerGate.js @@ -1,5 +1,5 @@ import { useBackend } from '../backend'; -import { Box, Button, LabeledList, NumberInput, Section } from '../components'; +import { Box, Button, LabeledList, Section } from '../components'; import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox'; import { Window } from '../layouts'; diff --git a/tgui/packages/tgui/interfaces/Telecomms.js b/tgui/packages/tgui/interfaces/Telecomms.js index 24a6a169bed80..f52a570065100 100644 --- a/tgui/packages/tgui/interfaces/Telecomms.js +++ b/tgui/packages/tgui/interfaces/Telecomms.js @@ -1,5 +1,3 @@ -import { map, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { useBackend } from '../backend'; import { Button, Input, LabeledList, Section, Table, NoticeBox, NumberInput, LabeledControls, Box } from '../components'; import { RADIO_CHANNELS } from '../constants'; diff --git a/tgui/packages/tgui/layouts/Window.js b/tgui/packages/tgui/layouts/Window.js index b0707ffb9a3d8..7960006f2bab0 100644 --- a/tgui/packages/tgui/layouts/Window.js +++ b/tgui/packages/tgui/layouts/Window.js @@ -9,7 +9,7 @@ import { useDispatch } from 'common/redux'; import { decodeHtmlEntities, toTitleCase } from 'common/string'; import { Component } from 'inferno'; import { backendSuspendStart, useBackend } from '../backend'; -import { Icon, Flex } from '../components'; +import { Icon } from '../components'; import { UI_DISABLED, UI_INTERACTIVE, UI_UPDATE } from '../constants'; import { useDebug } from '../debug'; import { toggleKitchenSink } from '../debug/actions'; diff --git a/tgui/packages/tgui/stories/Popper.stories.js b/tgui/packages/tgui/stories/Popper.stories.js index 652cee92877c3..30043de079b5d 100644 --- a/tgui/packages/tgui/stories/Popper.stories.js +++ b/tgui/packages/tgui/stories/Popper.stories.js @@ -1,5 +1,4 @@ -import { Component, forwardRef } from 'inferno'; -import { Box, Popper } from '../components'; +import { Box, Popper } from "../components"; export const meta = { title: 'Popper', diff --git a/tgui/packages/tgui/stories/Tooltip.stories.js b/tgui/packages/tgui/stories/Tooltip.stories.js index bba37c417bac9..306929ba767a5 100644 --- a/tgui/packages/tgui/stories/Tooltip.stories.js +++ b/tgui/packages/tgui/stories/Tooltip.stories.js @@ -4,7 +4,6 @@ * @license MIT */ -import { Placement } from '@popperjs/core'; import { Box, Button, Section, Tooltip } from '../components'; export const meta = { diff --git a/tgui/packages/tgui/styles/atomic/centered-image.scss b/tgui/packages/tgui/styles/atomic/centered-image.scss new file mode 100644 index 0000000000000..cce5bfdf2c110 --- /dev/null +++ b/tgui/packages/tgui/styles/atomic/centered-image.scss @@ -0,0 +1,7 @@ +.centered-image { + position: absolute; + height: 100%; + left: 50%; + top: 50%; + transform: translateX(-50%) translateY(-50%) scale(0.8); +} diff --git a/tgui/packages/tgui/styles/atomic/fit-text.scss b/tgui/packages/tgui/styles/atomic/fit-text.scss new file mode 100644 index 0000000000000..2a899601ba26a --- /dev/null +++ b/tgui/packages/tgui/styles/atomic/fit-text.scss @@ -0,0 +1,10 @@ +$mqIterations: 19; +@mixin fontResize($iterations) { + $i: 1; + @while $i <= $iterations { + @media all and (min-width: 100px * $i) { .fit-text { font-size:0.1em * $i; } } + $i: $i + 1; + } +} + +@include fontResize($mqIterations); diff --git a/tgui/packages/tgui/styles/colors.scss b/tgui/packages/tgui/styles/colors.scss index aca122de1fc08..d1a9fc4fd9de9 100644 --- a/tgui/packages/tgui/styles/colors.scss +++ b/tgui/packages/tgui/styles/colors.scss @@ -22,6 +22,7 @@ $purple: #a333c8 !default; $pink: #e03997 !default; $brown: #a5673f !default; $grey: #767676 !default; +$light-grey: #aaa !default; $primary: #4972a1 !default; $good: #5baa27 !default; @@ -58,6 +59,7 @@ $_gen_map: ( 'pink': $pink, 'brown': $brown, 'grey': $grey, + 'light-grey': $light-grey, 'good': $good, 'average': $average, 'bad': $bad, diff --git a/tgui/packages/tgui/styles/components/Dropdown.scss b/tgui/packages/tgui/styles/components/Dropdown.scss index 66f357e9e1a82..6909f8d931238 100644 --- a/tgui/packages/tgui/styles/components/Dropdown.scss +++ b/tgui/packages/tgui/styles/components/Dropdown.scss @@ -74,7 +74,6 @@ .Dropdown__selected-text { display: inline-block; text-overflow: ellipsis; - overflow: hidden; white-space: nowrap; height: base.em(17px); width: calc(100% - 1.2em); diff --git a/tgui/packages/tgui/styles/components/LabeledList.scss b/tgui/packages/tgui/styles/components/LabeledList.scss index 411af74c9a13e..94adf932549d5 100644 --- a/tgui/packages/tgui/styles/components/LabeledList.scss +++ b/tgui/packages/tgui/styles/components/LabeledList.scss @@ -32,7 +32,6 @@ padding: 0.25em 0.5em; border: 0; text-align: left; - vertical-align: baseline; } .LabeledList__label--nowrap { diff --git a/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss b/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss new file mode 100644 index 0000000000000..6f936a1ce4c25 --- /dev/null +++ b/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss @@ -0,0 +1,241 @@ +@use 'sass:color'; +@use 'sass:map'; +@use '../components/Button.scss'; +@use '../colors.scss'; + +$department_map: ( + 'Assistant': colors.$grey, + 'Captain': colors.fg(colors.$blue), + 'Command': colors.$yellow, + 'Security': colors.$red, + 'Engineering': #f1a839, + 'Medical': colors.$teal, + 'Misc': colors.$white, + 'Science': colors.fg(colors.$purple), + 'Supply': colors.$brown, + 'Service': colors.$green, + 'Silicon': colors.$pink, +); + +.PreferencesMenu { + &__Antags { + &__antagSelection { + $antagonist_bottom_padding: 10px; + + margin-bottom: -$antagonist_bottom_padding; + + @mixin animate-hover { + .antagonist-icon-parent .antagonist-icon { + &:hover { + transform: scale(1.3); + transition: transform 1s ease-out; + } + } + } + + &__antagonist { + padding-bottom: $antagonist_bottom_padding; + padding-right: 20px; + + .antagonist-icon-parent { + border-style: solid; + border-radius: 50%; + border-width: 4px; + box-sizing: content-box; + overflow: hidden; + position: relative; + + height: 96px; + width: 96px; + + .antagonist-icon { + border-radius: 50%; + -ms-interpolation-mode: nearest-neighbor; + overflow: hidden; + transition: transform 0.1s ease-in; + } + } + + &--off { + @include animate-hover; + + .antagonist-icon-parent { + border-color: colors.$red; + + .antagonist-icon { + opacity: 0.5; + + &:hover { + opacity: 1; + } + } + } + } + + &--on { + @include animate-hover; + + .antagonist-icon-parent { + border-color: colors.$green; + } + } + + &--banned { + .antagonist-icon-parent { + border-color: colors.$grey; + } + + .antagonist-icon { + opacity: 0.5; + } + } + + .antagonist-banned-slash { + background: colors.$grey; + + width: 100%; + height: 3px; + + position: absolute; + top: 50%; + left: 50%; + transform: translateY(-50%) translateX(-50%) rotate(35deg); + + opacity: 0.8; + } + + .antagonist-days-left { + text-align: center; + text-shadow: 1px 1px 1px #222; + + width: 100%; + + position: absolute; + top: 50%; + left: 50%; + transform: translateY(-50%) translateX(-50%); + } + } + } + } + + &__Jobs { + > * { + flex: 1; + } + + &__departments { + @each $department-name, $color-value in $department_map { + &--#{$department-name} { + &.head { + background: $color-value; + + .job-name { + font-weight: bold; + } + } + + background: colors.fg($color-value); + border-bottom: 2px solid rgba(0, 0, 0, 0.3); + border-left: 2px solid rgba(0, 0, 0, 0.3); + border-right: 2px solid rgba(0, 0, 0, 0.3); + color: black; + + > * { + height: calc(100% + 0.2em); + padding-bottom: 0.2em; + } + + &:first-child { + border-top: 2px solid rgba(0, 0, 0, 0.3); + } + + .options { + background: rgba(0, 0, 0, 0.2); + height: 100%; + } + } + + &--Captain { + border: 3px solid rgba(200, 200, 0, 1); + + &:first-child { + border-top: 3px solid rgba(200, 200, 0, 1); + } + + .job-name { + font-size: 17px; + } + } + } + + &__priority { + border: 1px solid rgba(0, 0, 0, 0.3); + + &--off::after { + content: ""; + + background: rgba(0, 0, 0, 0.2); + display: block; + height: 80%; + left: 50%; + position: relative; + top: 50%; + transform: translateX(-50%) translateY(-50%) rotate(40deg); + width: 2px; + } + } + } + + &__PriorityHeader { + font-weight: bold; + transform: + translateX(-4px) + translateY(-8px) + rotate(315deg); + white-space: nowrap; + width: 19px; + } + } + + &__Quirks { + &__QuirkList { + background-color: colors.$light-grey; + height: calc(90vh - 170px); + min-height: 100%; + overflow-y: scroll; + + &__quirk { + background-color: colors.$white; + border-bottom: 1px solid black; + color: #111; + transition: background-color 0.1s ease-in; + + $quality_map: ( + "positive": colors.$green, + "neutral": colors.$white, + "negative": colors.$red, + ); + + @each $quality, $color-value in $quality_map { + &--#{$quality} { + background-color: $color-value; + transition: background-color 0.1s ease-in; + } + } + + &:hover { + background-color: colors.$grey; + transition: background-color 0.1s ease-out; + + @each $quality, $color-value in $quality_map { + .PreferencesMenu__Quirks__QuirkList__quirk--#{$quality} { + background-color: color.scale($color-value, $lightness: -25%); + transition: background-color 0.1s ease-out; + } + } + } + } + } + } +} diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss index b1da50a16620e..6dd8aafe7ed71 100644 --- a/tgui/packages/tgui/styles/main.scss +++ b/tgui/packages/tgui/styles/main.scss @@ -11,8 +11,11 @@ // Atomic classes @include meta.load-css('./atomic/candystripe.scss'); +@include meta.load-css('./atomic/centered-image.scss'); @include meta.load-css('./atomic/color.scss'); @include meta.load-css('./atomic/debug-layout.scss'); +@include meta.load-css('./atomic/fit-text.scss'); +@include meta.load-css('./atomic/links.scss'); @include meta.load-css('./atomic/outline.scss'); @include meta.load-css('./atomic/text.scss'); @@ -48,6 +51,7 @@ @include meta.load-css('./interfaces/ModularFabricator.scss'); @include meta.load-css('./interfaces/OrbitalMap.scss'); @include meta.load-css('./interfaces/Paper.scss'); +@include meta.load-css('./interfaces/PreferencesMenu.scss'); @include meta.load-css('./interfaces/Roulette.scss'); @include meta.load-css('./interfaces/IntegratedCircuit.scss'); @include meta.load-css('./interfaces/Techweb.scss'); diff --git a/tgui/yarn.lock b/tgui/yarn.lock index 8fd6df26a8eea..0a85f455e55ff 100644 --- a/tgui/yarn.lock +++ b/tgui/yarn.lock @@ -4926,6 +4926,28 @@ __metadata: languageName: node linkType: hard +"eslint-plugin-unused-imports@npm:^2.0.0": + version: 2.0.0 + resolution: "eslint-plugin-unused-imports@npm:2.0.0" + dependencies: + eslint-rule-composer: ^0.3.0 + peerDependencies: + "@typescript-eslint/eslint-plugin": ^5.0.0 + eslint: ^8.0.0 + peerDependenciesMeta: + "@typescript-eslint/eslint-plugin": + optional: true + checksum: 8aa1e03e75da2a62a354065e0cb8fe370118c6f8d9720a32fe8c1da937de6adb81a4fed7d0d391d115ac9453b49029fb19f970d180a2cf3dba451fd4c20f0dc4 + languageName: node + linkType: hard + +"eslint-rule-composer@npm:^0.3.0": + version: 0.3.0 + resolution: "eslint-rule-composer@npm:0.3.0" + checksum: c2f57cded8d1c8f82483e0ce28861214347e24fd79fd4144667974cd334d718f4ba05080aaef2399e3bbe36f7d6632865110227e6b176ed6daa2d676df9281b1 + languageName: node + linkType: hard + "eslint-scope@npm:5.1.1": version: 5.1.1 resolution: "eslint-scope@npm:5.1.1" @@ -10452,6 +10474,7 @@ __metadata: eslint-config-prettier: ^8.8.0 eslint-plugin-react: ^7.32.2 eslint-plugin-sonarjs: ^0.18.0 + eslint-plugin-unused-imports: ^2.0.0 file-loader: ^6.2.0 inferno: ^8.2.1 jest: ^29.5.0 From 8581eec8d29ff5be70c0f62c6e553871ecc5e0a6 Mon Sep 17 00:00:00 2001 From: AndrewL97 Date: Thu, 23 Dec 2021 15:07:53 +1000 Subject: [PATCH 017/269] Fix skin tone sorting and refactor color procs (#63554) * Remove superfluous hash from hex2rgb call Add stack_trace to hex2rgb to catch future nonsense --- code/__HELPERS/colors.dm | 5 +- code/__HELPERS/type2type.dm | 77 +++---------------- .../modules/awaymissions/super_secret_room.dm | 2 +- code/modules/client/preferences/skin_tone.dm | 3 +- .../computers/item/computer_ui.dm | 2 +- .../wiremod/components/action/light.dm | 15 ++-- 6 files changed, 22 insertions(+), 82 deletions(-) diff --git a/code/__HELPERS/colors.dm b/code/__HELPERS/colors.dm index aee61fbea4d01..c893ab11b17cb 100644 --- a/code/__HELPERS/colors.dm +++ b/code/__HELPERS/colors.dm @@ -48,9 +48,8 @@ /// Given a color in the format of "#RRGGBB", will return if the color /// is dark. -/proc/is_color_dark(color, threshold = 0.25) - var/list/rgb = hex2rgb(color) - var/list/hsl = rgb2hsl(rgb[1], rgb[2], rgb[3]) +/proc/is_color_dark(color, threshold = 25) + var/hsl = rgb2num(color, COLORSPACE_HSL) return hsl[3] < threshold /// Given a 3 character color (no hash), converts it into #RRGGBB (with hash) diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index 75e37968f5d63..3904581ebc06b 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -174,73 +174,6 @@ . = "NONE" return . -/// Converts an RGB color to an HSL color -/proc/rgb2hsl(red, green, blue) - red /= 255;green /= 255;blue /= 255; - var/max = max(red,green,blue) - var/min = min(red,green,blue) - var/range = max-min - - var/hue=0;var/saturation=0;var/lightness=0; - lightness = (max + min)/2 - if(range != 0) - if(lightness < 0.5) - saturation = range/(max+min) - else - saturation = range/(2-max-min) - - var/dred = ((max-red)/(6*max)) + 0.5 - var/dgreen = ((max-green)/(6*max)) + 0.5 - var/dblue = ((max-blue)/(6*max)) + 0.5 - - if(max==red) - hue = dblue - dgreen - else if(max==green) - hue = dred - dblue + (1/3) - else - hue = dgreen - dred + (2/3) - if(hue < 0) - hue++ - else if(hue > 1) - hue-- - - return list(hue, saturation, lightness) - -/// Converts an HSL color to an RGB color -/proc/hsl2rgb(hue, saturation, lightness) - var/red;var/green;var/blue; - if(saturation == 0) - red = lightness * 255 - green = red - blue = red - else - var/a;var/b; - if(lightness < 0.5) - b = lightness*(1+saturation) - else - b = (lightness+saturation) - (saturation*lightness) - a = 2*lightness - b - - red = round(255 * hue2rgb(a, b, hue+(1/3))) - green = round(255 * hue2rgb(a, b, hue)) - blue = round(255 * hue2rgb(a, b, hue-(1/3))) - - return list(red, green, blue) - -/// Converts an ABH color to an RGB color -/proc/hue2rgb(a, b, hue) - if(hue < 0) - hue++ - else if(hue > 1) - hue-- - if(6*hue < 1) - return (a+(b-a)*6*hue) - if(2*hue < 1) - return b - if(3*hue < 2) - return (a+(b-a)*((2/3)-hue)*6) - return a - /// Very ugly, BYOND doesn't support unix time and rounding errors make it really hard to convert it to BYOND time. returns "YYYY-MM-DD" by default /proc/unix2date(timestamp, seperator = "-") @@ -395,6 +328,7 @@ else . = max(0, min(255, 138.5177312231 * log(temp - 10) - 305.0447927307)) +<<<<<<< HEAD /// Converts a text color like "red" to a hex color ("#FF0000") /proc/color2hex(color) //web colors @@ -444,6 +378,12 @@ /** This is a weird one: It returns a list of all var names found in the string. These vars must be in the [var_name] format +======= +//This is a weird one: +//It returns a list of all var names found in the string +//These vars must be in the [var_name] format +//It's only a proc because it's used in more than one place +>>>>>>> 619acc1624c (Fix skin tone sorting and refactor color procs (#63554)) It's only a proc because it's used in more than one place @@ -476,6 +416,7 @@ Takes a string and a datum. The string is well, obviously the string being check if(var_source.vars.Find(A)) . += A +<<<<<<< HEAD /// Converts a hex code to a number /proc/color_hex2num(A) if(!A || length(A) != length_char(A)) @@ -485,6 +426,8 @@ Takes a string and a datum. The string is well, obviously the string being check var/B = hex2num(copytext(A, 6, 8)) return R+G+B +======= +>>>>>>> 619acc1624c (Fix skin tone sorting and refactor color procs (#63554)) //word of warning: using a matrix like this as a color value will simplify it back to a string after being set /proc/color_hex2color_matrix(string) var/length = length(string) diff --git a/code/modules/awaymissions/super_secret_room.dm b/code/modules/awaymissions/super_secret_room.dm index ee37438848ead..81f6a5f036d98 100644 --- a/code/modules/awaymissions/super_secret_room.dm +++ b/code/modules/awaymissions/super_secret_room.dm @@ -126,7 +126,7 @@ /obj/item/rupee/Initialize(mapload) . = ..() - var/newcolor = color2hex(pick(10;"green", 5;"blue", 3;"red", 1;"purple")) + var/newcolor = pick(10;COLOR_GREEN, 5;COLOR_BLUE, 3;COLOR_RED, 1;COLOR_PURPLE) add_atom_colour(newcolor, FIXED_COLOUR_PRIORITY) var/static/list/loc_connections = list( COMSIG_ATOM_ENTERED = PROC_REF(on_entered), diff --git a/code/modules/client/preferences/skin_tone.dm b/code/modules/client/preferences/skin_tone.dm index 60e71da430adb..119fd6a86a314 100644 --- a/code/modules/client/preferences/skin_tone.dm +++ b/code/modules/client/preferences/skin_tone.dm @@ -14,8 +14,7 @@ var/list/to_hex = list() for (var/choice in get_choices()) var/hex_value = skintone2hex(choice) - var/list/rgb = hex2rgb("#[hex_value]") - var/list/hsl = rgb2hsl(rgb[1], rgb[2], rgb[3]) + var/list/hsl = rgb2num(hex_value, COLORSPACE_HSL) to_hex[choice] = list( "lightness" = hsl[3], diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm index 58a0b7be31339..c70aae319aaf8 100644 --- a/code/modules/modular_computers/computers/item/computer_ui.dm +++ b/code/modules/modular_computers/computers/item/computer_ui.dm @@ -216,7 +216,7 @@ new_color = input(user, "Choose a new color for [src]'s flashlight.", "Light Color",light_color) as color|null if(!new_color) return - if(color_hex2num(new_color) < 200) //Colors too dark are rejected + if(is_color_dark(new_color, 50) ) //Colors too dark are rejected to_chat(user, "That color is too dark! Choose a lighter one.") new_color = null return set_flashlight_color(new_color) diff --git a/code/modules/wiremod/components/action/light.dm b/code/modules/wiremod/components/action/light.dm index 7dfbcc20764c5..a192a0444bffd 100644 --- a/code/modules/wiremod/components/action/light.dm +++ b/code/modules/wiremod/components/action/light.dm @@ -19,7 +19,7 @@ var/datum/port/input/on var/max_power = 5 - var/min_lightness = 0.4 + var/min_lightness = 40 var/shell_light_color /obj/item/circuit_component/light/get_ui_notices() @@ -54,13 +54,12 @@ /obj/item/circuit_component/light/input_received(datum/port/input/port) . = ..() - brightness.set_input(clamp(brightness.input_value || 0, 0, max_power), FALSE) - red.set_input(clamp(red.input_value, 0, 255), FALSE) - blue.set_input(clamp(blue.input_value, 0, 255), FALSE) - green.set_input(clamp(green.input_value, 0, 255), FALSE) - var/list/hsl = rgb2hsl(red.input_value || 0, green.input_value || 0, blue.input_value || 0) - var/list/light_col = hsl2rgb(hsl[1], hsl[2], max(min_lightness, hsl[3])) - shell_light_color = rgb(light_col[1], light_col[2], light_col[3]) + brightness.set_value(clamp(brightness.value || 0, 0, max_power)) + red.set_value(clamp(red.value, 0, 255)) + blue.set_value(clamp(blue.value, 0, 255)) + green.set_value(clamp(green.value, 0, 255)) + var/list/hsl = rgb2num(rgb(red.value || 0, green.value || 0, blue.value || 0), COLORSPACE_HSL) + shell_light_color = rgb(hsl[1], hsl[2], max(min_lightness, hsl[3]), space=COLORSPACE_HSL) if(.) return From d0ff5309dcc2c425afacc6a14b79f0e0efe350a3 Mon Sep 17 00:00:00 2001 From: Mothblocks <35135081+Mothblocks@users.noreply.github.com> Date: Sun, 12 Dec 2021 21:49:26 -0800 Subject: [PATCH 018/269] Fix skin tones on the preferences menu not having colors (#63370) This was broken by that PR that changed how color preferences worked. --- code/__HELPERS/type2type.dm | 67 ------------------- .../character_preferences/skin_tone.tsx | 2 +- 2 files changed, 1 insertion(+), 68 deletions(-) diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index 3904581ebc06b..800628c4b73de 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -328,63 +328,8 @@ else . = max(0, min(255, 138.5177312231 * log(temp - 10) - 305.0447927307)) -<<<<<<< HEAD - -/// Converts a text color like "red" to a hex color ("#FF0000") -/proc/color2hex(color) //web colors - if(!color) - return "#000000" - - switch(color) - if("white") - return "#FFFFFF" - if("black") - return "#000000" - if("gray") - return "#808080" - if("brown") - return "#A52A2A" - if("red") - return "#FF0000" - if("darkred") - return "#8B0000" - if("crimson") - return "#DC143C" - if("orange") - return "#FFA500" - if("yellow") - return "#FFFF00" - if("green") - return "#008000" - if("lime") - return "#00FF00" - if("darkgreen") - return "#006400" - if("cyan") - return "#00FFFF" - if("blue") - return "#0000FF" - if("navy") - return "#000080" - if("teal") - return "#008080" - if("purple") - return "#800080" - if("indigo") - return "#4B0082" - else - return "#FFFFFF" - - /** This is a weird one: It returns a list of all var names found in the string. These vars must be in the [var_name] format -======= -//This is a weird one: -//It returns a list of all var names found in the string -//These vars must be in the [var_name] format -//It's only a proc because it's used in more than one place ->>>>>>> 619acc1624c (Fix skin tone sorting and refactor color procs (#63554)) - It's only a proc because it's used in more than one place Takes a string and a datum. The string is well, obviously the string being checked. The datum is used as a source for var names, to check validity. Otherwise every single word could technically be a variable! @@ -416,18 +361,6 @@ Takes a string and a datum. The string is well, obviously the string being check if(var_source.vars.Find(A)) . += A -<<<<<<< HEAD -/// Converts a hex code to a number -/proc/color_hex2num(A) - if(!A || length(A) != length_char(A)) - return 0 - var/R = hex2num(copytext(A, 2, 4)) - var/G = hex2num(copytext(A, 4, 6)) - var/B = hex2num(copytext(A, 6, 8)) - return R+G+B - -======= ->>>>>>> 619acc1624c (Fix skin tone sorting and refactor color procs (#63554)) //word of warning: using a matrix like this as a color value will simplify it back to a string after being set /proc/color_hex2color_matrix(string) var/length = length(string) diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skin_tone.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skin_tone.tsx index 40856fed36aa8..5bd5096367c02 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skin_tone.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skin_tone.tsx @@ -41,7 +41,7 @@ export const skin_tone: Feature = { Date: Wed, 29 Mar 2023 03:35:22 -0500 Subject: [PATCH 019/269] Some fixes and commenting stuff out --- beestation.dme | 61 ++++++++- code/__DEFINES/colors.dm | 3 +- code/__DEFINES/preferences.dm | 5 + code/__HELPERS/sanitize_values.dm | 13 -- code/datums/brain_damage/imaginary_friend.dm | 11 +- code/datums/mind.dm | 46 +++---- .../antagonists/changeling/changeling.dm | 7 -- code/modules/client/preferences.dm | 77 ++++++------ code/modules/client/preferences/clothing.dm | 4 +- .../preferences/tgui_prefs_migration.dm | 116 ------------------ .../client/preferences2/preferences2.dm | 66 +++++----- .../living/silicon/ai/ai_portrait_picker.dm | 8 +- code/modules/mob/login.dm | 4 - config/game_options.txt | 3 - tgui/packages/tgui/styles/main.scss | 2 - 15 files changed, 160 insertions(+), 266 deletions(-) delete mode 100644 code/modules/client/preferences/tgui_prefs_migration.dm diff --git a/beestation.dme b/beestation.dme index a865c2fec21e7..01fec651930cb 100644 --- a/beestation.dme +++ b/beestation.dme @@ -106,7 +106,6 @@ #include "code\__DEFINES\MC.dm" #include "code\__DEFINES\mecha.dm" #include "code\__DEFINES\melee.dm" -#include "code\__DEFINES\menu.dm" #include "code\__DEFINES\metacoin.dm" #include "code\__DEFINES\mobs.dm" #include "code\__DEFINES\monkeys.dm" @@ -222,6 +221,7 @@ #include "code\__HELPERS\_lists.dm" #include "code\__HELPERS\_logging.dm" #include "code\__HELPERS\_string_lists.dm" +#include "code\__HELPERS\admin.dm" #include "code\__HELPERS\areas.dm" #include "code\__HELPERS\atoms.dm" #include "code\__HELPERS\bitflag_list.dm" @@ -386,6 +386,7 @@ #include "code\controllers\subsystem\dcs.dm" #include "code\controllers\subsystem\delay_component.dm" #include "code\controllers\subsystem\disease.dm" +#include "code\controllers\subsystem\early_assets.dm" #include "code\controllers\subsystem\economy.dm" #include "code\controllers\subsystem\enumeration.dm" #include "code\controllers\subsystem\events.dm" @@ -2124,7 +2125,7 @@ #include "code\modules\client\message.dm" #include "code\modules\client\player_details.dm" #include "code\modules\client\preferences.dm" -#include "code\modules\client\preferences_toggles.dm" +#include "code\modules\client\preferences_menu.dm" #include "code\modules\client\loadout\loadout.dm" #include "code\modules\client\loadout\loadout_accessories.dm" #include "code\modules\client\loadout\loadout_colorizers.dm" @@ -2135,9 +2136,63 @@ #include "code\modules\client\loadout\loadout_ooc.dm" #include "code\modules\client\loadout\loadout_suit.dm" #include "code\modules\client\loadout\loadout_uniform.dm" +#include "code\modules\client\preferences\_preference.dm" +#include "code\modules\client\preferences\admin.dm" +#include "code\modules\client\preferences\age.dm" +#include "code\modules\client\preferences\ai_core_display.dm" +#include "code\modules\client\preferences\ambient_occlusion.dm" +#include "code\modules\client\preferences\assets.dm" +#include "code\modules\client\preferences\auto_fit_viewport.dm" +#include "code\modules\client\preferences\body_type.dm" +#include "code\modules\client\preferences\broadcast_login_logout.dm" +#include "code\modules\client\preferences\buttons_locked.dm" +#include "code\modules\client\preferences\clothing.dm" +#include "code\modules\client\preferences\darkened_flash.dm" +#include "code\modules\client\preferences\fps.dm" +#include "code\modules\client\preferences\gender.dm" +#include "code\modules\client\preferences\ghost.dm" +#include "code\modules\client\preferences\hotkeys.dm" +#include "code\modules\client\preferences\item_outlines.dm" +#include "code\modules\client\preferences\jobless_role.dm" +#include "code\modules\client\preferences\names.dm" +#include "code\modules\client\preferences\ooc.dm" +#include "code\modules\client\preferences\parallax.dm" +#include "code\modules\client\preferences\pda.dm" +#include "code\modules\client\preferences\phobia.dm" +#include "code\modules\client\preferences\pixel_size.dm" +#include "code\modules\client\preferences\playtime_reward_cloak.dm" +#include "code\modules\client\preferences\preferred_map.dm" +#include "code\modules\client\preferences\random.dm" +#include "code\modules\client\preferences\runechat.dm" +#include "code\modules\client\preferences\scaling_method.dm" +#include "code\modules\client\preferences\screentips.dm" +#include "code\modules\client\preferences\security_department.dm" +#include "code\modules\client\preferences\skin_tone.dm" +#include "code\modules\client\preferences\species.dm" +#include "code\modules\client\preferences\tgui.dm" +#include "code\modules\client\preferences\tooltips.dm" +#include "code\modules\client\preferences\ui_style.dm" +#include "code\modules\client\preferences\underwear_color.dm" +#include "code\modules\client\preferences\uplink_location.dm" +#include "code\modules\client\preferences\widescreen.dm" +#include "code\modules\client\preferences\window_flashing.dm" +#include "code\modules\client\preferences\middleware\_middleware.dm" +#include "code\modules\client\preferences\middleware\antags.dm" +#include "code\modules\client\preferences\middleware\jobs.dm" +#include "code\modules\client\preferences\middleware\keybindings.dm" +#include "code\modules\client\preferences\middleware\legacy_toggles.dm" +#include "code\modules\client\preferences\middleware\names.dm" +#include "code\modules\client\preferences\middleware\quirks.dm" +#include "code\modules\client\preferences\middleware\random.dm" +#include "code\modules\client\preferences\middleware\species.dm" +#include "code\modules\client\preferences\species_features\basic.dm" +#include "code\modules\client\preferences\species_features\ethereal.dm" +#include "code\modules\client\preferences\species_features\felinid.dm" +#include "code\modules\client\preferences\species_features\lizard.dm" +#include "code\modules\client\preferences\species_features\moth.dm" +#include "code\modules\client\preferences\species_features\mutants.dm" #include "code\modules\client\preferences2\character_save.dm" #include "code\modules\client\preferences2\preferences2.dm" -#include "code\modules\client\verbs\etips.dm" #include "code\modules\client\verbs\input_box.dm" #include "code\modules\client\verbs\looc.dm" #include "code\modules\client\verbs\ooc.dm" diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index 452b862d567e3..62d8f6a5de379 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -37,6 +37,7 @@ #define COLOR_YELLOW "#FFFF00" #define COLOR_OLIVE "#808000" #define COLOR_LIME "#32CD32" +#define COLOR_VIBRANT_LIME "#00FF00" #define COLOR_GREEN "#008000" #define COLOR_CYAN "#00FFFF" #define COLOR_TEAL "#008080" @@ -58,7 +59,7 @@ #define COLOR_RED_GRAY "#B4696A" #define COLOR_PALE_BLUE_GRAY "#98C5DF" #define COLOR_PALE_GREEN_GRAY "#B7D993" -#define COLOR_PALE_ORANGE "#FFBE9D" +#define COLOR_PALE_ORANGE "#FFBE9D" #define COLOR_PALE_RED_GRAY "#D59998" #define COLOR_PALE_PURPLE_GRAY "#CBB1CA" #define COLOR_PURPLE_GRAY "#AE8CA8" diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm index b4573ba75ecad..538d9b319c65e 100644 --- a/code/__DEFINES/preferences.dm +++ b/code/__DEFINES/preferences.dm @@ -231,3 +231,8 @@ /// These are preferences that are supplementary for main features, /// such as hair color being affixed to hair. #define PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES "supplemental_features" + +//randomised elements +#define RANDOM_ANTAG_ONLY 1 +#define RANDOM_DISABLED 2 +#define RANDOM_ENABLED 3 diff --git a/code/__HELPERS/sanitize_values.dm b/code/__HELPERS/sanitize_values.dm index 9c311beeed781..d099c9e7e1cad 100644 --- a/code/__HELPERS/sanitize_values.dm +++ b/code/__HELPERS/sanitize_values.dm @@ -87,16 +87,3 @@ return crunch + repeat_string(desired_format, "0") return crunch + . - -/// Return `color` as a formatted ooc valid hex color -/proc/sanitize_ooccolor(color) - if(length(color) != length_char(color)) - CRASH("Invalid characters in color '[color]'") - var/list/HSL = rgb2hsl(hex2num(copytext(color, 2, 4)), hex2num(copytext(color, 4, 6)), hex2num(copytext(color, 6, 8))) - HSL[3] = min(HSL[3],0.4) - var/list/RGB = hsl2rgb(arglist(HSL)) - return "#[num2hex(RGB[1],2)][num2hex(RGB[2],2)][num2hex(RGB[3],2)]" - -/// Makes sure the input color is text with a # at the start followed by 6 hexadecimal characters. Examples: "#ff1234", "#A38321", COLOR_GREEN_GRAY -/proc/sanitize_color(color) - return findtext(color, GLOB.is_color) ? color : GLOB.normal_ooc_colour diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index 0f7a4c14eac13..eaeab5f50d167 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -174,15 +174,8 @@ friend_talk(message) -<<<<<<< HEAD /mob/camera/imaginary_friend/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode) to_chat(src, compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode)) -======= -/mob/camera/imaginary_friend/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list()) - if (client?.prefs.read_preference(/datum/preference/toggle/enable_runechat) && (client.prefs.read_preference(/datum/preference/toggle/enable_runechat_non_mobs) || ismob(speaker))) - create_chat_message(speaker, message_language, raw_message, spans) - to_chat(src, compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mods)) ->>>>>>> 5a4c87a9fc3 (tgui Preferences Menu + total rewrite of the preferences backend (#61313)) /mob/camera/imaginary_friend/proc/friend_talk(message) message = treat_message_min(trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))) @@ -193,8 +186,8 @@ src.log_talk(message, LOG_SAY, tag="imaginary friend") // Display message - var/owner_chat_map = owner.client?.prefs.toggles & (PREFTOGGLE_RUNECHAT_GLOBAL | PREFTOGGLE_RUNECHAT_NONMOBS) - var/friend_chat_map = client?.prefs.toggles & (PREFTOGGLE_RUNECHAT_GLOBAL | PREFTOGGLE_RUNECHAT_NONMOBS) + var/owner_chat_map = owner.client?.prefs.read_preference(/datum/preference/toggle/enable_runechat) && owner.client.prefs.read_preference(/datum/preference/toggle/enable_runechat_non_mobs) + var/friend_chat_map = client?.prefs.read_preference(/datum/preference/toggle/enable_runechat) && client.prefs.read_preference(/datum/preference/toggle/enable_runechat_non_mobs) if (!owner_chat_map) var/mutable_appearance/MA = mutable_appearance('icons/mob/talk.dmi', src, "default[say_test(message)]", FLY_LAYER) MA.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 13c78d754f7ea..e84b19a512272 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -307,33 +307,8 @@ var/obj/item/uplink_loc var/implant = FALSE -<<<<<<< HEAD - if(traitor_mob.client?.prefs) - switch(traitor_mob.client.prefs.active_character.uplink_spawn_loc) - if(UPLINK_PDA) - uplink_loc = PDA - if(!uplink_loc) - uplink_loc = R - if(!uplink_loc) - uplink_loc = P - if(UPLINK_RADIO) - if(HAS_TRAIT(traitor_mob, TRAIT_MUTE)) // cant speak code into headset - to_chat(traitor_mob, "Using a radio uplink would be impossible with your muteness! Equipping PDA Uplink..") - uplink_loc = PDA - if(!uplink_loc) - uplink_loc = R - if(!uplink_loc) - uplink_loc = P - else - uplink_loc = R - if(!uplink_loc) - uplink_loc = PDA - if(!uplink_loc) - uplink_loc = P - if(UPLINK_PEN) -======= var/uplink_spawn_location = traitor_mob.client?.prefs?.read_preference(/datum/preference/choiced/uplink_location) - switch (uplink_spawn_location) + switch(uplink_spawn_location) if(UPLINK_PDA) uplink_loc = PDA if(!uplink_loc) @@ -341,12 +316,21 @@ if(!uplink_loc) uplink_loc = P if(UPLINK_RADIO) - uplink_loc = R - if(!uplink_loc) + if(HAS_TRAIT(traitor_mob, TRAIT_MUTE)) // cant speak code into headset + to_chat(traitor_mob, "Using a radio uplink would be impossible with your muteness! Equipping PDA Uplink..") uplink_loc = PDA - if(!uplink_loc) ->>>>>>> 5a4c87a9fc3 (tgui Preferences Menu + total rewrite of the preferences backend (#61313)) - uplink_loc = P + if(!uplink_loc) + uplink_loc = R + if(!uplink_loc) + uplink_loc = P + else + uplink_loc = R + if(!uplink_loc) + uplink_loc = PDA + if(!uplink_loc) + uplink_loc = P + if(UPLINK_PEN) + uplink_loc = P if(UPLINK_PEN) uplink_loc = P if(UPLINK_IMPLANT) diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm index 554118363ce62..5eb476291aeab 100644 --- a/code/modules/antagonists/changeling/changeling.dm +++ b/code/modules/antagonists/changeling/changeling.dm @@ -607,13 +607,6 @@ return parts.Join("
    ") -<<<<<<< HEAD -/datum/antagonist/changeling/antag_listing_name() - return ..() + "([changelingID])" - -/datum/antagonist/changeling/xenobio/antag_listing_name() - return ..() + "(Xenobio)" - /datum/antagonist/changeling/get_preview_icon() var/icon/final_icon = render_preview_outfit(/datum/outfit/changeling) var/icon/split_icon = render_preview_outfit(/datum/outfit/job/engineer) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 86f7957dbb4d5..0ed70fa544915 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -74,12 +74,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) /// A list of instantiated middleware var/list/datum/preference_middleware/middleware = list() - /// The savefile relating to core preferences, PREFERENCE_PLAYER - var/savefile/game_savefile - - /// The savefile relating to character preferences, PREFERENCE_CHARACTER - var/savefile/character_savefile - /// A list of keys that have been updated since the last save. var/list/recently_updated_keys = list() @@ -102,22 +96,22 @@ GLOBAL_LIST_EMPTY(preferences_datums) for (var/middleware_type in subtypesof(/datum/preference_middleware)) middleware += new middleware_type(src) + if(istype(C)) - if(!IsGuestKey(C.key)) - load_path(C.ckey) - unlock_content = !!C.IsByondMember() + if(!IS_GUEST_KEY(C.key)) + unlock_content = C.IsByondMember() if(unlock_content) - max_save_slots = 8 + set_max_character_slots(8) + else if(!length(key_bindings)) // Guests need default keybinds + key_bindings = deepCopyList(GLOB.keybinding_list_by_key) - // give them default keybinds and update their movement keys - key_bindings = deepCopyList(GLOB.default_hotkeys) - key_bindings_by_key = get_key_bindings_by_key(key_bindings) randomise = get_default_randomization() - var/loaded_preferences_successfully = load_preferences() + var/loaded_preferences_successfully = load_from_database() if(loaded_preferences_successfully) - if("6030fe461e610e2be3a2c3e75c06067e" in purchased_gear) //MD5 hash of, "extra character slot" - set_max_character_slots(max_usable_slots + 1) + // TODO tgui-prefs + /*if("6030fe461e610e2be3a2c3e75c06067e" in purchased_gear) //MD5 hash of, "extra character slot" + set_max_character_slots(max_usable_slots + 1)*/ if(load_characters()) // inside this proc is a disgusting SQL query var/datum/character_save/target_save = character_saves[default_slot] if(target_save && !target_save.slot_locked) @@ -127,13 +121,19 @@ GLOBAL_LIST_EMPTY(preferences_datums) return //we couldn't load character data so just randomize the character appearance + name - randomise_appearance_prefs() //let's create a random character then - rather than a fat, bald and naked man. - - C?.set_macros() - + active_character = character_saves[1] + var/fallback_default_species = CONFIG_GET(string/fallback_default_species) + if(!active_character.pref_species && fallback_default_species != "random") + var/datum/species/spath = GLOB.species_list[fallback_default_species || "human"] + active_character.pref_species = new spath + active_character.randomise() //let's create a random character then - rather than a fat, bald and naked man. + active_character.real_name = active_character.pref_species.random_name(active_character.gender, TRUE) if(!loaded_preferences_successfully) save_preferences() - save_character() //let's save this new random character so it doesn't keep generating new ones. + save_character(C) //let's save this new random character so it doesn't keep generating new ones. + +/datum/preferences/proc/save_character(client/C) + active_character.save(C) /datum/preferences/ui_interact(mob/user, datum/tgui/ui) // If you leave and come back, re-register the character preview @@ -215,14 +215,17 @@ GLOBAL_LIST_EMPTY(preferences_datums) switch (action) if ("change_slot") - // Save existing character - save_character() - - // SAFETY: `load_character` performs sanitization the slot number - if (!load_character(params["slot"])) - tainted_character_profiles = TRUE - randomise_appearance_prefs() - save_character() + var/numerical_slot = text2num(params["slot"]) + var/datum/character_save/CS = character_saves[numerical_slot] + if(!CS || CS.slot_locked) + return + active_character = CS + default_slot = numerical_slot + tainted_character_profiles = TRUE + // If its fresh, randomise & save it + if(!CS.from_db) + CS.randomise() + CS.save(user.client) for (var/datum/preference_middleware/preference_middleware as anything in middleware) preference_middleware.on_new_character(usr) @@ -294,7 +297,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) return FALSE /datum/preferences/ui_close(mob/user) - save_character() + save_character(user.client) save_preferences() QDEL_NULL(character_preview_view) @@ -428,17 +431,14 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/character_preview_view) /datum/preferences/proc/create_character_profiles() var/list/profiles = list() - var/savefile/savefile = new(path) - for (var/index in 1 to max_save_slots) + for(var/index in 1 to TRUE_MAX_SAVE_SLOTS) // It won't be updated in the savefile yet, so just read the name directly if (index == default_slot) profiles += read_preference(/datum/preference/name/real_name) continue - savefile.cd = "/character[index]" - - var/name - READ_FILE(savefile["real_name"], name) + // TODO tgui-prefs + var/name = "testing" if (isnull(name)) profiles += null @@ -505,8 +505,9 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/character_preview_view) /datum/preferences/proc/should_be_random_hardcore(datum/job/job, datum/mind/mind) if(!read_preference(/datum/preference/toggle/random_hardcore)) return FALSE - if(job.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND) //No command staff - return FALSE + // TODO tgui-prefs + //if(job.department_flag & DEPARTMENT_BITFLAG_COMMAND) //No command staff + // return FALSE for(var/datum/antagonist/antag as anything in mind.antag_datums) if(antag.get_team()) //No team antags return FALSE diff --git a/code/modules/client/preferences/clothing.dm b/code/modules/client/preferences/clothing.dm index 8dca08d090a37..88313c6fd8b9f 100644 --- a/code/modules/client/preferences/clothing.dm +++ b/code/modules/client/preferences/clothing.dm @@ -1,5 +1,5 @@ /proc/generate_values_for_underwear(list/accessory_list, list/icons, color) - var/icon/lower_half = icon('icons/blanks/32x32.dmi', "nothing") + var/icon/lower_half = icon('icons/effects/effects.dmi', "nothing") for (var/icon in icons) lower_half.Blend(icon('icons/mob/human_parts_greyscale.dmi', icon), ICON_OVERLAY) @@ -51,7 +51,7 @@ return values /datum/preference/choiced/backpack/apply_to_human(mob/living/carbon/human/target, value) - target.backpack = value + target.backbag = value /// Jumpsuit preference /datum/preference/choiced/jumpsuit diff --git a/code/modules/client/preferences/tgui_prefs_migration.dm b/code/modules/client/preferences/tgui_prefs_migration.dm deleted file mode 100644 index c6492dfa60ace..0000000000000 --- a/code/modules/client/preferences/tgui_prefs_migration.dm +++ /dev/null @@ -1,116 +0,0 @@ -/// Handle the migrations necessary from pre-tgui prefs to post-tgui prefs -/datum/preferences/proc/migrate_preferences_to_tgui_prefs_menu() - migrate_antagonists() - migrate_key_bindings() - -/// Handle the migrations necessary from pre-tgui prefs to post-tgui prefs, for characters -/datum/preferences/proc/migrate_character_to_tgui_prefs_menu() - migrate_randomization() - -// Key bindings used to be "key" -> list("action"), -// such as "X" -> list("swap_hands"). -// This made it impossible to determine any order, meaning placing a new -// hotkey would produce non-deterministic order. -// tgui prefs menu moves this over to "swap_hands" -> list("X"). -/datum/preferences/proc/migrate_key_bindings() - var/new_key_bindings = list() - - for (var/unbound_hotkey in key_bindings["Unbound"]) - new_key_bindings[unbound_hotkey] = list() - - for (var/hotkey in key_bindings) - if (hotkey == "Unbound") - continue - - for (var/keybind in key_bindings[hotkey]) - if (keybind in new_key_bindings) - new_key_bindings[keybind] |= hotkey - else - new_key_bindings[keybind] = list(hotkey) - - key_bindings = new_key_bindings - -// Before tgui preferences menu, "traitor" would handle both roundstart, midround, and latejoin. -// These were split apart. -/datum/preferences/proc/migrate_antagonists() - migrate_antagonist(ROLE_HERETIC, list(ROLE_HERETIC_SMUGGLER)) - migrate_antagonist(ROLE_MALF, list(ROLE_MALF_MIDROUND)) - migrate_antagonist(ROLE_OPERATIVE, list(ROLE_OPERATIVE_MIDROUND, ROLE_LONE_OPERATIVE)) - migrate_antagonist(ROLE_REV_HEAD, list(ROLE_PROVOCATEUR)) - migrate_antagonist(ROLE_TRAITOR, list(ROLE_SYNDICATE_INFILTRATOR, ROLE_SLEEPER_AGENT)) - migrate_antagonist(ROLE_WIZARD, list(ROLE_WIZARD_MIDROUND)) - - // "Familes [sic] Antagonists" was the old name of the catch-all. - migrate_antagonist("Familes Antagonists", list(ROLE_FAMILIES, ROLE_FAMILY_HEAD_ASPIRANT)) - -/datum/preferences/proc/migrate_antagonist(will_exist, list/to_add) - if (will_exist in be_special) - for (var/add in to_add) - be_special += add - -// Randomization used to be an assoc list of fields to TRUE. -// Antagonist randomization was not even available to all options. -// tgui prefs menu changes from list("random_socks" = TRUE, "random_name_antag" = TRUE) -// to list("socks" = "enabled", "name" = "antag") -// as well as removing anything that was set to FALSE, as this can be extrapolated. -/datum/preferences/proc/migrate_randomization() - var/static/list/random_settings = list( - "random_age" = "age", - "random_backpack" = "backpack", - "random_eye_color" = "eye_color", - "random_facial_hair_color" = "facial_hair_color", - "random_facial_hairstyle" = "facial_hairstyle", - "random_gender" = "gender", - "random_hair_color" = "hair_color", - "random_hairstyle" = "hairstyle", - "random_jumpsuit_style" = "jumpsuit_style", - "random_skin_tone" = "skin_tone", - "random_socks" = "socks", - "random_species" = "species", - "random_undershirt" = "undershirt", - "random_underwear" = "underwear", - "random_underwear_color" = "underwear_color", - ) - - var/static/list/random_antag_settings = list( - "random_age_antag" = "age", - "random_gender_antag" = "gender", - "random_name_antag" = "name", - ) - - var/list/new_randomise = list() - - for (var/old_setting in random_settings) - if (randomise[old_setting]) - new_randomise[random_settings[old_setting]] = RANDOM_ENABLED - - for (var/old_antag_setting in random_antag_settings) - if (randomise[old_antag_setting]) - new_randomise[random_settings[old_antag_setting]] = RANDOM_ANTAG_ONLY - - migrate_randomization_to_new_pref( - /datum/preference/choiced/random_body, - "random_body", - "random_body_antag", - ) - - migrate_randomization_to_new_pref( - /datum/preference/choiced/random_name, - "random_name", - "random_name_antag", - ) - - if (randomise["random_hardcore"]) - write_preference(GLOB.preference_entries[/datum/preference/toggle/random_hardcore], TRUE) - - randomise = new_randomise - -/datum/preferences/proc/migrate_randomization_to_new_pref( - preference_type, - key, - key_antag, -) - if (randomise[key_antag]) - write_preference(GLOB.preference_entries[preference_type], RANDOM_ANTAG_ONLY) - else if (randomise[key]) - write_preference(GLOB.preference_entries[preference_type], RANDOM_ENABLED) diff --git a/code/modules/client/preferences2/preferences2.dm b/code/modules/client/preferences2/preferences2.dm index 399bcb600eb4a..6e81466c78bae 100644 --- a/code/modules/client/preferences2/preferences2.dm +++ b/code/modules/client/preferences2/preferences2.dm @@ -61,59 +61,59 @@ READPREF_INT(chat_toggles, PREFERENCE_TAG_CHAT_TOGGLES) READPREF_INT(toggles, PREFERENCE_TAG_TOGGLES) READPREF_INT(toggles2, PREFERENCE_TAG_TOGGLES2) - READPREF_INT(clientfps, PREFERENCE_TAG_CLIENTFPS) + /*READPREF_INT(clientfps, PREFERENCE_TAG_CLIENTFPS) READPREF_INT(parallax, PREFERENCE_TAG_PARALLAX) READPREF_INT(pixel_size, PREFERENCE_TAG_PIXELSIZE) READPREF_INT(tip_delay, PREFERENCE_TAG_TIP_DELAY) - READPREF_RAW(asaycolor, PREFERENCE_TAG_ASAY_COLOUR) + READPREF_RAW(asaycolor, PREFERENCE_TAG_ASAY_COLOUR)*/ READPREF_RAW(ooccolor, PREFERENCE_TAG_OOC_COLOUR) READPREF_RAW(lastchangelog, PREFERENCE_TAG_LAST_CL) - READPREF_RAW(UI_style, PREFERENCE_TAG_UI_STYLE) + /*READPREF_RAW(UI_style, PREFERENCE_TAG_UI_STYLE) READPREF_RAW(outline_color, PREFERENCE_TAG_OUTLINE_COLOUR) READPREF_RAW(see_balloon_alerts, PREFERENCE_TAG_BALLOON_ALERTS) - READPREF_RAW(scaling_method, PREFERENCE_TAG_SCALING_METHOD) + READPREF_RAW(scaling_method, PREFERENCE_TAG_SCALING_METHOD)*/ READPREF_RAW(ghost_form, PREFERENCE_TAG_GHOST_FORM) - READPREF_RAW(ghost_orbit, PREFERENCE_TAG_GHOST_ORBIT) + /*READPREF_RAW(ghost_orbit, PREFERENCE_TAG_GHOST_ORBIT) READPREF_RAW(ghost_accs, PREFERENCE_TAG_GHOST_ACCS) READPREF_RAW(ghost_others, PREFERENCE_TAG_GHOST_OTHERS) READPREF_RAW(pda_theme, PREFERENCE_TAG_PDA_THEME) READPREF_RAW(pda_color, PREFERENCE_TAG_PDA_COLOUR) READPREF_RAW(pai_name, PREFERENCE_TAG_PAI_NAME) READPREF_RAW(pai_description, PREFERENCE_TAG_PAI_DESCRIPTION) - READPREF_RAW(pai_comment, PREFERENCE_TAG_PAI_COMMENT) + READPREF_RAW(pai_comment, PREFERENCE_TAG_PAI_COMMENT)*/ READPREF_JSONDEC(ignoring, PREFERENCE_TAG_IGNORING) READPREF_JSONDEC(key_bindings, PREFERENCE_TAG_KEYBINDS) - READPREF_JSONDEC(purchased_gear, PREFERENCE_TAG_PURCHASED_GEAR) + //READPREF_JSONDEC(purchased_gear, PREFERENCE_TAG_PURCHASED_GEAR) READPREF_JSONDEC(be_special, PREFERENCE_TAG_BE_SPECIAL) //Sanitize - asaycolor = sanitize_ooccolor(sanitize_hexcolor(asaycolor, 6, TRUE, initial(asaycolor))) - ooccolor = sanitize_ooccolor(sanitize_hexcolor(ooccolor, 6, TRUE, initial(ooccolor))) + //asaycolor = sanitize_ooccolor(sanitize_hexcolor(asaycolor, 6, TRUE, initial(asaycolor))) + //ooccolor = sanitize_ooccolor(sanitize_hexcolor(ooccolor, 6, TRUE, initial(ooccolor))) lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog)) - UI_style = sanitize_inlist(UI_style, GLOB.available_ui_styles, GLOB.available_ui_styles[1]) + //UI_style = sanitize_inlist(UI_style, GLOB.available_ui_styles, GLOB.available_ui_styles[1]) default_slot = sanitize_integer(default_slot, TRUE, TRUE_MAX_SAVE_SLOTS, initial(default_slot)) toggles = sanitize_integer(toggles, FALSE, INFINITY, initial(toggles)) // yes toggles2 = sanitize_integer(toggles2, FALSE, INFINITY, initial(toggles2)) - clientfps = sanitize_integer(clientfps, FALSE, 1000, FALSE) - parallax = sanitize_integer(parallax, PARALLAX_INSANE, PARALLAX_DISABLE, null) + //clientfps = sanitize_integer(clientfps, FALSE, 1000, FALSE) + //parallax = sanitize_integer(parallax, PARALLAX_INSANE, PARALLAX_DISABLE, null) - pixel_size = sanitize_float(pixel_size, PIXEL_SCALING_AUTO, PIXEL_SCALING_3X, 0.5, initial(pixel_size)) - scaling_method = sanitize_text(scaling_method, initial(scaling_method)) + //pixel_size = sanitize_float(pixel_size, PIXEL_SCALING_AUTO, PIXEL_SCALING_3X, 0.5, initial(pixel_size)) + //scaling_method = sanitize_text(scaling_method, initial(scaling_method)) ghost_form = sanitize_inlist(ghost_form, GLOB.ghost_forms, initial(ghost_form)) - ghost_orbit = sanitize_inlist(ghost_orbit, GLOB.ghost_orbits, initial(ghost_orbit)) - ghost_accs = sanitize_inlist(ghost_accs, GLOB.ghost_accs_options, GHOST_ACCS_DEFAULT_OPTION) - ghost_others = sanitize_inlist(ghost_others, GLOB.ghost_others_options, GHOST_OTHERS_DEFAULT_OPTION) + //ghost_orbit = sanitize_inlist(ghost_orbit, GLOB.ghost_orbits, initial(ghost_orbit)) + //ghost_accs = sanitize_inlist(ghost_accs, GLOB.ghost_accs_options, GHOST_ACCS_DEFAULT_OPTION) + //ghost_others = sanitize_inlist(ghost_others, GLOB.ghost_others_options, GHOST_OTHERS_DEFAULT_OPTION) be_special = SANITIZE_LIST(be_special) - pda_theme = sanitize_inlist(pda_theme, GLOB.ntos_device_themes_default_content, initial(pda_theme)) - pda_color = sanitize_hexcolor(pda_color, 6, TRUE, initial(pda_color)) + //pda_theme = sanitize_inlist(pda_theme, GLOB.ntos_device_themes_default_content, initial(pda_theme)) + //pda_color = sanitize_hexcolor(pda_color, 6, TRUE, initial(pda_color)) - pai_name = sanitize_text(pai_name, initial(pai_name)) - pai_description = sanitize_text(pai_description, initial(pai_description)) - pai_comment = sanitize_text(pai_comment, initial(pai_comment)) + //pai_name = sanitize_text(pai_name, initial(pai_name)) + //pai_description = sanitize_text(pai_description, initial(pai_description)) + //pai_comment = sanitize_text(pai_comment, initial(pai_comment)) key_bindings = sanitize_islist(key_bindings, deep_copy_list(GLOB.keybinding_list_by_key)) if (!length(key_bindings)) @@ -137,8 +137,8 @@ if(any_changed) save_keybinds() - if(!purchased_gear) - purchased_gear = list() + //if(!purchased_gear) + // purchased_gear = list() return TRUE @@ -170,31 +170,31 @@ PREP_WRITEPREF_RAW(chat_toggles, PREFERENCE_TAG_CHAT_TOGGLES) PREP_WRITEPREF_RAW(toggles, PREFERENCE_TAG_TOGGLES) PREP_WRITEPREF_RAW(toggles2, PREFERENCE_TAG_TOGGLES2) - PREP_WRITEPREF_RAW(clientfps, PREFERENCE_TAG_CLIENTFPS) + /*PREP_WRITEPREF_RAW(clientfps, PREFERENCE_TAG_CLIENTFPS) PREP_WRITEPREF_RAW(parallax, PREFERENCE_TAG_PARALLAX) PREP_WRITEPREF_RAW(pixel_size, PREFERENCE_TAG_PIXELSIZE) PREP_WRITEPREF_RAW(tip_delay, PREFERENCE_TAG_TIP_DELAY) PREP_WRITEPREF_RAW(pda_theme, PREFERENCE_TAG_PDA_THEME) - PREP_WRITEPREF_RAW(pda_color, PREFERENCE_TAG_PDA_COLOUR) + PREP_WRITEPREF_RAW(pda_color, PREFERENCE_TAG_PDA_COLOUR)*/ - PREP_WRITEPREF_RAW(asaycolor, PREFERENCE_TAG_ASAY_COLOUR) - PREP_WRITEPREF_RAW(ooccolor, PREFERENCE_TAG_OOC_COLOUR) + //PREP_WRITEPREF_RAW(asaycolor, PREFERENCE_TAG_ASAY_COLOUR) + //PREP_WRITEPREF_RAW(ooccolor, PREFERENCE_TAG_OOC_COLOUR) PREP_WRITEPREF_RAW(lastchangelog, PREFERENCE_TAG_LAST_CL) - PREP_WRITEPREF_RAW(UI_style, PREFERENCE_TAG_UI_STYLE) + /*PREP_WRITEPREF_RAW(UI_style, PREFERENCE_TAG_UI_STYLE) PREP_WRITEPREF_RAW(outline_color, PREFERENCE_TAG_OUTLINE_COLOUR) PREP_WRITEPREF_RAW(see_balloon_alerts, PREFERENCE_TAG_BALLOON_ALERTS) - PREP_WRITEPREF_RAW(scaling_method, PREFERENCE_TAG_SCALING_METHOD) + PREP_WRITEPREF_RAW(scaling_method, PREFERENCE_TAG_SCALING_METHOD)*/ PREP_WRITEPREF_RAW(ghost_form, PREFERENCE_TAG_GHOST_FORM) - PREP_WRITEPREF_RAW(ghost_orbit, PREFERENCE_TAG_GHOST_ORBIT) + /*PREP_WRITEPREF_RAW(ghost_orbit, PREFERENCE_TAG_GHOST_ORBIT) PREP_WRITEPREF_RAW(ghost_accs, PREFERENCE_TAG_GHOST_ACCS) PREP_WRITEPREF_RAW(ghost_others, PREFERENCE_TAG_GHOST_OTHERS) PREP_WRITEPREF_RAW(pai_name, PREFERENCE_TAG_PAI_NAME) PREP_WRITEPREF_RAW(pai_description, PREFERENCE_TAG_PAI_DESCRIPTION) - PREP_WRITEPREF_RAW(pai_comment, PREFERENCE_TAG_PAI_COMMENT) + PREP_WRITEPREF_RAW(pai_comment, PREFERENCE_TAG_PAI_COMMENT)*/ PREP_WRITEPREF_JSONENC(ignoring, PREFERENCE_TAG_IGNORING) PREP_WRITEPREF_JSONENC(key_bindings, PREFERENCE_TAG_KEYBINDS) - PREP_WRITEPREF_JSONENC(purchased_gear, PREFERENCE_TAG_PURCHASED_GEAR) + //PREP_WRITEPREF_JSONENC(purchased_gear, PREFERENCE_TAG_PURCHASED_GEAR) PREP_WRITEPREF_JSONENC(be_special, PREFERENCE_TAG_BE_SPECIAL) // QuerySelect can execute many queries at once. That name is dumb but w/e diff --git a/code/modules/mob/living/silicon/ai/ai_portrait_picker.dm b/code/modules/mob/living/silicon/ai/ai_portrait_picker.dm index c880e11c70b56..45ddb4250395d 100644 --- a/code/modules/mob/living/silicon/ai/ai_portrait_picker.dm +++ b/code/modules/mob/living/silicon/ai/ai_portrait_picker.dm @@ -37,9 +37,9 @@ /datum/portrait_picker/ui_data(mob/user) var/list/data = list() - data["library"] = SSpersistent_paintings.paintings["library"] ? SSpersistent_paintings.paintings["library"] : 0 - data["library_secure"] = SSpersistent_paintings.paintings["library_secure"] ? SSpersistent_paintings.paintings["library_secure"] : 0 - data["library_private"] = SSpersistent_paintings.paintings["library_private"] ? SSpersistent_paintings.paintings["library_private"] : 0 //i'm gonna regret this, won't i? + data["library"] = SSpersistence.paintings["library"] ? SSpersistence.paintings["library"] : 0 + data["library_secure"] = SSpersistence.paintings["library_secure"] ? SSpersistence.paintings["library_secure"] : 0 + data["library_private"] = SSpersistence.paintings["library_private"] ? SSpersistence.paintings["library_private"] : 0 //i'm gonna regret this, won't i? return data /datum/portrait_picker/ui_act(action, params) @@ -50,7 +50,7 @@ if("select") var/list/tab2key = list(TAB_LIBRARY = "library", TAB_SECURE = "library_secure", TAB_PRIVATE = "library_private") var/folder = tab2key[params["tab"]] - var/list/current_list = SSpersistent_paintings.paintings[folder] + var/list/current_list = SSpersistence.paintings[folder] var/list/chosen_portrait = current_list[params["selected"]] var/png = "data/paintings/[folder]/[chosen_portrait["md5"]].png" var/icon/portrait_icon = new(png) diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 5ef619e8e0f30..0df18d712c649 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -72,15 +72,11 @@ if(client.view_size) client.view_size.resetToDefault(getScreenSize(src)) // Sets the defaul view_size because it can be different to what it was on the lobby. else -<<<<<<< HEAD client.change_view(getScreenSize(src)) // Resets the client.view in case it was changed. //Reset verb information, give verbs accessible to the mob. if(client.tgui_panel) client.tgui_panel.set_verb_infomation(client) -======= - client.change_view(getScreenSize(client.prefs.read_preference(/datum/preference/toggle/widescreen))) ->>>>>>> 5a4c87a9fc3 (tgui Preferences Menu + total rewrite of the preferences backend (#61313)) if(client.player_details.player_actions.len) for(var/datum/action/A in client.player_details.player_actions) diff --git a/config/game_options.txt b/config/game_options.txt index c32553546274a..e5d1a42a8e58b 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -530,12 +530,9 @@ ROUNDSTART_RACES plasmaman ##------------------------------------------------------------------------------------------- -<<<<<<< HEAD ## Uncomment to give players the choice of joining as a human with mutant bodyparts before they join the game JOIN_WITH_MUTANT_HUMANS -======= ->>>>>>> 5a4c87a9fc3 (tgui Preferences Menu + total rewrite of the preferences backend (#61313)) ##Overflow job. Default is assistant OVERFLOW_JOB Assistant diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss index 6dd8aafe7ed71..3587707587682 100644 --- a/tgui/packages/tgui/styles/main.scss +++ b/tgui/packages/tgui/styles/main.scss @@ -15,7 +15,6 @@ @include meta.load-css('./atomic/color.scss'); @include meta.load-css('./atomic/debug-layout.scss'); @include meta.load-css('./atomic/fit-text.scss'); -@include meta.load-css('./atomic/links.scss'); @include meta.load-css('./atomic/outline.scss'); @include meta.load-css('./atomic/text.scss'); @@ -50,7 +49,6 @@ @include meta.load-css('./interfaces/NuclearBomb.scss'); @include meta.load-css('./interfaces/ModularFabricator.scss'); @include meta.load-css('./interfaces/OrbitalMap.scss'); -@include meta.load-css('./interfaces/Paper.scss'); @include meta.load-css('./interfaces/PreferencesMenu.scss'); @include meta.load-css('./interfaces/Roulette.scss'); @include meta.load-css('./interfaces/IntegratedCircuit.scss'); From 605f22c876d3bc2e4eb4b5707c658484e303e876 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Thu, 30 Mar 2023 03:14:19 -0500 Subject: [PATCH 020/269] Rename savefile_key to db_key --- code/modules/client/preferences.dm | 2 +- code/modules/client/preferences/_preference.dm | 12 ++++++------ code/modules/client/preferences/admin.dm | 4 ++-- code/modules/client/preferences/age.dm | 2 +- .../client/preferences/ai_core_display.dm | 2 +- .../client/preferences/ambient_occlusion.dm | 2 +- code/modules/client/preferences/assets.dm | 4 ++-- .../client/preferences/auto_fit_viewport.dm | 2 +- code/modules/client/preferences/body_type.dm | 2 +- .../preferences/broadcast_login_logout.dm | 2 +- .../client/preferences/buttons_locked.dm | 2 +- code/modules/client/preferences/clothing.dm | 10 +++++----- .../client/preferences/darkened_flash.dm | 2 +- code/modules/client/preferences/fps.dm | 2 +- code/modules/client/preferences/gender.dm | 2 +- code/modules/client/preferences/ghost.dm | 12 ++++++------ code/modules/client/preferences/hotkeys.dm | 2 +- .../client/preferences/item_outlines.dm | 2 +- .../modules/client/preferences/jobless_role.dm | 2 +- .../client/preferences/middleware/names.dm | 4 ++-- .../client/preferences/middleware/random.dm | 4 ++-- code/modules/client/preferences/names.dm | 18 +++++++++--------- code/modules/client/preferences/ooc.dm | 2 +- code/modules/client/preferences/parallax.dm | 2 +- code/modules/client/preferences/pda.dm | 4 ++-- code/modules/client/preferences/phobia.dm | 2 +- code/modules/client/preferences/pixel_size.dm | 2 +- .../preferences/playtime_reward_cloak.dm | 2 +- .../client/preferences/preferred_map.dm | 2 +- code/modules/client/preferences/random.dm | 6 +++--- code/modules/client/preferences/runechat.dm | 8 ++++---- .../client/preferences/scaling_method.dm | 2 +- code/modules/client/preferences/screentips.dm | 4 ++-- .../client/preferences/security_department.dm | 2 +- code/modules/client/preferences/skin_tone.dm | 2 +- code/modules/client/preferences/species.dm | 2 +- .../preferences/species_features/basic.dm | 10 +++++----- .../preferences/species_features/ethereal.dm | 2 +- .../preferences/species_features/felinid.dm | 4 ++-- .../preferences/species_features/lizard.dm | 14 +++++++------- .../preferences/species_features/moth.dm | 6 +++--- .../preferences/species_features/mutants.dm | 2 +- code/modules/client/preferences/tgui.dm | 4 ++-- code/modules/client/preferences/tooltips.dm | 4 ++-- code/modules/client/preferences/ui_style.dm | 2 +- .../client/preferences/underwear_color.dm | 2 +- .../client/preferences/uplink_location.dm | 2 +- code/modules/client/preferences/widescreen.dm | 2 +- .../client/preferences/window_flashing.dm | 2 +- .../organs/external/_external_organs.dm | 2 +- code/modules/unit_tests/preferences.dm | 16 ++++++++-------- 51 files changed, 106 insertions(+), 106 deletions(-) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 0ed70fa544915..20704d8669e18 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -331,7 +331,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/value = read_preference(preference.type) var/data = preference.compile_ui_data(user, value) - preferences[preference.category][preference.savefile_key] = data + preferences[preference.category][preference.db_key] = data for (var/datum/preference_middleware/preference_middleware as anything in middleware) var/list/append_character_preferences = preference_middleware.get_character_preferences(user) diff --git a/code/modules/client/preferences/_preference.dm b/code/modules/client/preferences/_preference.dm index 0f5de07a7dff3..94464df1b7149 100644 --- a/code/modules/client/preferences/_preference.dm +++ b/code/modules/client/preferences/_preference.dm @@ -28,7 +28,7 @@ /// An assoc list list of types to instantiated `/datum/preference` instances GLOBAL_LIST_INIT(preference_entries, init_preference_entries()) -/// An assoc list of preference entries by their `savefile_key` +/// An assoc list of preference entries by their `db_key` GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) /proc/init_preference_entries() @@ -44,7 +44,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) for (var/datum/preference/preference_type as anything in subtypesof(/datum/preference)) if (initial(preference_type.abstract_type) == preference_type) continue - output[initial(preference_type.savefile_key)] = GLOB.preference_entries[preference_type] + output[initial(preference_type.db_key)] = GLOB.preference_entries[preference_type] return output /// Returns a flat list of preferences in order of their priority @@ -65,7 +65,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) /// The key inside the savefile to use. /// This is also sent to the UI. /// Once you pick this, don't change it. - var/savefile_key + var/db_key /// The category of preference, for use by the PreferencesMenu. /// This isn't used for anything other than as a key for UI data. @@ -155,7 +155,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) var/value if (!isnull(savefile)) - READ_FILE(savefile[savefile_key], value) + READ_FILE(savefile[db_key], value) if (isnull(value)) return null @@ -172,7 +172,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) return FALSE if (!isnull(savefile)) - WRITE_FILE(savefile[savefile_key], serialize(value)) + WRITE_FILE(savefile[db_key], serialize(value)) return TRUE @@ -306,7 +306,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) var/species_type = preferences.read_preference(/datum/preference/choiced/species) var/datum/species/species = new species_type - if (!(savefile_key in species.get_features())) + if (!(db_key in species.get_features())) return FALSE if (!should_show_on_page(preferences.current_window)) diff --git a/code/modules/client/preferences/admin.dm b/code/modules/client/preferences/admin.dm index 9cc1026d473c7..8bbb20f079f0f 100644 --- a/code/modules/client/preferences/admin.dm +++ b/code/modules/client/preferences/admin.dm @@ -1,6 +1,6 @@ /datum/preference/color/asay_color category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "asaycolor" + db_key = "asaycolor" savefile_identifier = PREFERENCE_PLAYER /datum/preference/color/asay_color/create_default_value() @@ -15,7 +15,7 @@ /// What outfit to equip when spawning as a briefing officer for an ERT /datum/preference/choiced/brief_outfit category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "brief_outfit" + db_key = "brief_outfit" savefile_identifier = PREFERENCE_PLAYER /datum/preference/choiced/brief_outfit/deserialize(input, datum/preferences/preferences) diff --git a/code/modules/client/preferences/age.dm b/code/modules/client/preferences/age.dm index cad9786ce1fef..4b2abb37f3f67 100644 --- a/code/modules/client/preferences/age.dm +++ b/code/modules/client/preferences/age.dm @@ -1,6 +1,6 @@ /datum/preference/numeric/age category = PREFERENCE_CATEGORY_NON_CONTEXTUAL - savefile_key = "age" + db_key = "age" savefile_identifier = PREFERENCE_CHARACTER minimum = AGE_MIN diff --git a/code/modules/client/preferences/ai_core_display.dm b/code/modules/client/preferences/ai_core_display.dm index dab61f2b323f3..3c6ed19b74461 100644 --- a/code/modules/client/preferences/ai_core_display.dm +++ b/code/modules/client/preferences/ai_core_display.dm @@ -2,7 +2,7 @@ /datum/preference/choiced/ai_core_display category = PREFERENCE_CATEGORY_NON_CONTEXTUAL savefile_identifier = PREFERENCE_CHARACTER - savefile_key = "preferred_ai_core_display" + db_key = "preferred_ai_core_display" should_generate_icons = TRUE /datum/preference/choiced/ai_core_display/init_possible_values() diff --git a/code/modules/client/preferences/ambient_occlusion.dm b/code/modules/client/preferences/ambient_occlusion.dm index a81efca00bd8f..94182cc6bb99a 100644 --- a/code/modules/client/preferences/ambient_occlusion.dm +++ b/code/modules/client/preferences/ambient_occlusion.dm @@ -1,7 +1,7 @@ /// Whether or not to toggle ambient occlusion, the shadows around people /datum/preference/toggle/ambient_occlusion category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "ambientocclusion" + db_key = "ambientocclusion" savefile_identifier = PREFERENCE_PLAYER /datum/preference/toggle/ambient_occlusion/apply_to_client(client/client, value) diff --git a/code/modules/client/preferences/assets.dm b/code/modules/client/preferences/assets.dm index de29b6e4fbdeb..4838490f7ae0f 100644 --- a/code/modules/client/preferences/assets.dm +++ b/code/modules/client/preferences/assets.dm @@ -39,7 +39,7 @@ /// Returns the key that will be used in the spritesheet for a given value. /datum/preference/proc/get_spritesheet_key(value) - return "[savefile_key]___[sanitize_css_class_name(value)]" + return "[db_key]___[sanitize_css_class_name(value)]" /// Sends information needed for shared details on individual preferences /datum/asset/json/preferences @@ -60,6 +60,6 @@ var/datum/preference/preference_entry = GLOB.preference_entries[preference_type] var/data = preference_entry.compile_constant_data() if (!isnull(data)) - preference_data[preference_entry.savefile_key] = data + preference_data[preference_entry.db_key] = data return preference_data diff --git a/code/modules/client/preferences/auto_fit_viewport.dm b/code/modules/client/preferences/auto_fit_viewport.dm index 3550af054549a..3606bd5761814 100644 --- a/code/modules/client/preferences/auto_fit_viewport.dm +++ b/code/modules/client/preferences/auto_fit_viewport.dm @@ -1,6 +1,6 @@ /datum/preference/toggle/auto_fit_viewport category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "auto_fit_viewport" + db_key = "auto_fit_viewport" savefile_identifier = PREFERENCE_PLAYER /datum/preference/toggle/auto_fit_viewport/apply_to_client_updated(client/client, value) diff --git a/code/modules/client/preferences/body_type.dm b/code/modules/client/preferences/body_type.dm index 81c6b3e9baa73..cde2aa7255b08 100644 --- a/code/modules/client/preferences/body_type.dm +++ b/code/modules/client/preferences/body_type.dm @@ -1,7 +1,7 @@ /datum/preference/choiced/body_type category = PREFERENCE_CATEGORY_NON_CONTEXTUAL priority = PREFERENCE_PRIORITY_BODY_TYPE - savefile_key = "body_type" + db_key = "body_type" savefile_identifier = PREFERENCE_CHARACTER /datum/preference/choiced/body_type/init_possible_values() diff --git a/code/modules/client/preferences/broadcast_login_logout.dm b/code/modules/client/preferences/broadcast_login_logout.dm index 90250118d2a0e..d87d524ae6044 100644 --- a/code/modules/client/preferences/broadcast_login_logout.dm +++ b/code/modules/client/preferences/broadcast_login_logout.dm @@ -1,5 +1,5 @@ /// Whether or not to announce when the player logs in or out. /datum/preference/toggle/broadcast_login_logout category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "broadcast_login_logout" + db_key = "broadcast_login_logout" savefile_identifier = PREFERENCE_PLAYER diff --git a/code/modules/client/preferences/buttons_locked.dm b/code/modules/client/preferences/buttons_locked.dm index b4f54ff10f8f9..0c60a73f867ca 100644 --- a/code/modules/client/preferences/buttons_locked.dm +++ b/code/modules/client/preferences/buttons_locked.dm @@ -1,5 +1,5 @@ /datum/preference/toggle/buttons_locked category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "buttons_locked" + db_key = "buttons_locked" savefile_identifier = PREFERENCE_PLAYER default_value = FALSE diff --git a/code/modules/client/preferences/clothing.dm b/code/modules/client/preferences/clothing.dm index 88313c6fd8b9f..74f601152efff 100644 --- a/code/modules/client/preferences/clothing.dm +++ b/code/modules/client/preferences/clothing.dm @@ -26,7 +26,7 @@ /// Backpack preference /datum/preference/choiced/backpack - savefile_key = "backpack" + db_key = "backpack" savefile_identifier = PREFERENCE_CHARACTER main_feature_name = "Backpack" category = PREFERENCE_CATEGORY_CLOTHING @@ -55,7 +55,7 @@ /// Jumpsuit preference /datum/preference/choiced/jumpsuit - savefile_key = "jumpsuit_style" + db_key = "jumpsuit_style" savefile_identifier = PREFERENCE_CHARACTER main_feature_name = "Jumpsuit" category = PREFERENCE_CATEGORY_CLOTHING @@ -74,7 +74,7 @@ /// Socks preference /datum/preference/choiced/socks - savefile_key = "socks" + db_key = "socks" savefile_identifier = PREFERENCE_CHARACTER main_feature_name = "Socks" category = PREFERENCE_CATEGORY_CLOTHING @@ -88,7 +88,7 @@ /// Undershirt preference /datum/preference/choiced/undershirt - savefile_key = "undershirt" + db_key = "undershirt" savefile_identifier = PREFERENCE_CHARACTER main_feature_name = "Undershirt" category = PREFERENCE_CATEGORY_CLOTHING @@ -123,7 +123,7 @@ /// Underwear preference /datum/preference/choiced/underwear - savefile_key = "underwear" + db_key = "underwear" savefile_identifier = PREFERENCE_CHARACTER main_feature_name = "Underwear" category = PREFERENCE_CATEGORY_CLOTHING diff --git a/code/modules/client/preferences/darkened_flash.dm b/code/modules/client/preferences/darkened_flash.dm index ef89467bf357a..9dd67d3225cf8 100644 --- a/code/modules/client/preferences/darkened_flash.dm +++ b/code/modules/client/preferences/darkened_flash.dm @@ -2,5 +2,5 @@ /datum/preference/toggle/darkened_flash category = PREFERENCE_CATEGORY_GAME_PREFERENCES default_value = FALSE - savefile_key = "darkened_flash" + db_key = "darkened_flash" savefile_identifier = PREFERENCE_PLAYER diff --git a/code/modules/client/preferences/fps.dm b/code/modules/client/preferences/fps.dm index 8bd1310932be7..e7760536753af 100644 --- a/code/modules/client/preferences/fps.dm +++ b/code/modules/client/preferences/fps.dm @@ -1,6 +1,6 @@ /datum/preference/numeric/fps category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "clientfps" + db_key = "clientfps" savefile_identifier = PREFERENCE_PLAYER minimum = -1 diff --git a/code/modules/client/preferences/gender.dm b/code/modules/client/preferences/gender.dm index a9400465b6708..9992ff0e7786c 100644 --- a/code/modules/client/preferences/gender.dm +++ b/code/modules/client/preferences/gender.dm @@ -1,7 +1,7 @@ /// Gender preference /datum/preference/choiced/gender savefile_identifier = PREFERENCE_CHARACTER - savefile_key = "gender" + db_key = "gender" priority = PREFERENCE_PRIORITY_GENDER /datum/preference/choiced/gender/init_possible_values() diff --git a/code/modules/client/preferences/ghost.dm b/code/modules/client/preferences/ghost.dm index ce8e05235ee4c..668651db94606 100644 --- a/code/modules/client/preferences/ghost.dm +++ b/code/modules/client/preferences/ghost.dm @@ -1,6 +1,6 @@ /// Determines what accessories your ghost will look like they have. /datum/preference/choiced/ghost_accessories - savefile_key = "ghost_accs" + db_key = "ghost_accs" savefile_identifier = PREFERENCE_PLAYER category = PREFERENCE_CATEGORY_GAME_PREFERENCES @@ -35,7 +35,7 @@ /// Determines the appearance of your ghost to others, when you are a BYOND member /datum/preference/choiced/ghost_form - savefile_key = "ghost_form" + db_key = "ghost_form" savefile_identifier = PREFERENCE_PLAYER category = PREFERENCE_CATEGORY_GAME_PREFERENCES should_generate_icons = TRUE @@ -100,7 +100,7 @@ /// Toggles the HUD for ghosts /datum/preference/toggle/ghost_hud - savefile_key = "ghost_hud" + db_key = "ghost_hud" savefile_identifier = PREFERENCE_PLAYER category = PREFERENCE_CATEGORY_GAME_PREFERENCES @@ -110,7 +110,7 @@ /// Determines what ghosts orbiting look like to you. /datum/preference/choiced/ghost_orbit - savefile_key = "ghost_orbit" + db_key = "ghost_orbit" savefile_identifier = PREFERENCE_PLAYER category = PREFERENCE_CATEGORY_GAME_PREFERENCES @@ -135,7 +135,7 @@ /// Determines how to show other ghosts /datum/preference/choiced/ghost_others - savefile_key = "ghost_others" + db_key = "ghost_others" savefile_identifier = PREFERENCE_PLAYER category = PREFERENCE_CATEGORY_GAME_PREFERENCES @@ -173,6 +173,6 @@ /// Whether or not ghosts can examine things by clicking on them. /datum/preference/toggle/inquisitive_ghost - savefile_key = "inquisitive_ghost" + db_key = "inquisitive_ghost" savefile_identifier = PREFERENCE_PLAYER category = PREFERENCE_CATEGORY_GAME_PREFERENCES diff --git a/code/modules/client/preferences/hotkeys.dm b/code/modules/client/preferences/hotkeys.dm index b96b68286d60a..cc65cf2ac1b1c 100644 --- a/code/modules/client/preferences/hotkeys.dm +++ b/code/modules/client/preferences/hotkeys.dm @@ -1,6 +1,6 @@ /datum/preference/toggle/hotkeys category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "hotkeys" + db_key = "hotkeys" savefile_identifier = PREFERENCE_PLAYER /datum/preference/toggle/hotkeys/apply_to_client(client/client, value) diff --git a/code/modules/client/preferences/item_outlines.dm b/code/modules/client/preferences/item_outlines.dm index 74b8996617345..f9d4b6626bb99 100644 --- a/code/modules/client/preferences/item_outlines.dm +++ b/code/modules/client/preferences/item_outlines.dm @@ -1,4 +1,4 @@ /datum/preference/toggle/item_outlines category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "itemoutline_pref" + db_key = "itemoutline_pref" savefile_identifier = PREFERENCE_PLAYER diff --git a/code/modules/client/preferences/jobless_role.dm b/code/modules/client/preferences/jobless_role.dm index 8514451fb71dc..29c0219915bc6 100644 --- a/code/modules/client/preferences/jobless_role.dm +++ b/code/modules/client/preferences/jobless_role.dm @@ -1,5 +1,5 @@ /datum/preference/choiced/jobless_role - savefile_key = "joblessrole" + db_key = "joblessrole" savefile_identifier = PREFERENCE_PLAYER /datum/preference/choiced/jobless_role/create_default_value() diff --git a/code/modules/client/preferences/middleware/names.dm b/code/modules/client/preferences/middleware/names.dm index 34e97f0f727a9..9cf8c577c6f78 100644 --- a/code/modules/client/preferences/middleware/names.dm +++ b/code/modules/client/preferences/middleware/names.dm @@ -15,7 +15,7 @@ if (!istype(name_preference)) continue - types[name_preference.savefile_key] = list( + types[name_preference.db_key] = list( "can_randomize" = name_preference.is_randomizable(), "explanation" = name_preference.explanation, "group" = name_preference.group, @@ -44,7 +44,7 @@ continue if (istype(highest_priority_job, name_preference.relevant_job)) - return name_preference.savefile_key + return name_preference.db_key return "real_name" diff --git a/code/modules/client/preferences/middleware/random.dm b/code/modules/client/preferences/middleware/random.dm index 17bc259e2c253..716f4c6713a15 100644 --- a/code/modules/client/preferences/middleware/random.dm +++ b/code/modules/client/preferences/middleware/random.dm @@ -18,7 +18,7 @@ if (!preference.is_randomizable()) continue - randomizable += preference.savefile_key + randomizable += preference.db_key return list( "randomizable" = randomizable, @@ -60,7 +60,7 @@ if (!preference.is_randomizable()) return FALSE - var/requested_randomization = randomise[preference.savefile_key] + var/requested_randomization = randomise[preference.db_key] if (istype(preference, /datum/preference/name)) requested_randomization = read_preference(/datum/preference/choiced/random_name) diff --git a/code/modules/client/preferences/names.dm b/code/modules/client/preferences/names.dm index 7c6dbdd2165a1..fab26bec12320 100644 --- a/code/modules/client/preferences/names.dm +++ b/code/modules/client/preferences/names.dm @@ -37,7 +37,7 @@ explanation = "Name" // The `_` makes it first in ABC order. group = "_real_name" - savefile_key = "real_name" + db_key = "real_name" /datum/preference/name/real_name/apply_to_human(mob/living/carbon/human/target, value) target.real_name = value @@ -69,7 +69,7 @@ /datum/preference/name/backup_human explanation = "Backup human name" group = "backup_human" - savefile_key = "human_name" + db_key = "human_name" /datum/preference/name/backup_human/create_informed_default_value(datum/preferences/preferences) var/gender = preferences.read_preference(/datum/preference/choiced/gender) @@ -77,7 +77,7 @@ return random_unique_name(gender) /datum/preference/name/clown - savefile_key = "clown_name" + db_key = "clown_name" explanation = "Clown name" group = "fun" @@ -87,7 +87,7 @@ return pick(GLOB.clown_names) /datum/preference/name/mime - savefile_key = "mime_name" + db_key = "mime_name" explanation = "Mime name" group = "fun" @@ -97,7 +97,7 @@ return pick(GLOB.mime_names) /datum/preference/name/cyborg - savefile_key = "cyborg_name" + db_key = "cyborg_name" allow_numbers = TRUE can_randomize = FALSE @@ -110,7 +110,7 @@ return DEFAULT_CYBORG_NAME /datum/preference/name/ai - savefile_key = "ai_name" + db_key = "ai_name" allow_numbers = TRUE explanation = "AI name" @@ -121,7 +121,7 @@ return pick(GLOB.ai_names) /datum/preference/name/religion - savefile_key = "religion_name" + db_key = "religion_name" allow_numbers = TRUE @@ -132,7 +132,7 @@ return pick(GLOB.religion_names) /datum/preference/name/deity - savefile_key = "deity_name" + db_key = "deity_name" allow_numbers = TRUE can_randomize = FALSE @@ -144,7 +144,7 @@ return DEFAULT_DEITY /datum/preference/name/bible - savefile_key = "bible_name" + db_key = "bible_name" allow_numbers = TRUE can_randomize = FALSE diff --git a/code/modules/client/preferences/ooc.dm b/code/modules/client/preferences/ooc.dm index de436391edc97..af349d4239b45 100644 --- a/code/modules/client/preferences/ooc.dm +++ b/code/modules/client/preferences/ooc.dm @@ -1,7 +1,7 @@ /// The color admins will speak in for OOC. /datum/preference/color/ooc_color category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "ooccolor" + db_key = "ooccolor" savefile_identifier = PREFERENCE_PLAYER /datum/preference/color/ooc_color/create_default_value() diff --git a/code/modules/client/preferences/parallax.dm b/code/modules/client/preferences/parallax.dm index 24cccce2da62d..4be04b2be79b0 100644 --- a/code/modules/client/preferences/parallax.dm +++ b/code/modules/client/preferences/parallax.dm @@ -1,6 +1,6 @@ /// Determines parallax, "fancy space" /datum/preference/choiced/parallax - savefile_key = "parallax" + db_key = "parallax" savefile_identifier = PREFERENCE_PLAYER category = PREFERENCE_CATEGORY_GAME_PREFERENCES diff --git a/code/modules/client/preferences/pda.dm b/code/modules/client/preferences/pda.dm index fcbf4f1ad4be5..09a603fa0e24f 100644 --- a/code/modules/client/preferences/pda.dm +++ b/code/modules/client/preferences/pda.dm @@ -1,7 +1,7 @@ /// The color of a PDA /datum/preference/color/pda_color category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "pda_color" + db_key = "pda_color" savefile_identifier = PREFERENCE_PLAYER /datum/preference/color/pda_color/create_default_value() @@ -10,7 +10,7 @@ /// The visual style of a PDA /datum/preference/choiced/pda_style category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "pda_style" + db_key = "pda_style" savefile_identifier = PREFERENCE_PLAYER /datum/preference/choiced/pda_style/init_possible_values() diff --git a/code/modules/client/preferences/phobia.dm b/code/modules/client/preferences/phobia.dm index 2a5caa5ba0f8e..744f62dae8031 100644 --- a/code/modules/client/preferences/phobia.dm +++ b/code/modules/client/preferences/phobia.dm @@ -1,6 +1,6 @@ /datum/preference/choiced/phobia category = PREFERENCE_CATEGORY_SECONDARY_FEATURES - savefile_key = "phobia" + db_key = "phobia" savefile_identifier = PREFERENCE_CHARACTER /datum/preference/choiced/phobia/init_possible_values() diff --git a/code/modules/client/preferences/pixel_size.dm b/code/modules/client/preferences/pixel_size.dm index cb166e0139ac5..c22affec9b833 100644 --- a/code/modules/client/preferences/pixel_size.dm +++ b/code/modules/client/preferences/pixel_size.dm @@ -1,6 +1,6 @@ /datum/preference/numeric/pixel_size category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "pixel_size" + db_key = "pixel_size" savefile_identifier = PREFERENCE_PLAYER minimum = 0 diff --git a/code/modules/client/preferences/playtime_reward_cloak.dm b/code/modules/client/preferences/playtime_reward_cloak.dm index 5eb1c89e9b252..aa2af68da0171 100644 --- a/code/modules/client/preferences/playtime_reward_cloak.dm +++ b/code/modules/client/preferences/playtime_reward_cloak.dm @@ -4,7 +4,7 @@ /datum/preference/toggle/playtime_reward_cloak category = PREFERENCE_CATEGORY_NON_CONTEXTUAL savefile_identifier = PREFERENCE_CHARACTER - savefile_key = "playtime_reward_cloak" + db_key = "playtime_reward_cloak" /datum/preference/toggle/playtime_reward_cloak/is_accessible(datum/preferences/preferences) if (!..(preferences)) diff --git a/code/modules/client/preferences/preferred_map.dm b/code/modules/client/preferences/preferred_map.dm index 8c9f87e823220..2cbedc1a4a288 100644 --- a/code/modules/client/preferences/preferred_map.dm +++ b/code/modules/client/preferences/preferred_map.dm @@ -1,7 +1,7 @@ /// During map rotation, this will help determine the chosen map. /datum/preference/choiced/preferred_map category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "preferred_map" + db_key = "preferred_map" savefile_identifier = PREFERENCE_PLAYER /datum/preference/choiced/preferred_map/init_possible_values() diff --git a/code/modules/client/preferences/random.dm b/code/modules/client/preferences/random.dm index 6ad675fcda299..b3e45666d6e97 100644 --- a/code/modules/client/preferences/random.dm +++ b/code/modules/client/preferences/random.dm @@ -1,6 +1,6 @@ /datum/preference/choiced/random_body category = PREFERENCE_CATEGORY_NON_CONTEXTUAL - savefile_key = "random_body" + db_key = "random_body" savefile_identifier = PREFERENCE_CHARACTER can_randomize = FALSE @@ -19,7 +19,7 @@ /datum/preference/toggle/random_hardcore category = PREFERENCE_CATEGORY_NON_CONTEXTUAL - savefile_key = "random_hardcore" + db_key = "random_hardcore" savefile_identifier = PREFERENCE_CHARACTER can_randomize = FALSE default_value = FALSE @@ -35,7 +35,7 @@ /datum/preference/choiced/random_name category = PREFERENCE_CATEGORY_NON_CONTEXTUAL - savefile_key = "random_name" + db_key = "random_name" savefile_identifier = PREFERENCE_CHARACTER can_randomize = FALSE diff --git a/code/modules/client/preferences/runechat.dm b/code/modules/client/preferences/runechat.dm index 83282fefe36c5..d30e7cc951f8f 100644 --- a/code/modules/client/preferences/runechat.dm +++ b/code/modules/client/preferences/runechat.dm @@ -1,21 +1,21 @@ /datum/preference/toggle/enable_runechat category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "chat_on_map" + db_key = "chat_on_map" savefile_identifier = PREFERENCE_PLAYER /datum/preference/toggle/enable_runechat_non_mobs category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "see_chat_non_mob" + db_key = "see_chat_non_mob" savefile_identifier = PREFERENCE_PLAYER /datum/preference/toggle/see_rc_emotes category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "see_rc_emotes" + db_key = "see_rc_emotes" savefile_identifier = PREFERENCE_PLAYER /datum/preference/numeric/max_chat_length category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "max_chat_length" + db_key = "max_chat_length" savefile_identifier = PREFERENCE_PLAYER minimum = 1 diff --git a/code/modules/client/preferences/scaling_method.dm b/code/modules/client/preferences/scaling_method.dm index 63235abaf996f..66a0fdb623140 100644 --- a/code/modules/client/preferences/scaling_method.dm +++ b/code/modules/client/preferences/scaling_method.dm @@ -1,7 +1,7 @@ /// The scaling method to show the world in, e.g. nearest neighbor /datum/preference/choiced/scaling_method category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "scaling_method" + db_key = "scaling_method" savefile_identifier = PREFERENCE_PLAYER /datum/preference/choiced/scaling_method/create_default_value() diff --git a/code/modules/client/preferences/screentips.dm b/code/modules/client/preferences/screentips.dm index d446575ac7ba3..e86bf108db58c 100644 --- a/code/modules/client/preferences/screentips.dm +++ b/code/modules/client/preferences/screentips.dm @@ -1,6 +1,6 @@ /datum/preference/toggle/enable_screentips category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "screentip_pref" + db_key = "screentip_pref" savefile_identifier = PREFERENCE_PLAYER /datum/preference/toggle/enable_screentips/apply_to_client(client/client, value) @@ -8,7 +8,7 @@ /datum/preference/color/screentip_color category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "screentip_color" + db_key = "screentip_color" savefile_identifier = PREFERENCE_PLAYER /datum/preference/color/screentip_color/apply_to_client(client/client, value) diff --git a/code/modules/client/preferences/security_department.dm b/code/modules/client/preferences/security_department.dm index d3a637275efbc..a4f0b44c8cde3 100644 --- a/code/modules/client/preferences/security_department.dm +++ b/code/modules/client/preferences/security_department.dm @@ -3,7 +3,7 @@ category = PREFERENCE_CATEGORY_NON_CONTEXTUAL can_randomize = FALSE savefile_identifier = PREFERENCE_CHARACTER - savefile_key = "prefered_security_department" + db_key = "prefered_security_department" // This is what that #warn wants you to remove :) /datum/preference/choiced/security_department/deserialize(input, datum/preferences/preferences) diff --git a/code/modules/client/preferences/skin_tone.dm b/code/modules/client/preferences/skin_tone.dm index 119fd6a86a314..3bbffc25cc106 100644 --- a/code/modules/client/preferences/skin_tone.dm +++ b/code/modules/client/preferences/skin_tone.dm @@ -1,7 +1,7 @@ /datum/preference/choiced/skin_tone category = PREFERENCE_CATEGORY_SECONDARY_FEATURES savefile_identifier = PREFERENCE_CHARACTER - savefile_key = "skin_tone" + db_key = "skin_tone" /datum/preference/choiced/skin_tone/init_possible_values() return GLOB.skin_tones diff --git a/code/modules/client/preferences/species.dm b/code/modules/client/preferences/species.dm index 6cfffda952569..40329d666f75f 100644 --- a/code/modules/client/preferences/species.dm +++ b/code/modules/client/preferences/species.dm @@ -1,7 +1,7 @@ /// Species preference /datum/preference/choiced/species savefile_identifier = PREFERENCE_CHARACTER - savefile_key = "species" + db_key = "species" priority = PREFERENCE_PRIORITY_SPECIES randomize_by_default = FALSE diff --git a/code/modules/client/preferences/species_features/basic.dm b/code/modules/client/preferences/species_features/basic.dm index bf57c5fd1b5a0..44ed906d190d2 100644 --- a/code/modules/client/preferences/species_features/basic.dm +++ b/code/modules/client/preferences/species_features/basic.dm @@ -23,7 +23,7 @@ return values /datum/preference/color_legacy/eye_color - savefile_key = "eye_color" + db_key = "eye_color" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES relevant_species_trait = EYECOLOR @@ -41,7 +41,7 @@ return random_eye_color() /datum/preference/choiced/facial_hairstyle - savefile_key = "facial_style_name" + db_key = "facial_style_name" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Facial hair" @@ -62,7 +62,7 @@ return data /datum/preference/color_legacy/facial_hair_color - savefile_key = "facial_hair_color" + db_key = "facial_hair_color" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES relevant_species_trait = FACEHAIR @@ -71,7 +71,7 @@ target.facial_hair_color = value /datum/preference/color_legacy/hair_color - savefile_key = "hair_color" + db_key = "hair_color" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES relevant_species_trait = HAIR @@ -80,7 +80,7 @@ target.hair_color = value /datum/preference/choiced/hairstyle - savefile_key = "hairstyle_name" + db_key = "hairstyle_name" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Hairstyle" diff --git a/code/modules/client/preferences/species_features/ethereal.dm b/code/modules/client/preferences/species_features/ethereal.dm index 5a0ac1e5be5f9..81bfc419d6938 100644 --- a/code/modules/client/preferences/species_features/ethereal.dm +++ b/code/modules/client/preferences/species_features/ethereal.dm @@ -1,5 +1,5 @@ /datum/preference/choiced/ethereal_color - savefile_key = "feature_ethcolor" + db_key = "feature_ethcolor" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Ethereal color" diff --git a/code/modules/client/preferences/species_features/felinid.dm b/code/modules/client/preferences/species_features/felinid.dm index bc5445cd2fd0e..1ac69141b43ba 100644 --- a/code/modules/client/preferences/species_features/felinid.dm +++ b/code/modules/client/preferences/species_features/felinid.dm @@ -1,5 +1,5 @@ /datum/preference/choiced/tail_human - savefile_key = "feature_human_tail" + db_key = "feature_human_tail" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES can_randomize = FALSE @@ -16,7 +16,7 @@ return initial(tail.name) /datum/preference/choiced/ears - savefile_key = "feature_human_ears" + db_key = "feature_human_ears" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES can_randomize = FALSE diff --git a/code/modules/client/preferences/species_features/lizard.dm b/code/modules/client/preferences/species_features/lizard.dm index 732639fd63ee5..36c84ee5c987c 100644 --- a/code/modules/client/preferences/species_features/lizard.dm +++ b/code/modules/client/preferences/species_features/lizard.dm @@ -28,7 +28,7 @@ return values /datum/preference/choiced/lizard_body_markings - savefile_key = "feature_lizard_body_markings" + db_key = "feature_lizard_body_markings" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Body markings" @@ -66,7 +66,7 @@ target.dna.features["body_markings"] = value /datum/preference/choiced/lizard_frills - savefile_key = "feature_lizard_frills" + db_key = "feature_lizard_frills" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Frills" @@ -79,7 +79,7 @@ target.dna.features["frills"] = value /datum/preference/choiced/lizard_horns - savefile_key = "feature_lizard_horns" + db_key = "feature_lizard_horns" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Horns" @@ -92,7 +92,7 @@ target.dna.features["horns"] = value /datum/preference/choiced/lizard_legs - savefile_key = "feature_lizard_legs" + db_key = "feature_lizard_legs" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES relevant_mutant_bodypart = "legs" @@ -104,7 +104,7 @@ target.dna.features["legs"] = value /datum/preference/choiced/lizard_snout - savefile_key = "feature_lizard_snout" + db_key = "feature_lizard_snout" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Snout" @@ -117,7 +117,7 @@ target.dna.features["snout"] = value /datum/preference/choiced/lizard_spines - savefile_key = "feature_lizard_spines" + db_key = "feature_lizard_spines" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES relevant_mutant_bodypart = "spines" @@ -129,7 +129,7 @@ target.dna.features["spines"] = value /datum/preference/choiced/lizard_tail - savefile_key = "feature_lizard_tail" + db_key = "feature_lizard_tail" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES relevant_mutant_bodypart = "tail_lizard" diff --git a/code/modules/client/preferences/species_features/moth.dm b/code/modules/client/preferences/species_features/moth.dm index 10e1550a47f79..9cc6c975d3546 100644 --- a/code/modules/client/preferences/species_features/moth.dm +++ b/code/modules/client/preferences/species_features/moth.dm @@ -1,5 +1,5 @@ /datum/preference/choiced/moth_antennae - savefile_key = "feature_moth_antennae" + db_key = "feature_moth_antennae" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Antennae" @@ -27,7 +27,7 @@ target.dna.features["moth_antennae"] = value /datum/preference/choiced/moth_markings - savefile_key = "feature_moth_markings" + db_key = "feature_moth_markings" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Body markings" @@ -79,7 +79,7 @@ target.dna.features["moth_markings"] = value /datum/preference/choiced/moth_wings - savefile_key = "feature_moth_wings" + db_key = "feature_moth_wings" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Moth wings" diff --git a/code/modules/client/preferences/species_features/mutants.dm b/code/modules/client/preferences/species_features/mutants.dm index 3d79bcb5c342b..956291548684b 100644 --- a/code/modules/client/preferences/species_features/mutants.dm +++ b/code/modules/client/preferences/species_features/mutants.dm @@ -1,5 +1,5 @@ /datum/preference/color_legacy/mutant_color - savefile_key = "feature_mcolor" + db_key = "feature_mcolor" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES relevant_species_trait = MUTCOLORS diff --git a/code/modules/client/preferences/tgui.dm b/code/modules/client/preferences/tgui.dm index 44a8666e642bf..e685c66cd29c0 100644 --- a/code/modules/client/preferences/tgui.dm +++ b/code/modules/client/preferences/tgui.dm @@ -1,6 +1,6 @@ /datum/preference/toggle/tgui_fancy category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "tgui_fancy" + db_key = "tgui_fancy" savefile_identifier = PREFERENCE_PLAYER /datum/preference/toggle/tgui_fancy/apply_to_client(client/client, value) @@ -10,7 +10,7 @@ /datum/preference/toggle/tgui_lock category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "tgui_lock" + db_key = "tgui_lock" savefile_identifier = PREFERENCE_PLAYER default_value = FALSE diff --git a/code/modules/client/preferences/tooltips.dm b/code/modules/client/preferences/tooltips.dm index 1d7f558783888..0bd8ad5ed7447 100644 --- a/code/modules/client/preferences/tooltips.dm +++ b/code/modules/client/preferences/tooltips.dm @@ -1,6 +1,6 @@ /datum/preference/numeric/tooltip_delay category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "tip_delay" + db_key = "tip_delay" savefile_identifier = PREFERENCE_PLAYER minimum = 0 @@ -11,5 +11,5 @@ /datum/preference/toggle/enable_tooltips category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "enable_tips" + db_key = "enable_tips" savefile_identifier = PREFERENCE_PLAYER diff --git a/code/modules/client/preferences/ui_style.dm b/code/modules/client/preferences/ui_style.dm index 08f1af6c7dd53..c9f470c096254 100644 --- a/code/modules/client/preferences/ui_style.dm +++ b/code/modules/client/preferences/ui_style.dm @@ -2,7 +2,7 @@ /datum/preference/choiced/ui_style category = PREFERENCE_CATEGORY_GAME_PREFERENCES savefile_identifier = PREFERENCE_PLAYER - savefile_key = "UI_style" + db_key = "UI_style" should_generate_icons = TRUE /datum/preference/choiced/ui_style/init_possible_values() diff --git a/code/modules/client/preferences/underwear_color.dm b/code/modules/client/preferences/underwear_color.dm index cb0aa0c9bdafe..348e8153a8902 100644 --- a/code/modules/client/preferences/underwear_color.dm +++ b/code/modules/client/preferences/underwear_color.dm @@ -1,5 +1,5 @@ /datum/preference/color_legacy/underwear_color - savefile_key = "underwear_color" + db_key = "underwear_color" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES diff --git a/code/modules/client/preferences/uplink_location.dm b/code/modules/client/preferences/uplink_location.dm index 8ae892bb4024e..b29b985a03020 100644 --- a/code/modules/client/preferences/uplink_location.dm +++ b/code/modules/client/preferences/uplink_location.dm @@ -1,7 +1,7 @@ /datum/preference/choiced/uplink_location category = PREFERENCE_CATEGORY_NON_CONTEXTUAL savefile_identifier = PREFERENCE_CHARACTER - savefile_key = "uplink_loc" + db_key = "uplink_loc" can_randomize = FALSE /datum/preference/choiced/uplink_location/init_possible_values() diff --git a/code/modules/client/preferences/widescreen.dm b/code/modules/client/preferences/widescreen.dm index 1041a4f6f272c..f9e7f5fcb52af 100644 --- a/code/modules/client/preferences/widescreen.dm +++ b/code/modules/client/preferences/widescreen.dm @@ -1,6 +1,6 @@ /datum/preference/toggle/widescreen category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "widescreenpref" + db_key = "widescreenpref" savefile_identifier = PREFERENCE_PLAYER /datum/preference/toggle/widescreen/apply_to_client(client/client, value) diff --git a/code/modules/client/preferences/window_flashing.dm b/code/modules/client/preferences/window_flashing.dm index 687315387c631..2e1b6e98623ce 100644 --- a/code/modules/client/preferences/window_flashing.dm +++ b/code/modules/client/preferences/window_flashing.dm @@ -1,5 +1,5 @@ /// Enables flashing the window in your task tray for important events /datum/preference/toggle/window_flashing category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "windowflashing" + db_key = "windowflashing" savefile_identifier = PREFERENCE_PLAYER diff --git a/code/modules/surgery/organs/external/_external_organs.dm b/code/modules/surgery/organs/external/_external_organs.dm index e3c612fdf3d4c..ef1198317ceb4 100644 --- a/code/modules/surgery/organs/external/_external_organs.dm +++ b/code/modules/surgery/organs/external/_external_organs.dm @@ -18,7 +18,7 @@ ///Defines what kind of 'organ' we're looking at. Sprites have names like 'm_firemoth_mothwings'. 'mothwings' would then be feature_key var/feature_key = "" - /// The savefile_key of the preference this relates to. Used for the preferences UI. + /// The db_key of the preference this relates to. Used for the preferences UI. var/preference ///Sprite datum we use to draw on the bodypart diff --git a/code/modules/unit_tests/preferences.dm b/code/modules/unit_tests/preferences.dm index e3d6afbe53300..bfffb5ab3ba6c 100644 --- a/code/modules/unit_tests/preferences.dm +++ b/code/modules/unit_tests/preferences.dm @@ -21,20 +21,20 @@ preference.is_valid(list(1, 2, 3)) /// Requires all preferences to have a valid, unique savefile_identifier. -/datum/unit_test/preferences_valid_savefile_key +/datum/unit_test/preferences_valid_db_key -/datum/unit_test/preferences_valid_savefile_key/Run() - var/list/known_savefile_keys = list() +/datum/unit_test/preferences_valid_db_key/Run() + var/list/known_db_keys = list() for (var/preference_type in GLOB.preference_entries) var/datum/preference/preference = GLOB.preference_entries[preference_type] - if (!istext(preference.savefile_key)) - Fail("[preference_type] has an invalid savefile_key.") + if (!istext(preference.db_key)) + Fail("[preference_type] has an invalid db_key.") - if (preference.savefile_key in known_savefile_keys) - Fail("[preference_type] has a non-unique savefile_key `[preference.savefile_key]`!") + if (preference.db_key in known_db_keys) + Fail("[preference_type] has a non-unique db_key `[preference.db_key]`!") - known_savefile_keys += preference.savefile_key + known_db_keys += preference.db_key /// Requires all main features have a main_feature_name /datum/unit_test/preferences_valid_main_feature_name From 958d86cd3634484914ab03428e47eb374a607a2f Mon Sep 17 00:00:00 2001 From: itsmeow Date: Thu, 30 Mar 2023 03:15:25 -0500 Subject: [PATCH 021/269] savefile_identifier to preference_type --- code/__DEFINES/preferences.dm | 2 +- code/modules/client/preferences.dm | 2 +- code/modules/client/preferences/README.md | 132 ++++++++++-------- .../modules/client/preferences/_preference.dm | 22 +-- code/modules/client/preferences/admin.dm | 4 +- code/modules/client/preferences/age.dm | 2 +- .../client/preferences/ai_core_display.dm | 2 +- .../client/preferences/ambient_occlusion.dm | 2 +- .../client/preferences/auto_fit_viewport.dm | 2 +- code/modules/client/preferences/body_type.dm | 2 +- .../preferences/broadcast_login_logout.dm | 2 +- .../client/preferences/buttons_locked.dm | 2 +- code/modules/client/preferences/clothing.dm | 10 +- .../client/preferences/darkened_flash.dm | 2 +- code/modules/client/preferences/fps.dm | 2 +- code/modules/client/preferences/gender.dm | 2 +- code/modules/client/preferences/ghost.dm | 12 +- code/modules/client/preferences/hotkeys.dm | 2 +- .../client/preferences/item_outlines.dm | 2 +- .../client/preferences/jobless_role.dm | 2 +- code/modules/client/preferences/names.dm | 2 +- code/modules/client/preferences/ooc.dm | 2 +- code/modules/client/preferences/parallax.dm | 2 +- code/modules/client/preferences/pda.dm | 4 +- code/modules/client/preferences/phobia.dm | 2 +- code/modules/client/preferences/pixel_size.dm | 2 +- .../preferences/playtime_reward_cloak.dm | 2 +- .../client/preferences/preferred_map.dm | 2 +- code/modules/client/preferences/random.dm | 6 +- code/modules/client/preferences/runechat.dm | 8 +- .../client/preferences/scaling_method.dm | 2 +- code/modules/client/preferences/screentips.dm | 4 +- .../client/preferences/security_department.dm | 2 +- code/modules/client/preferences/skin_tone.dm | 2 +- code/modules/client/preferences/species.dm | 2 +- .../preferences/species_features/basic.dm | 10 +- .../preferences/species_features/ethereal.dm | 2 +- .../preferences/species_features/felinid.dm | 4 +- .../preferences/species_features/lizard.dm | 14 +- .../preferences/species_features/moth.dm | 6 +- .../preferences/species_features/mutants.dm | 2 +- code/modules/client/preferences/tgui.dm | 4 +- code/modules/client/preferences/tooltips.dm | 4 +- code/modules/client/preferences/ui_style.dm | 2 +- .../client/preferences/underwear_color.dm | 2 +- .../client/preferences/uplink_location.dm | 2 +- code/modules/client/preferences/widescreen.dm | 2 +- .../client/preferences/window_flashing.dm | 2 +- code/modules/unit_tests/preferences.dm | 4 +- 49 files changed, 162 insertions(+), 152 deletions(-) diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm index 538d9b319c65e..d41f78f0842ad 100644 --- a/code/__DEFINES/preferences.dm +++ b/code/__DEFINES/preferences.dm @@ -197,7 +197,7 @@ // True value of max save slots (3 is default, 8 is byond member, +1 to either if you have the extra slot loadout entry). Potential max is 9 #define TRUE_MAX_SAVE_SLOTS 9 -// Values for /datum/preference/savefile_identifier +// Values for /datum/preference/preference_type /// This preference is character specific. #define PREFERENCE_CHARACTER "character" /// This preference is account specific. diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 20704d8669e18..bd73faf11b8ac 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -488,7 +488,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/character_preview_view) character.dna.features = list() for (var/datum/preference/preference as anything in get_preferences_in_priority_order()) - if (preference.savefile_identifier != PREFERENCE_CHARACTER) + if (preference.preference_type != PREFERENCE_CHARACTER) continue preference.apply_to_human(character, read_preference(preference.type)) diff --git a/code/modules/client/preferences/README.md b/code/modules/client/preferences/README.md index fa13228f11c02..082136d693c28 100644 --- a/code/modules/client/preferences/README.md +++ b/code/modules/client/preferences/README.md @@ -1,6 +1,6 @@ # Preferences (by Mothblocks) -This does not contain all the information on specific values--you can find those as doc-comments in relevant paths, such as `/datum/preference`. Rather, this gives you an overview for creating *most* preferences, and getting your foot in the door to create more advanced ones. +This does not contain all the information on specific values--you can find those as doc-comments in relevant paths, such as `/datum/preference`. Rather, this gives you an overview for creating _most_ preferences, and getting your foot in the door to create more advanced ones. ## Anatomy of a preference (A.K.A. how do I make one?) @@ -10,9 +10,10 @@ Most preferences consist of two parts: 2. A tgui representation in a TypeScript file. Every `/datum/preference` requires these three values be set: + 1. `category` - See [Categories](#Categories). -2. `savefile_key` - The value which will be saved in the savefile. This will also be the identifier for tgui. -3. `savefile_identifier` - Whether or not this is a character specific preference (`PREFERENCE_CHARACTER`) or one that affects the player (`PREFERENCE_PLAYER`). As an example: hair color is `PREFERENCE_CHARACTER` while your UI settings are `PREFERENCE_PLAYER`, since they do not change between characters. +2. `db_key` - The value which will be saved in the savefile. This will also be the identifier for tgui. +3. `preference_type` - Whether or not this is a character specific preference (`PREFERENCE_CHARACTER`) or one that affects the player (`PREFERENCE_PLAYER`). As an example: hair color is `PREFERENCE_CHARACTER` while your UI settings are `PREFERENCE_PLAYER`, since they do not change between characters. For the tgui representation, most preferences will create a `.tsx` file in `tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/`. If your preference is a character preference, make a new file in `character_preferences`. Otherwise, put it in `game_preferences`. The filename does not matter, and this file can hold multiple relevant preferences if you would like. @@ -21,16 +22,16 @@ From here, you will want to write code resembling: ```ts import { Feature } from "../base"; -export const savefile_key_here: Feature = { - name: "Preference Name Here", - component: Component, +export const db_key_here: Feature = { + name: "Preference Name Here", + component: Component, - // Necessary for game preferences, unused for others - category: "CATEGORY", + // Necessary for game preferences, unused for others + category: "CATEGORY", - // Optional, only shown in game preferences - description: "This preference will blow your mind!", -} + // Optional, only shown in game preferences + description: "This preference will blow your mind!", +}; ``` `T` and `Component` depend on the type of preference you're making. Here are all common examples... @@ -44,8 +45,8 @@ A numeric preference derives from `/datum/preference/numeric`. ```dm /datum/preference/numeric/legs category = PREFERENCE_CATEGORY_NON_CONTEXTUAL - savefile_identifier = PREFERENCE_CHARACTER - savefile_key = "legs" + preference_type = PREFERENCE_CHARACTER + db_key = "legs" minimum = 1 maximum = 8 @@ -59,9 +60,9 @@ Your `.tsx` file would look like: import { Feature, FeatureNumberInput } from "../base"; export const legs: Feature = { - name: "Legs", - component: FeatureNumberInput, -} + name: "Legs", + component: FeatureNumberInput, +}; ``` ## Toggle preferences @@ -71,8 +72,8 @@ Examples include enabling tooltips. ```dm /datum/preference/toggle/enable_breathing category = PREFERENCE_CATEGORY_NON_CONTEXTUAL - savefile_identifier = PREFERENCE_CHARACTER - savefile_key = "enable_breathing" + preference_type = PREFERENCE_CHARACTER + db_key = "enable_breathing" // Optional, TRUE by default default_value = FALSE @@ -84,12 +85,13 @@ Your `.tsx` file would look like: import { CheckboxInput, FeatureToggle } from "../base"; export const enable_breathing: Feature = { - name: "Enable breathing", - component: CheckboxInput, -} + name: "Enable breathing", + component: CheckboxInput, +}; ``` ## Choiced preferences + A choiced preference is one where the only options are in a distinct few amount of choices. Examples include skin tone, shirt, and UI style. To create one, derive from `/datum/preference/choiced`. @@ -97,8 +99,8 @@ To create one, derive from `/datum/preference/choiced`. ```dm /datum/preference/choiced/favorite_drink category = PREFERENCE_CATEGORY_NON_CONTEXTUAL - savefile_identifier = PREFERENCE_CHARACTER - savefile_key = "favorite_drink" + preference_type = PREFERENCE_CHARACTER + db_key = "favorite_drink" ``` Now we need to tell the game what the choices are. We do this by overriding `init_possible_values()`. This will return a list of possible options. @@ -118,21 +120,22 @@ Your `.tsx` file would then look like: import { FeatureChoiced, FeatureDropdownInput } from "../base"; export const favorite_drink: FeatureChoiced = { - name: "Favorite drink", - component: FeatureDropdownInput, + name: "Favorite drink", + component: FeatureDropdownInput, }; ``` This will create a dropdown input for your preference. ### Choiced preferences - Icons + Choiced preferences can generate icons. This is how the clothing/species preferences work, for instance. However, if we just want a basic dropdown input with icons, it would look like this: ```dm /datum/preference/choiced/favorite_drink category = PREFERENCE_CATEGORY_NON_CONTEXTUAL - savefile_identifier = PREFERENCE_CHARACTER - savefile_key = "favorite_drink" + preference_type = PREFERENCE_CHARACTER + db_key = "favorite_drink" should_generate_icons = TRUE // NEW! This is necessary. // Instead of returning a flat list, this now returns an assoc list @@ -151,12 +154,13 @@ Then, change your `.tsx` file to look like: import { FeatureChoiced, FeatureIconnedDropdownInput } from "../base"; export const favorite_drink: FeatureChoiced = { - name: "Favorite drink", - component: FeatureIconnedDropdownInput, + name: "Favorite drink", + component: FeatureIconnedDropdownInput, }; ``` ### Choiced preferences - Display names + Sometimes the values you want to save in code aren't the same as the ones you want to display. You can specify display names to change this. The only thing you will add is "compiled data". @@ -178,13 +182,14 @@ The only thing you will add is "compiled data". Your `.tsx` file does not change. The UI will figure it out for you! ## Color preferences -These refer to colors, such as your OOC color. When read, these values will be given as 6 hex digits, *without* the pound sign. + +These refer to colors, such as your OOC color. When read, these values will be given as 6 hex digits, _without_ the pound sign. ```dm /datum/preference/color/eyeliner_color category = PREFERENCE_CATEGORY_NON_CONTEXTUAL - savefile_identifier = PREFERENCE_CHARACTER - savefile_key = "eyeliner_color" + preference_type = PREFERENCE_CHARACTER + db_key = "eyeliner_color" ``` Your `.tsx` file would look like: @@ -193,12 +198,13 @@ Your `.tsx` file would look like: import { FeatureColorInput, Feature } from "../base"; export const eyeliner_color: Feature = { - name: "Eyeliner color", - component: FeatureColorInput, + name: "Eyeliner color", + component: FeatureColorInput, }; ``` ## Name preferences + These refer to an alternative name. Examples include AI names and backup human names. These exist in `code/modules/client/preferences/names.dm`. @@ -207,7 +213,7 @@ These do not need a `.ts` file, and will be created in the UI automatically. ```dm /datum/preference/name/doctor - savefile_key = "doctor_name" + db_key = "doctor_name" // The name on the UI explanation = "Doctor name" @@ -226,7 +232,7 @@ There are a handful of procs preferences can use to act on their own: ```dm /// Apply this preference onto the given client. -/// Called when the savefile_identifier == PREFERENCE_PLAYER. +/// Called when the preference_type == PREFERENCE_PLAYER. /datum/preference/proc/apply_to_client(client/client, value) /// Fired when the preference is updated. @@ -235,7 +241,7 @@ There are a handful of procs preferences can use to act on their own: /// Apply this preference onto the given human. /// Must be overriden by subtypes. -/// Called when the savefile_identifier == PREFERENCE_CHARACTER. +/// Called when the preference_type == PREFERENCE_CHARACTER. /datum/preference/proc/apply_to_human(mob/living/carbon/human/target, value) ``` @@ -251,6 +257,7 @@ If your preference is `PREFERENCE_CHARACTER`, it MUST override `apply_to_human`, You can also read preferences directly with `preferences.read_preference(/datum/preference/type/here)`, which will return the stored value. ## Categories + Every preference needs to be in a `category`. These can be found in `code/__DEFINES/preferences.dm`. ```dm @@ -284,9 +291,9 @@ Secondary features tend to be species specific. Non contextual features shouldn' There are three procs to be aware of in regards to this topic: -- `create_default_value()`. This is used when a value deserializes improperly or when a new character is created. -- `create_informed_default_value(datum/preferences/preferences)` - Used for more complicated default values, like how names require the gender. Will call `create_default_value()` by default. -- `create_random_value(datum/preferences/preferences)` - Explicitly used for random values, such as when a character is being randomized. +- `create_default_value()`. This is used when a value deserializes improperly or when a new character is created. +- `create_informed_default_value(datum/preferences/preferences)` - Used for more complicated default values, like how names require the gender. Will call `create_default_value()` by default. +- `create_random_value(datum/preferences/preferences)` - Explicitly used for random values, such as when a character is being randomized. `create_default_value()` in most preferences will create a random value. If this is a problem (like how default characters should always be human), you can override `create_default_value()`. By default (without overriding `create_random_value`), random values are just default values. @@ -302,7 +309,7 @@ Compiled data is sent to the `serverData` field in the `FeatureValueProps`. If you have good knowledge with tgui (especially TypeScript), you'll be able to create your own component to represent preferences. -The `component` field in a feature accepts __any__ component that accepts `FeatureValueProps`. +The `component` field in a feature accepts **any** component that accepts `FeatureValueProps`. This will give you the fields: @@ -317,7 +324,7 @@ value: TReceiving, `act` is the same as the one you get from `useBackend`. -`featureId` is the savefile_key of the feature. +`featureId` is the db_key of the feature. `handleSetValue` is a function that, when called, will tell the server the new value, as well as changing the value immediately locally. @@ -331,18 +338,21 @@ For a basic example of how this can look, observe `CheckboxInput`: ```tsx export const CheckboxInput = ( - props: FeatureValueProps + props: FeatureValueProps ) => { - return ( { - props.handleSetValue(!props.value); - }} - />); + return ( + { + props.handleSetValue(!props.value); + }} + /> + ); }; ``` ## Advanced - Middleware + A `/datum/preference_middleware` is a way to inject your own data at specific points, as well as hijack actions. Middleware can hijack actions by specifying `action_delegations`: @@ -376,10 +386,10 @@ import { Job } from "../base"; import { Service } from "../departments"; const Cook: Job = { - name: "Cook", - // If you need more room, use `multiline` - description: "Serve food, cook meat, keep the crew fed.", - department: Service, + name: "Cook", + // If you need more room, use `multiline` + description: "Serve food, cook meat, keep the crew fed.", + department: Service, }; export default Cook; @@ -442,20 +452,20 @@ import { Antagonist, Category } from "../base"; import { multiline } from "common/string"; const Changeling: Antagonist = { - key: "changeling", // This must be the same as your filename - name: "Changeling", - description: [ - multiline` + key: "changeling", // This must be the same as your filename + name: "Changeling", + description: [ + multiline` A highly intelligent alien predator that is capable of altering their shape to flawlessly resemble a human. `, - multiline` + multiline` Transform yourself or others into different identities, and buy from an arsenal of biological weaponry with the DNA you collect. `, - ], - category: Category.Roundstart, // Category.Roundstart, Category.Midround, or Category.Latejoin + ], + category: Category.Roundstart, // Category.Roundstart, Category.Midround, or Category.Latejoin }; export default Changeling; @@ -473,4 +483,4 @@ If `antag_preference` is set, it will refer to that preference instead of `antag ## Updating special_roles -In `code/__DEFINES/role_preferences.dm` (the same place you'll need to make your ROLE_\* defined), simply add your antagonist to the `special_roles` assoc list. The key is your ROLE, the value is the number of days since your first game in order to play as that antagonist. +In `code/__DEFINES/role_preferences.dm` (the same place you'll need to make your ROLE\_\* defined), simply add your antagonist to the `special_roles` assoc list. The key is your ROLE, the value is the number of days since your first game in order to play as that antagonist. diff --git a/code/modules/client/preferences/_preference.dm b/code/modules/client/preferences/_preference.dm index 94464df1b7149..12ad78f9e948f 100644 --- a/code/modules/client/preferences/_preference.dm +++ b/code/modules/client/preferences/_preference.dm @@ -78,7 +78,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) /// What savefile should this preference be read from? /// Valid values are PREFERENCE_CHARACTER and PREFERENCE_PLAYER. /// See the documentation in [code/__DEFINES/preferences.dm]. - var/savefile_identifier + var/preference_type /// The priority of when to apply this preference. /// Used for when you need to rely on another preference. @@ -145,7 +145,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) /// Returns whether or not a preference can be randomized. /datum/preference/proc/is_randomizable() SHOULD_NOT_OVERRIDE(TRUE) - return savefile_identifier == PREFERENCE_CHARACTER && can_randomize + return preference_type == PREFERENCE_CHARACTER && can_randomize /// Given a savefile, return either the saved data or an acceptable default. /// This will write to the savefile if a value was not found with the new value. @@ -177,7 +177,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) return TRUE /// Apply this preference onto the given client. -/// Called when the savefile_identifier == PREFERENCE_PLAYER. +/// Called when the preference_type == PREFERENCE_PLAYER. /datum/preference/proc/apply_to_client(client/client, value) SHOULD_NOT_SLEEP(TRUE) SHOULD_CALL_PARENT(FALSE) @@ -191,20 +191,20 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) /// Apply this preference onto the given human. /// Must be overriden by subtypes. -/// Called when the savefile_identifier == PREFERENCE_CHARACTER. +/// Called when the preference_type == PREFERENCE_CHARACTER. /datum/preference/proc/apply_to_human(mob/living/carbon/human/target, value) SHOULD_NOT_SLEEP(TRUE) SHOULD_CALL_PARENT(FALSE) CRASH("`apply_to_human()` was not implemented for [type]!") /// Returns which savefile to use for a given savefile identifier -/datum/preferences/proc/get_savefile_for_savefile_identifier(savefile_identifier) +/datum/preferences/proc/get_savefile_for_preference_type(preference_type) RETURN_TYPE(/savefile) if (!parent) return null - switch (savefile_identifier) + switch (preference_type) if (PREFERENCE_CHARACTER) return character_savefile if (PREFERENCE_PLAYER) @@ -213,7 +213,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) game_savefile.cd = "/" return game_savefile else - CRASH("Unknown savefile identifier [savefile_identifier]") + CRASH("Unknown savefile identifier [preference_type]") /// Read a /datum/preference type and return its value. /// This will write to the savefile if a value was not found with the new value. @@ -232,7 +232,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) if (preference_type in value_cache) return value_cache[preference_type] - var/value = preference_entry.read(get_savefile_for_savefile_identifier(preference_entry.savefile_identifier), src) + var/value = preference_entry.read(get_savefile_for_preference_type(preference_entry.preference_type), src) if (isnull(value)) value = preference_entry.create_informed_default_value(src) if (write_preference(preference_entry, value)) @@ -246,7 +246,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) /// Returns TRUE for a successful preference application. /// Returns FALSE if it is invalid. /datum/preferences/proc/write_preference(datum/preference/preference, preference_value) - var/savefile = get_savefile_for_savefile_identifier(preference.savefile_identifier) + var/savefile = get_savefile_for_preference_type(preference.preference_type) var/new_value = preference.deserialize(preference_value, src) var/success = preference.write(savefile, new_value) if (success) @@ -269,7 +269,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) recently_updated_keys |= preference.type value_cache[preference.type] = new_value - if (preference.savefile_identifier == PREFERENCE_PLAYER) + if (preference.preference_type == PREFERENCE_PLAYER) preference.apply_to_client_updated(parent, read_preference(preference.type)) else character_preview_view?.update_body() @@ -318,7 +318,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) /// appear. /datum/preference/proc/should_show_on_page(preference_tab) var/is_on_character_page = preference_tab == PREFERENCE_TAB_CHARACTER_PREFERENCES - var/is_character_preference = savefile_identifier == PREFERENCE_CHARACTER + var/is_character_preference = preference_type == PREFERENCE_CHARACTER return is_on_character_page == is_character_preference /// A preference that is a choice of one option among a fixed set. diff --git a/code/modules/client/preferences/admin.dm b/code/modules/client/preferences/admin.dm index 8bbb20f079f0f..2a7f077b0d046 100644 --- a/code/modules/client/preferences/admin.dm +++ b/code/modules/client/preferences/admin.dm @@ -1,7 +1,7 @@ /datum/preference/color/asay_color category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "asaycolor" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER /datum/preference/color/asay_color/create_default_value() return DEFAULT_ASAY_COLOR @@ -16,7 +16,7 @@ /datum/preference/choiced/brief_outfit category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "brief_outfit" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER /datum/preference/choiced/brief_outfit/deserialize(input, datum/preferences/preferences) var/path = text2path(input) diff --git a/code/modules/client/preferences/age.dm b/code/modules/client/preferences/age.dm index 4b2abb37f3f67..5d5d9bbf7a939 100644 --- a/code/modules/client/preferences/age.dm +++ b/code/modules/client/preferences/age.dm @@ -1,7 +1,7 @@ /datum/preference/numeric/age category = PREFERENCE_CATEGORY_NON_CONTEXTUAL db_key = "age" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER minimum = AGE_MIN maximum = AGE_MAX diff --git a/code/modules/client/preferences/ai_core_display.dm b/code/modules/client/preferences/ai_core_display.dm index 3c6ed19b74461..a083455018ecf 100644 --- a/code/modules/client/preferences/ai_core_display.dm +++ b/code/modules/client/preferences/ai_core_display.dm @@ -1,7 +1,7 @@ /// What to show on the AI screen /datum/preference/choiced/ai_core_display category = PREFERENCE_CATEGORY_NON_CONTEXTUAL - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER db_key = "preferred_ai_core_display" should_generate_icons = TRUE diff --git a/code/modules/client/preferences/ambient_occlusion.dm b/code/modules/client/preferences/ambient_occlusion.dm index 94182cc6bb99a..5e94743ae3834 100644 --- a/code/modules/client/preferences/ambient_occlusion.dm +++ b/code/modules/client/preferences/ambient_occlusion.dm @@ -2,7 +2,7 @@ /datum/preference/toggle/ambient_occlusion category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "ambientocclusion" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER /datum/preference/toggle/ambient_occlusion/apply_to_client(client/client, value) var/atom/movable/screen/plane_master/game_world/plane_master = locate() in client?.screen diff --git a/code/modules/client/preferences/auto_fit_viewport.dm b/code/modules/client/preferences/auto_fit_viewport.dm index 3606bd5761814..462b0601e7a3d 100644 --- a/code/modules/client/preferences/auto_fit_viewport.dm +++ b/code/modules/client/preferences/auto_fit_viewport.dm @@ -1,7 +1,7 @@ /datum/preference/toggle/auto_fit_viewport category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "auto_fit_viewport" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER /datum/preference/toggle/auto_fit_viewport/apply_to_client_updated(client/client, value) INVOKE_ASYNC(client, /client/verb/fit_viewport) diff --git a/code/modules/client/preferences/body_type.dm b/code/modules/client/preferences/body_type.dm index cde2aa7255b08..b9dbd6e0acaba 100644 --- a/code/modules/client/preferences/body_type.dm +++ b/code/modules/client/preferences/body_type.dm @@ -2,7 +2,7 @@ category = PREFERENCE_CATEGORY_NON_CONTEXTUAL priority = PREFERENCE_PRIORITY_BODY_TYPE db_key = "body_type" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER /datum/preference/choiced/body_type/init_possible_values() return list(MALE, FEMALE) diff --git a/code/modules/client/preferences/broadcast_login_logout.dm b/code/modules/client/preferences/broadcast_login_logout.dm index d87d524ae6044..ffea82ef3c3de 100644 --- a/code/modules/client/preferences/broadcast_login_logout.dm +++ b/code/modules/client/preferences/broadcast_login_logout.dm @@ -2,4 +2,4 @@ /datum/preference/toggle/broadcast_login_logout category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "broadcast_login_logout" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER diff --git a/code/modules/client/preferences/buttons_locked.dm b/code/modules/client/preferences/buttons_locked.dm index 0c60a73f867ca..faad04e9e7888 100644 --- a/code/modules/client/preferences/buttons_locked.dm +++ b/code/modules/client/preferences/buttons_locked.dm @@ -1,5 +1,5 @@ /datum/preference/toggle/buttons_locked category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "buttons_locked" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER default_value = FALSE diff --git a/code/modules/client/preferences/clothing.dm b/code/modules/client/preferences/clothing.dm index 74f601152efff..bef5e3f41df69 100644 --- a/code/modules/client/preferences/clothing.dm +++ b/code/modules/client/preferences/clothing.dm @@ -27,7 +27,7 @@ /// Backpack preference /datum/preference/choiced/backpack db_key = "backpack" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER main_feature_name = "Backpack" category = PREFERENCE_CATEGORY_CLOTHING should_generate_icons = TRUE @@ -56,7 +56,7 @@ /// Jumpsuit preference /datum/preference/choiced/jumpsuit db_key = "jumpsuit_style" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER main_feature_name = "Jumpsuit" category = PREFERENCE_CATEGORY_CLOTHING should_generate_icons = TRUE @@ -75,7 +75,7 @@ /// Socks preference /datum/preference/choiced/socks db_key = "socks" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER main_feature_name = "Socks" category = PREFERENCE_CATEGORY_CLOTHING should_generate_icons = TRUE @@ -89,7 +89,7 @@ /// Undershirt preference /datum/preference/choiced/undershirt db_key = "undershirt" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER main_feature_name = "Undershirt" category = PREFERENCE_CATEGORY_CLOTHING should_generate_icons = TRUE @@ -124,7 +124,7 @@ /// Underwear preference /datum/preference/choiced/underwear db_key = "underwear" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER main_feature_name = "Underwear" category = PREFERENCE_CATEGORY_CLOTHING should_generate_icons = TRUE diff --git a/code/modules/client/preferences/darkened_flash.dm b/code/modules/client/preferences/darkened_flash.dm index 9dd67d3225cf8..d8ab9fd4de94c 100644 --- a/code/modules/client/preferences/darkened_flash.dm +++ b/code/modules/client/preferences/darkened_flash.dm @@ -3,4 +3,4 @@ category = PREFERENCE_CATEGORY_GAME_PREFERENCES default_value = FALSE db_key = "darkened_flash" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER diff --git a/code/modules/client/preferences/fps.dm b/code/modules/client/preferences/fps.dm index e7760536753af..4987b429cf4bc 100644 --- a/code/modules/client/preferences/fps.dm +++ b/code/modules/client/preferences/fps.dm @@ -1,7 +1,7 @@ /datum/preference/numeric/fps category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "clientfps" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER minimum = -1 maximum = 240 diff --git a/code/modules/client/preferences/gender.dm b/code/modules/client/preferences/gender.dm index 9992ff0e7786c..e3a384573f8c6 100644 --- a/code/modules/client/preferences/gender.dm +++ b/code/modules/client/preferences/gender.dm @@ -1,6 +1,6 @@ /// Gender preference /datum/preference/choiced/gender - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER db_key = "gender" priority = PREFERENCE_PRIORITY_GENDER diff --git a/code/modules/client/preferences/ghost.dm b/code/modules/client/preferences/ghost.dm index 668651db94606..7f80fc5300b0f 100644 --- a/code/modules/client/preferences/ghost.dm +++ b/code/modules/client/preferences/ghost.dm @@ -1,7 +1,7 @@ /// Determines what accessories your ghost will look like they have. /datum/preference/choiced/ghost_accessories db_key = "ghost_accs" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER category = PREFERENCE_CATEGORY_GAME_PREFERENCES /datum/preference/choiced/ghost_accessories/init_possible_values() @@ -36,7 +36,7 @@ /// Determines the appearance of your ghost to others, when you are a BYOND member /datum/preference/choiced/ghost_form db_key = "ghost_form" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER category = PREFERENCE_CATEGORY_GAME_PREFERENCES should_generate_icons = TRUE @@ -101,7 +101,7 @@ /// Toggles the HUD for ghosts /datum/preference/toggle/ghost_hud db_key = "ghost_hud" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER category = PREFERENCE_CATEGORY_GAME_PREFERENCES /datum/preference/toggle/ghost_hud/apply_to_client(client/client, value) @@ -111,7 +111,7 @@ /// Determines what ghosts orbiting look like to you. /datum/preference/choiced/ghost_orbit db_key = "ghost_orbit" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER category = PREFERENCE_CATEGORY_GAME_PREFERENCES /datum/preference/choiced/ghost_orbit/init_possible_values() @@ -136,7 +136,7 @@ /// Determines how to show other ghosts /datum/preference/choiced/ghost_others db_key = "ghost_others" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER category = PREFERENCE_CATEGORY_GAME_PREFERENCES /datum/preference/choiced/ghost_others/init_possible_values() @@ -174,5 +174,5 @@ /// Whether or not ghosts can examine things by clicking on them. /datum/preference/toggle/inquisitive_ghost db_key = "inquisitive_ghost" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER category = PREFERENCE_CATEGORY_GAME_PREFERENCES diff --git a/code/modules/client/preferences/hotkeys.dm b/code/modules/client/preferences/hotkeys.dm index cc65cf2ac1b1c..f4c9a1b19f655 100644 --- a/code/modules/client/preferences/hotkeys.dm +++ b/code/modules/client/preferences/hotkeys.dm @@ -1,7 +1,7 @@ /datum/preference/toggle/hotkeys category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "hotkeys" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER /datum/preference/toggle/hotkeys/apply_to_client(client/client, value) client.hotkeys = value diff --git a/code/modules/client/preferences/item_outlines.dm b/code/modules/client/preferences/item_outlines.dm index f9d4b6626bb99..7f13f38d3ae24 100644 --- a/code/modules/client/preferences/item_outlines.dm +++ b/code/modules/client/preferences/item_outlines.dm @@ -1,4 +1,4 @@ /datum/preference/toggle/item_outlines category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "itemoutline_pref" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER diff --git a/code/modules/client/preferences/jobless_role.dm b/code/modules/client/preferences/jobless_role.dm index 29c0219915bc6..955ea1a449049 100644 --- a/code/modules/client/preferences/jobless_role.dm +++ b/code/modules/client/preferences/jobless_role.dm @@ -1,6 +1,6 @@ /datum/preference/choiced/jobless_role db_key = "joblessrole" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER /datum/preference/choiced/jobless_role/create_default_value() return BERANDOMJOB diff --git a/code/modules/client/preferences/names.dm b/code/modules/client/preferences/names.dm index fab26bec12320..7ca417bf40bbf 100644 --- a/code/modules/client/preferences/names.dm +++ b/code/modules/client/preferences/names.dm @@ -2,7 +2,7 @@ /datum/preference/name category = "names" priority = PREFERENCE_PRIORITY_NAMES - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER abstract_type = /datum/preference/name /// The display name when showing on the "other names" panel diff --git a/code/modules/client/preferences/ooc.dm b/code/modules/client/preferences/ooc.dm index af349d4239b45..0f7a3e75a70ab 100644 --- a/code/modules/client/preferences/ooc.dm +++ b/code/modules/client/preferences/ooc.dm @@ -2,7 +2,7 @@ /datum/preference/color/ooc_color category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "ooccolor" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER /datum/preference/color/ooc_color/create_default_value() return "#c43b23" diff --git a/code/modules/client/preferences/parallax.dm b/code/modules/client/preferences/parallax.dm index 4be04b2be79b0..f2e4577c91e0d 100644 --- a/code/modules/client/preferences/parallax.dm +++ b/code/modules/client/preferences/parallax.dm @@ -1,7 +1,7 @@ /// Determines parallax, "fancy space" /datum/preference/choiced/parallax db_key = "parallax" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER category = PREFERENCE_CATEGORY_GAME_PREFERENCES /datum/preference/choiced/parallax/init_possible_values() diff --git a/code/modules/client/preferences/pda.dm b/code/modules/client/preferences/pda.dm index 09a603fa0e24f..5f658f28676d5 100644 --- a/code/modules/client/preferences/pda.dm +++ b/code/modules/client/preferences/pda.dm @@ -2,7 +2,7 @@ /datum/preference/color/pda_color category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "pda_color" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER /datum/preference/color/pda_color/create_default_value() return COLOR_OLIVE @@ -11,7 +11,7 @@ /datum/preference/choiced/pda_style category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "pda_style" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER /datum/preference/choiced/pda_style/init_possible_values() return GLOB.pda_styles diff --git a/code/modules/client/preferences/phobia.dm b/code/modules/client/preferences/phobia.dm index 744f62dae8031..5fba81631d638 100644 --- a/code/modules/client/preferences/phobia.dm +++ b/code/modules/client/preferences/phobia.dm @@ -1,7 +1,7 @@ /datum/preference/choiced/phobia category = PREFERENCE_CATEGORY_SECONDARY_FEATURES db_key = "phobia" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER /datum/preference/choiced/phobia/init_possible_values() return GLOB.phobia_types diff --git a/code/modules/client/preferences/pixel_size.dm b/code/modules/client/preferences/pixel_size.dm index c22affec9b833..4db8ded6fcec0 100644 --- a/code/modules/client/preferences/pixel_size.dm +++ b/code/modules/client/preferences/pixel_size.dm @@ -1,7 +1,7 @@ /datum/preference/numeric/pixel_size category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "pixel_size" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER minimum = 0 maximum = 3 diff --git a/code/modules/client/preferences/playtime_reward_cloak.dm b/code/modules/client/preferences/playtime_reward_cloak.dm index aa2af68da0171..e17c879556941 100644 --- a/code/modules/client/preferences/playtime_reward_cloak.dm +++ b/code/modules/client/preferences/playtime_reward_cloak.dm @@ -3,7 +3,7 @@ /// If true, it allows the user to get a cool looking roundstart cloak. /datum/preference/toggle/playtime_reward_cloak category = PREFERENCE_CATEGORY_NON_CONTEXTUAL - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER db_key = "playtime_reward_cloak" /datum/preference/toggle/playtime_reward_cloak/is_accessible(datum/preferences/preferences) diff --git a/code/modules/client/preferences/preferred_map.dm b/code/modules/client/preferences/preferred_map.dm index 2cbedc1a4a288..d3195f8813123 100644 --- a/code/modules/client/preferences/preferred_map.dm +++ b/code/modules/client/preferences/preferred_map.dm @@ -2,7 +2,7 @@ /datum/preference/choiced/preferred_map category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "preferred_map" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER /datum/preference/choiced/preferred_map/init_possible_values() var/list/maps = list() diff --git a/code/modules/client/preferences/random.dm b/code/modules/client/preferences/random.dm index b3e45666d6e97..083c95b9a02e0 100644 --- a/code/modules/client/preferences/random.dm +++ b/code/modules/client/preferences/random.dm @@ -1,7 +1,7 @@ /datum/preference/choiced/random_body category = PREFERENCE_CATEGORY_NON_CONTEXTUAL db_key = "random_body" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER can_randomize = FALSE /datum/preference/choiced/random_body/apply_to_human(mob/living/carbon/human/target, value) @@ -20,7 +20,7 @@ /datum/preference/toggle/random_hardcore category = PREFERENCE_CATEGORY_NON_CONTEXTUAL db_key = "random_hardcore" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER can_randomize = FALSE default_value = FALSE @@ -36,7 +36,7 @@ /datum/preference/choiced/random_name category = PREFERENCE_CATEGORY_NON_CONTEXTUAL db_key = "random_name" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER can_randomize = FALSE /datum/preference/choiced/random_name/apply_to_human(mob/living/carbon/human/target, value) diff --git a/code/modules/client/preferences/runechat.dm b/code/modules/client/preferences/runechat.dm index d30e7cc951f8f..ed27fe4f4cd63 100644 --- a/code/modules/client/preferences/runechat.dm +++ b/code/modules/client/preferences/runechat.dm @@ -1,22 +1,22 @@ /datum/preference/toggle/enable_runechat category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "chat_on_map" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER /datum/preference/toggle/enable_runechat_non_mobs category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "see_chat_non_mob" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER /datum/preference/toggle/see_rc_emotes category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "see_rc_emotes" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER /datum/preference/numeric/max_chat_length category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "max_chat_length" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER minimum = 1 maximum = CHAT_MESSAGE_MAX_LENGTH diff --git a/code/modules/client/preferences/scaling_method.dm b/code/modules/client/preferences/scaling_method.dm index 66a0fdb623140..920400d64c6df 100644 --- a/code/modules/client/preferences/scaling_method.dm +++ b/code/modules/client/preferences/scaling_method.dm @@ -2,7 +2,7 @@ /datum/preference/choiced/scaling_method category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "scaling_method" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER /datum/preference/choiced/scaling_method/create_default_value() return SCALING_METHOD_DISTORT diff --git a/code/modules/client/preferences/screentips.dm b/code/modules/client/preferences/screentips.dm index e86bf108db58c..068f2b86e4cca 100644 --- a/code/modules/client/preferences/screentips.dm +++ b/code/modules/client/preferences/screentips.dm @@ -1,7 +1,7 @@ /datum/preference/toggle/enable_screentips category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "screentip_pref" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER /datum/preference/toggle/enable_screentips/apply_to_client(client/client, value) client.mob?.hud_used?.screentips_enabled = value @@ -9,7 +9,7 @@ /datum/preference/color/screentip_color category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "screentip_color" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER /datum/preference/color/screentip_color/apply_to_client(client/client, value) client.mob?.hud_used?.screentip_color = value diff --git a/code/modules/client/preferences/security_department.dm b/code/modules/client/preferences/security_department.dm index a4f0b44c8cde3..9d97c88fe02a8 100644 --- a/code/modules/client/preferences/security_department.dm +++ b/code/modules/client/preferences/security_department.dm @@ -2,7 +2,7 @@ /datum/preference/choiced/security_department category = PREFERENCE_CATEGORY_NON_CONTEXTUAL can_randomize = FALSE - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER db_key = "prefered_security_department" // This is what that #warn wants you to remove :) diff --git a/code/modules/client/preferences/skin_tone.dm b/code/modules/client/preferences/skin_tone.dm index 3bbffc25cc106..ab230af45ca41 100644 --- a/code/modules/client/preferences/skin_tone.dm +++ b/code/modules/client/preferences/skin_tone.dm @@ -1,6 +1,6 @@ /datum/preference/choiced/skin_tone category = PREFERENCE_CATEGORY_SECONDARY_FEATURES - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER db_key = "skin_tone" /datum/preference/choiced/skin_tone/init_possible_values() diff --git a/code/modules/client/preferences/species.dm b/code/modules/client/preferences/species.dm index 40329d666f75f..3a8d61a3ebc67 100644 --- a/code/modules/client/preferences/species.dm +++ b/code/modules/client/preferences/species.dm @@ -1,6 +1,6 @@ /// Species preference /datum/preference/choiced/species - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER db_key = "species" priority = PREFERENCE_PRIORITY_SPECIES randomize_by_default = FALSE diff --git a/code/modules/client/preferences/species_features/basic.dm b/code/modules/client/preferences/species_features/basic.dm index 44ed906d190d2..361e393872469 100644 --- a/code/modules/client/preferences/species_features/basic.dm +++ b/code/modules/client/preferences/species_features/basic.dm @@ -24,7 +24,7 @@ /datum/preference/color_legacy/eye_color db_key = "eye_color" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES relevant_species_trait = EYECOLOR @@ -42,7 +42,7 @@ /datum/preference/choiced/facial_hairstyle db_key = "facial_style_name" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Facial hair" should_generate_icons = TRUE @@ -63,7 +63,7 @@ /datum/preference/color_legacy/facial_hair_color db_key = "facial_hair_color" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES relevant_species_trait = FACEHAIR @@ -72,7 +72,7 @@ /datum/preference/color_legacy/hair_color db_key = "hair_color" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES relevant_species_trait = HAIR @@ -81,7 +81,7 @@ /datum/preference/choiced/hairstyle db_key = "hairstyle_name" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Hairstyle" should_generate_icons = TRUE diff --git a/code/modules/client/preferences/species_features/ethereal.dm b/code/modules/client/preferences/species_features/ethereal.dm index 81bfc419d6938..a9eeac657df0b 100644 --- a/code/modules/client/preferences/species_features/ethereal.dm +++ b/code/modules/client/preferences/species_features/ethereal.dm @@ -1,6 +1,6 @@ /datum/preference/choiced/ethereal_color db_key = "feature_ethcolor" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Ethereal color" should_generate_icons = TRUE diff --git a/code/modules/client/preferences/species_features/felinid.dm b/code/modules/client/preferences/species_features/felinid.dm index 1ac69141b43ba..7482af71c8619 100644 --- a/code/modules/client/preferences/species_features/felinid.dm +++ b/code/modules/client/preferences/species_features/felinid.dm @@ -1,6 +1,6 @@ /datum/preference/choiced/tail_human db_key = "feature_human_tail" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES can_randomize = FALSE relevant_mutant_bodypart = "tail_human" @@ -17,7 +17,7 @@ /datum/preference/choiced/ears db_key = "feature_human_ears" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES can_randomize = FALSE relevant_mutant_bodypart = "ears" diff --git a/code/modules/client/preferences/species_features/lizard.dm b/code/modules/client/preferences/species_features/lizard.dm index 36c84ee5c987c..9a34baa75992c 100644 --- a/code/modules/client/preferences/species_features/lizard.dm +++ b/code/modules/client/preferences/species_features/lizard.dm @@ -29,7 +29,7 @@ /datum/preference/choiced/lizard_body_markings db_key = "feature_lizard_body_markings" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Body markings" should_generate_icons = TRUE @@ -67,7 +67,7 @@ /datum/preference/choiced/lizard_frills db_key = "feature_lizard_frills" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Frills" should_generate_icons = TRUE @@ -80,7 +80,7 @@ /datum/preference/choiced/lizard_horns db_key = "feature_lizard_horns" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Horns" should_generate_icons = TRUE @@ -93,7 +93,7 @@ /datum/preference/choiced/lizard_legs db_key = "feature_lizard_legs" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES relevant_mutant_bodypart = "legs" @@ -105,7 +105,7 @@ /datum/preference/choiced/lizard_snout db_key = "feature_lizard_snout" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Snout" should_generate_icons = TRUE @@ -118,7 +118,7 @@ /datum/preference/choiced/lizard_spines db_key = "feature_lizard_spines" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES relevant_mutant_bodypart = "spines" @@ -130,7 +130,7 @@ /datum/preference/choiced/lizard_tail db_key = "feature_lizard_tail" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES relevant_mutant_bodypart = "tail_lizard" diff --git a/code/modules/client/preferences/species_features/moth.dm b/code/modules/client/preferences/species_features/moth.dm index 9cc6c975d3546..a724dbdf9c013 100644 --- a/code/modules/client/preferences/species_features/moth.dm +++ b/code/modules/client/preferences/species_features/moth.dm @@ -1,6 +1,6 @@ /datum/preference/choiced/moth_antennae db_key = "feature_moth_antennae" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Antennae" should_generate_icons = TRUE @@ -28,7 +28,7 @@ /datum/preference/choiced/moth_markings db_key = "feature_moth_markings" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Body markings" should_generate_icons = TRUE @@ -80,7 +80,7 @@ /datum/preference/choiced/moth_wings db_key = "feature_moth_wings" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Moth wings" should_generate_icons = TRUE diff --git a/code/modules/client/preferences/species_features/mutants.dm b/code/modules/client/preferences/species_features/mutants.dm index 956291548684b..0a1897793aca9 100644 --- a/code/modules/client/preferences/species_features/mutants.dm +++ b/code/modules/client/preferences/species_features/mutants.dm @@ -1,6 +1,6 @@ /datum/preference/color_legacy/mutant_color db_key = "feature_mcolor" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES relevant_species_trait = MUTCOLORS diff --git a/code/modules/client/preferences/tgui.dm b/code/modules/client/preferences/tgui.dm index e685c66cd29c0..54ae4439e9066 100644 --- a/code/modules/client/preferences/tgui.dm +++ b/code/modules/client/preferences/tgui.dm @@ -1,7 +1,7 @@ /datum/preference/toggle/tgui_fancy category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "tgui_fancy" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER /datum/preference/toggle/tgui_fancy/apply_to_client(client/client, value) for (var/datum/tgui/tgui as anything in client.mob?.tgui_open_uis) @@ -11,7 +11,7 @@ /datum/preference/toggle/tgui_lock category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "tgui_lock" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER default_value = FALSE /datum/preference/toggle/tgui_lock/apply_to_client(client/client, value) diff --git a/code/modules/client/preferences/tooltips.dm b/code/modules/client/preferences/tooltips.dm index 0bd8ad5ed7447..c5ca3b632f5db 100644 --- a/code/modules/client/preferences/tooltips.dm +++ b/code/modules/client/preferences/tooltips.dm @@ -1,7 +1,7 @@ /datum/preference/numeric/tooltip_delay category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "tip_delay" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER minimum = 0 maximum = 5000 @@ -12,4 +12,4 @@ /datum/preference/toggle/enable_tooltips category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "enable_tips" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER diff --git a/code/modules/client/preferences/ui_style.dm b/code/modules/client/preferences/ui_style.dm index c9f470c096254..2a2741528182e 100644 --- a/code/modules/client/preferences/ui_style.dm +++ b/code/modules/client/preferences/ui_style.dm @@ -1,7 +1,7 @@ /// UI style preference /datum/preference/choiced/ui_style category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER db_key = "UI_style" should_generate_icons = TRUE diff --git a/code/modules/client/preferences/underwear_color.dm b/code/modules/client/preferences/underwear_color.dm index 348e8153a8902..5c892b022e550 100644 --- a/code/modules/client/preferences/underwear_color.dm +++ b/code/modules/client/preferences/underwear_color.dm @@ -1,6 +1,6 @@ /datum/preference/color_legacy/underwear_color db_key = "underwear_color" - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES /datum/preference/color_legacy/underwear_color/apply_to_human(mob/living/carbon/human/target, value) diff --git a/code/modules/client/preferences/uplink_location.dm b/code/modules/client/preferences/uplink_location.dm index b29b985a03020..6154c33889564 100644 --- a/code/modules/client/preferences/uplink_location.dm +++ b/code/modules/client/preferences/uplink_location.dm @@ -1,6 +1,6 @@ /datum/preference/choiced/uplink_location category = PREFERENCE_CATEGORY_NON_CONTEXTUAL - savefile_identifier = PREFERENCE_CHARACTER + preference_type = PREFERENCE_CHARACTER db_key = "uplink_loc" can_randomize = FALSE diff --git a/code/modules/client/preferences/widescreen.dm b/code/modules/client/preferences/widescreen.dm index f9e7f5fcb52af..e30104fb3b5f4 100644 --- a/code/modules/client/preferences/widescreen.dm +++ b/code/modules/client/preferences/widescreen.dm @@ -1,7 +1,7 @@ /datum/preference/toggle/widescreen category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "widescreenpref" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER /datum/preference/toggle/widescreen/apply_to_client(client/client, value) client.view_size?.setDefault(getScreenSize(value)) diff --git a/code/modules/client/preferences/window_flashing.dm b/code/modules/client/preferences/window_flashing.dm index 2e1b6e98623ce..4cc7d370109ed 100644 --- a/code/modules/client/preferences/window_flashing.dm +++ b/code/modules/client/preferences/window_flashing.dm @@ -2,4 +2,4 @@ /datum/preference/toggle/window_flashing category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "windowflashing" - savefile_identifier = PREFERENCE_PLAYER + preference_type = PREFERENCE_PLAYER diff --git a/code/modules/unit_tests/preferences.dm b/code/modules/unit_tests/preferences.dm index bfffb5ab3ba6c..20fd6bd7ad6d9 100644 --- a/code/modules/unit_tests/preferences.dm +++ b/code/modules/unit_tests/preferences.dm @@ -7,7 +7,7 @@ for (var/preference_type in GLOB.preference_entries) var/datum/preference/preference = GLOB.preference_entries[preference_type] - if (preference.savefile_identifier == PREFERENCE_CHARACTER) + if (preference.preference_type == PREFERENCE_CHARACTER) preference.apply_to_human(human, preference.create_informed_default_value(preferences)) if (istype(preference, /datum/preference/choiced)) @@ -20,7 +20,7 @@ preference.is_valid(100) preference.is_valid(list(1, 2, 3)) -/// Requires all preferences to have a valid, unique savefile_identifier. +/// Requires all preferences to have a valid, unique preference_type. /datum/unit_test/preferences_valid_db_key /datum/unit_test/preferences_valid_db_key/Run() From 7ad6553352a56305d3226fb1e3d016f93d7f0239 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Thu, 30 Mar 2023 05:04:42 -0500 Subject: [PATCH 022/269] Rewrite some of serialization and some compile fixes --- beestation.dme | 5 +- code/__HELPERS/icons.dm | 7 +- code/_globalvars/lists/objects.dm | 2 +- code/controllers/subsystem/atoms.dm | 9 + code/datums/datacore.dm | 14 +- code/modules/antagonists/cult/cult.dm | 2 +- code/modules/antagonists/gang/gang.dm | 5 +- .../antagonists/traitor/datum_traitor.dm | 4 +- code/modules/client/preferences.dm | 89 +++++----- .../modules/client/preferences/_preference.dm | 80 ++++----- code/modules/client/preferences/clothing.dm | 2 +- code/modules/client/preferences_database.dm | 154 ++++++++++++++++++ code/modules/client/verbs/etips.dm | 20 --- code/modules/events/devil.dm | 5 +- code/modules/jobs/job_types/assistant.dm | 2 +- code/modules/ninja/ninja_event.dm | 7 +- code/modules/projectiles/projectile/magic.dm | 5 +- 17 files changed, 284 insertions(+), 128 deletions(-) create mode 100644 code/modules/client/preferences_database.dm delete mode 100644 code/modules/client/verbs/etips.dm diff --git a/beestation.dme b/beestation.dme index 01fec651930cb..f38bfd770af6c 100644 --- a/beestation.dme +++ b/beestation.dme @@ -2125,6 +2125,7 @@ #include "code\modules\client\message.dm" #include "code\modules\client\player_details.dm" #include "code\modules\client\preferences.dm" +#include "code\modules\client\preferences_database.dm" #include "code\modules\client\preferences_menu.dm" #include "code\modules\client\loadout\loadout.dm" #include "code\modules\client\loadout\loadout_accessories.dm" @@ -2180,7 +2181,7 @@ #include "code\modules\client\preferences\middleware\antags.dm" #include "code\modules\client\preferences\middleware\jobs.dm" #include "code\modules\client\preferences\middleware\keybindings.dm" -#include "code\modules\client\preferences\middleware\legacy_toggles.dm" +//#include "code\modules\client\preferences\middleware\legacy_toggles.dm" #include "code\modules\client\preferences\middleware\names.dm" #include "code\modules\client\preferences\middleware\quirks.dm" #include "code\modules\client\preferences\middleware\random.dm" @@ -2191,8 +2192,6 @@ #include "code\modules\client\preferences\species_features\lizard.dm" #include "code\modules\client\preferences\species_features\moth.dm" #include "code\modules\client\preferences\species_features\mutants.dm" -#include "code\modules\client\preferences2\character_save.dm" -#include "code\modules\client\preferences2\preferences2.dm" #include "code\modules\client\verbs\input_box.dm" #include "code\modules\client\verbs\looc.dm" #include "code\modules\client\verbs\ooc.dm" diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index acfb42d875269..878efde64dff3 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -1109,13 +1109,14 @@ GLOBAL_LIST_EMPTY(friendly_animal_types) /// # If you already have a human and need to get its flat icon, call `get_flat_existing_human_icon()` instead. /// For creating consistent icons for human looking simple animals. -/proc/get_flat_human_icon(icon_id, datum/job/J, datum/character_save/CS, dummy_key, showDirs = GLOB.cardinals, outfit_override = null) +/proc/get_flat_human_icon(icon_id, datum/job/J, cs, dummy_key, showDirs = GLOB.cardinals, outfit_override = null) var/static/list/humanoid_icon_cache = list() if(!icon_id || !humanoid_icon_cache[icon_id]) var/mob/living/carbon/human/dummy/body = generate_or_wait_for_human_dummy(dummy_key) - if(CS) - CS.copy_to(body,TRUE,FALSE) + // TODO tgui-prefs + //if(CS) + // CS.copy_to(body,TRUE,FALSE) if(J) J.equip(body, TRUE, FALSE, outfit_override = outfit_override) else if (outfit_override) diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index 44cd6d9a0122f..de4297056a5bc 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -16,7 +16,7 @@ GLOBAL_LIST_EMPTY_TYPED(singularities, /datum/component/singularity) //li GLOBAL_LIST_EMPTY(uploads_list) //list of all silicon uploads GLOBAL_LIST(chemical_reactions_list) //list of all /datum/chemical_reaction datums. Used during chemical reactions -GLOBAL_LIST_INIT(chemical_reagents_list, init_chemical_reagent_list()) //list of all /datum/reagent datums indexed by reagent id. Used by chemistry stuff +GLOBAL_LIST(chemical_reagents_list) //list of all /datum/reagent datums indexed by reagent id. Used by chemistry stuff GLOBAL_LIST_EMPTY(tech_list) //list of all /datum/tech datums indexed by id. GLOBAL_LIST_EMPTY(surgeries_list) //list of all surgeries by name, associated with their path. GLOBAL_LIST_EMPTY(crafting_recipes) //list of all table craft recipes diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm index cfbcfc428dec5..710b76b42a0ff 100644 --- a/code/controllers/subsystem/atoms.dm +++ b/code/controllers/subsystem/atoms.dm @@ -20,6 +20,9 @@ SUBSYSTEM_DEF(atoms) ///initAtom() adds the atom its creating to this list iff InitializeAtoms() has been given a list to populate as an argument var/list/created_atoms + /// Atoms that will be deleted once the subsystem is initialized + var/list/queued_deletions = list() + #ifdef PROFILE_MAPLOAD_INIT_ATOM var/list/mapload_init_times = list() #endif @@ -77,6 +80,12 @@ SUBSYSTEM_DEF(atoms) atoms_to_return += created_atoms created_atoms = null + for (var/queued_deletion in queued_deletions) + qdel(queued_deletion) + + testing("[queued_deletions.len] atoms were queued for deletion.") + queued_deletions.Cut() + #ifdef PROFILE_MAPLOAD_INIT_ATOM rustg_file_write(json_encode(mapload_init_times), "[GLOB.log_directory]/init_times.json") #endif diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index e281ff55d85c1..5241f028ae7e9 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -387,5 +387,15 @@ security_records_out += list(crew_record) return security_records_out -/datum/datacore/proc/get_id_photo(mob/living/carbon/human/human, show_directions = list(SOUTH)) - return get_flat_existing_human_icon(human, show_directions) +/datum/datacore/proc/get_id_photo(mob/living/carbon/human/H, client/C, show_directions = list(SOUTH), humanoverride = FALSE) + var/datum/job/J = SSjob.GetJob(H.mind.assigned_role) + // TODO tgui-prefs + /*var/datum/character_save/CS + if(!C) + C = H.client + if(C) + CS = C.prefs.active_character + if(humanoverride) + CS.pref_species = new /datum/species/human + H.copy_features(CS) + return get_flat_human_icon(null, J, CS, DUMMY_HUMAN_SLOT_MANIFEST, show_directions)*/ diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm index ec98d8ae0bda9..7b6bcdd6319de 100644 --- a/code/modules/antagonists/cult/cult.dm +++ b/code/modules/antagonists/cult/cult.dm @@ -474,7 +474,7 @@ name = "Cultist (Preview only)" uniform = /obj/item/clothing/under/color/black - suit = /obj/item/clothing/suit/hooded/cultrobes/alt + suit = /obj/item/clothing/suit/hooded/cultrobes shoes = /obj/item/clothing/shoes/cult/alt r_hand = /obj/item/melee/blood_magic/stun diff --git a/code/modules/antagonists/gang/gang.dm b/code/modules/antagonists/gang/gang.dm index fe4ec21e2c356..f4ebb14ef881c 100644 --- a/code/modules/antagonists/gang/gang.dm +++ b/code/modules/antagonists/gang/gang.dm @@ -271,8 +271,9 @@ /datum/outfit/gangster name = "Gangster (Preview only)" - uniform = /obj/item/clothing/under/suit/henchmen - back = /obj/item/storage/backpack/henchmen + // TODO TGUI-prefs + uniform = /obj/item/clothing/under/suit/black + back = /obj/item/storage/backpack/clown #define INFLUENCE_SWAG 2 #define INFLUENCE_TERRITORY 0 diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 5a64de2961375..56a3e9bf97438 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -433,10 +433,10 @@ name = "Traitor (Preview only)" uniform = /obj/item/clothing/under/color/grey - suit = /obj/item/clothing/suit/hooded/ablative + suit = /obj/item/clothing/suit/hooded/hoodie/black gloves = /obj/item/clothing/gloves/color/yellow mask = /obj/item/clothing/mask/gas - l_hand = /obj/item/melee/energy/sword + l_hand = /obj/item/melee/transforming/energy/sword r_hand = /obj/item/gun/energy/kinetic_accelerator/crossbow /datum/outfit/traitor/post_equip(mob/living/carbon/human/H, visualsOnly) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index bd73faf11b8ac..bfedb14ecd7ad 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -12,11 +12,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/last_ip var/last_id - /// List of all character saves, with list index being slot ID - var/list/datum/character_save/character_saves = list() - /// Active character, ref to an item in that list - var/datum/character_save/active_character - //game-preferences var/lastchangelog = "" //Saved changlog filesize to detect if there was a change @@ -90,50 +85,42 @@ GLOBAL_LIST_EMPTY(preferences_datums) value_cache = null return ..() -/datum/preferences/New(client/C) - parent = C +/datum/preferences/New(client/parent) + src.parent = parent for (var/middleware_type in subtypesof(/datum/preference_middleware)) middleware += new middleware_type(src) + if(IS_CLIENT_OR_MOCK(parent)) + load_and_save = !is_guest_key(parent.key) + load_path(parent.ckey) + if(load_and_save && !fexists(path)) + try_savefile_type_migration() + unlock_content = !!parent.IsByondMember() + if(unlock_content) + max_save_slots = 8 + else + CRASH("attempted to create a preferences datum without a client or mock!") + load_database() - if(istype(C)) - if(!IS_GUEST_KEY(C.key)) - unlock_content = C.IsByondMember() - if(unlock_content) - set_max_character_slots(8) - else if(!length(key_bindings)) // Guests need default keybinds - key_bindings = deepCopyList(GLOB.keybinding_list_by_key) - + // give them default keybinds and update their movement keys + key_bindings = deep_copy_list(GLOB.default_hotkeys) + key_bindings_by_key = get_key_bindings_by_key(key_bindings) randomise = get_default_randomization() - var/loaded_preferences_successfully = load_from_database() + var/loaded_preferences_successfully = load_preferences() if(loaded_preferences_successfully) - // TODO tgui-prefs - /*if("6030fe461e610e2be3a2c3e75c06067e" in purchased_gear) //MD5 hash of, "extra character slot" - set_max_character_slots(max_usable_slots + 1)*/ - if(load_characters()) // inside this proc is a disgusting SQL query - var/datum/character_save/target_save = character_saves[default_slot] - if(target_save && !target_save.slot_locked) - active_character = target_save - else - active_character = character_saves[1] // Default to first if unavailable + if(load_character()) return - //we couldn't load character data so just randomize the character appearance + name - active_character = character_saves[1] - var/fallback_default_species = CONFIG_GET(string/fallback_default_species) - if(!active_character.pref_species && fallback_default_species != "random") - var/datum/species/spath = GLOB.species_list[fallback_default_species || "human"] - active_character.pref_species = new spath - active_character.randomise() //let's create a random character then - rather than a fat, bald and naked man. - active_character.real_name = active_character.pref_species.random_name(active_character.gender, TRUE) + randomise_appearance_prefs() //let's create a random character then - rather than a fat, bald and naked man. + if(parent) + apply_all_client_preferences() + parent.set_macros() + if(!loaded_preferences_successfully) save_preferences() - save_character(C) //let's save this new random character so it doesn't keep generating new ones. - -/datum/preferences/proc/save_character(client/C) - active_character.save(C) + save_character() //let's save this new random character so it doesn't keep generating new ones. /datum/preferences/ui_interact(mob/user, datum/tgui/ui) // If you leave and come back, re-register the character preview @@ -215,17 +202,14 @@ GLOBAL_LIST_EMPTY(preferences_datums) switch (action) if ("change_slot") - var/numerical_slot = text2num(params["slot"]) - var/datum/character_save/CS = character_saves[numerical_slot] - if(!CS || CS.slot_locked) - return - active_character = CS - default_slot = numerical_slot - tainted_character_profiles = TRUE - // If its fresh, randomise & save it - if(!CS.from_db) - CS.randomise() - CS.save(user.client) + // Save existing character + save_character() + + // SAFETY: `load_character` performs sanitization the slot number + if (!load_character(params["slot"])) + tainted_character_profiles = TRUE + randomise_appearance_prefs() + save_character() for (var/datum/preference_middleware/preference_middleware as anything in middleware) preference_middleware.on_new_character(usr) @@ -346,6 +330,15 @@ GLOBAL_LIST_EMPTY(preferences_datums) return preferences +/// Applies all PREFERENCE_PLAYER preferences +/datum/preferences/proc/apply_all_client_preferences() + for (var/datum/preference/preference as anything in get_preferences_in_priority_order()) + if (preference.prefence_type != PREFERENCE_PLAYER) + continue + + value_cache -= preference.type + preference.apply_to_client(parent, read_preference(preference.type)) + // This is necessary because you can open the set preferences menu before // the atoms SS is done loading. INITIALIZE_IMMEDIATE(/atom/movable/screen/character_preview_view) diff --git a/code/modules/client/preferences/_preference.dm b/code/modules/client/preferences/_preference.dm index 12ad78f9e948f..998e7873c64ea 100644 --- a/code/modules/client/preferences/_preference.dm +++ b/code/modules/client/preferences/_preference.dm @@ -62,7 +62,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) /// Represents an individual preference. /datum/preference - /// The key inside the savefile to use. + /// The key inside the database to use. /// This is also sent to the UI. /// Once you pick this, don't change it. var/db_key @@ -75,7 +75,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) /// Do not instantiate if type matches this. var/abstract_type = /datum/preference - /// What savefile should this preference be read from? + /// What location should this preference be read from? /// Valid values are PREFERENCE_CHARACTER and PREFERENCE_PLAYER. /// See the documentation in [code/__DEFINES/preferences.dm]. var/preference_type @@ -104,7 +104,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) /// Called on the saved input when retrieving. /// Also called by the value sent from the user through UI. Do not trust it. -/// Input is the value inside the savefile, output is to tell other code +/// Input is the value inside the database, output is to tell other code /// what the value is. /// This is useful either for more optimal data saving or for migrating /// older data. @@ -116,21 +116,23 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) CRASH("`deserialize()` was not implemented on [type]!") /// Called on the input while saving. -/// Input is the current value, output is what to save in the savefile. +/// Input is the current value, output is what to save in the database. +/// For PREFERENCE_PLAYER, this will be to a string, for PREFERENCE_CHARACTER, it varies /datum/preference/proc/serialize(input) SHOULD_NOT_SLEEP(TRUE) return input /// Produce a default, potentially random value for when no value for this -/// preference is found in the savefile. +/// preference is found in the database. /// Either this or create_informed_default_value must be overriden by subtypes. +/// For PREFERENCE_PLAYER, this will be from a string, for PREFERENCE_CHARACTER, it varies /datum/preference/proc/create_default_value() SHOULD_NOT_SLEEP(TRUE) SHOULD_CALL_PARENT(FALSE) CRASH("`create_default_value()` was not implemented on [type]!") /// Produce a default, potentially random value for when no value for this -/// preference is found in the savefile. +/// preference is found in the database. /// Unlike create_default_value(), will provide the preferences object if you /// need to use it. /// If not overriden, will call create_default_value() instead. @@ -147,32 +149,55 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) SHOULD_NOT_OVERRIDE(TRUE) return preference_type == PREFERENCE_CHARACTER && can_randomize -/// Given a savefile, return either the saved data or an acceptable default. -/// This will write to the savefile if a value was not found with the new value. -/datum/preference/proc/read(savefile/savefile, datum/preferences/preferences) +/// Given a ckey, return either the saved data or an acceptable default. +/// This will write to the database if a value was not found with the new value. +/datum/preference/proc/read(ckey, datum/preferences/preferences, slot) SHOULD_NOT_OVERRIDE(TRUE) var/value - if (!isnull(savefile)) - READ_FILE(savefile[db_key], value) + if(SSdbcore.IsConnected()) + var/datum/DBQuery/Q + if(preference_type == PREFERENCE_CHARACTER) + Q = SSdbcore.NewQuery( + "SELECT slot, :ptag FROM [format_table_name("characters")] WHERE ckey=:ckey AND slot=:slot", + list("ckey" = ckey, "slot" = slot, "ptag" = db_key) + ) + else + Q = SSdbcore.NewQuery( + "SELECT CAST(preference_tag AS CHAR) AS ptag, preference_value FROM [format_table_name("preferences")] WHERE ckey=:ckey AND ptag=:ptag", + list("ckey" = ckey, "ptag" = db_key) + ) + if(!Q.warn_execute()) + qdel(Q) + return + if(Q.NextRow()) + value = Q.item[2] + qdel(Q) if (isnull(value)) return null else return deserialize(value, preferences) -/// Given a savefile, writes the inputted value. +/// Given a ckey, writes the inputted value to the database /// Returns TRUE for a successful application. /// Return FALSE if it is invalid. -/datum/preference/proc/write(savefile/savefile, value) +/datum/preference/proc/write(ckey, value, slot) SHOULD_NOT_OVERRIDE(TRUE) if (!is_valid(value)) return FALSE - if (!isnull(savefile)) - WRITE_FILE(savefile[db_key], serialize(value)) + if(SSdbcore.IsConnected()) + var/serialized_value = serialize(value) + var/datum/DBQuery/Q + if(preference_type == PREFERENCE_CHARACTER) + Q = SSdbcore.NewQuery("REPLACE INTO [format_table_name("characters")] (ckey, slot, :ptag) VALUES (:ckey, :slot, :pvalue)", list("ckey" = ckey, "slot" = slot, "ptag" = db_key, "pvalue" = serialized_value)) + else + Q = SSdbcore.NewQuery("INSERT INTO [format_table_name("preferences")] (ckey, preference_tag, preference_value) VALUES (:ckey, :ptag, :pvalue) ON DUPLICATE KEY UPDATE preference_value=:pvalue2", list("ckey" = ckey, "ptag" = db_key, "pvalue" = serialized_value, "pvalue2" = serialized_value)) + if(!Q.Execute(async = TRUE)) + qdel(Q) return TRUE @@ -197,24 +222,6 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) SHOULD_CALL_PARENT(FALSE) CRASH("`apply_to_human()` was not implemented for [type]!") -/// Returns which savefile to use for a given savefile identifier -/datum/preferences/proc/get_savefile_for_preference_type(preference_type) - RETURN_TYPE(/savefile) - - if (!parent) - return null - - switch (preference_type) - if (PREFERENCE_CHARACTER) - return character_savefile - if (PREFERENCE_PLAYER) - if (!game_savefile) - game_savefile = new /savefile(path) - game_savefile.cd = "/" - return game_savefile - else - CRASH("Unknown savefile identifier [preference_type]") - /// Read a /datum/preference type and return its value. /// This will write to the savefile if a value was not found with the new value. /datum/preferences/proc/read_preference(preference_type) @@ -232,7 +239,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) if (preference_type in value_cache) return value_cache[preference_type] - var/value = preference_entry.read(get_savefile_for_preference_type(preference_entry.preference_type), src) + var/value = preference_entry.read(parent.ckey, src) if (isnull(value)) value = preference_entry.create_informed_default_value(src) if (write_preference(preference_entry, value)) @@ -246,9 +253,8 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) /// Returns TRUE for a successful preference application. /// Returns FALSE if it is invalid. /datum/preferences/proc/write_preference(datum/preference/preference, preference_value) - var/savefile = get_savefile_for_preference_type(preference.preference_type) var/new_value = preference.deserialize(preference_value, src) - var/success = preference.write(savefile, new_value) + var/success = preference.write(parent.ckey, new_value, selected_slot) if (success) value_cache[preference.type] = new_value return success @@ -261,7 +267,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) return FALSE var/new_value = preference.deserialize(preference_value, src) - var/success = preference.write(null, new_value) + var/success = preference.write(null, new_value, null) if (!success) return FALSE diff --git a/code/modules/client/preferences/clothing.dm b/code/modules/client/preferences/clothing.dm index bef5e3f41df69..ab9006daa80fa 100644 --- a/code/modules/client/preferences/clothing.dm +++ b/code/modules/client/preferences/clothing.dm @@ -26,7 +26,7 @@ /// Backpack preference /datum/preference/choiced/backpack - db_key = "backpack" + db_key = "backbag" preference_type = PREFERENCE_CHARACTER main_feature_name = "Backpack" category = PREFERENCE_CATEGORY_CLOTHING diff --git a/code/modules/client/preferences_database.dm b/code/modules/client/preferences_database.dm new file mode 100644 index 0000000000000..5eeb93b622732 --- /dev/null +++ b/code/modules/client/preferences_database.dm @@ -0,0 +1,154 @@ + + + +/datum/preferences/proc/load_path(ckey, filename="preferences.json") + if(!ckey || !load_and_save) + return + path = "data/player_saves/[ckey[1]]/[ckey]/[filename]" + +/datum/preferences/proc/load_savefile() + if(load_and_save && !path) + CRASH("Attempted to load savefile without first loading a path!") + savefile = new /datum/json_savefile(load_and_save ? path : null) + +/datum/preferences/proc/load_preferences() + apply_all_client_preferences() + + /* //general preferences + lastchangelog = savefile.get_entry("lastchangelog", lastchangelog) + be_special = savefile.get_entry("be_special", be_special) + default_slot = savefile.get_entry("default_slot", default_slot) + chat_toggles = savefile.get_entry("chat_toggles", chat_toggles) + toggles = savefile.get_entry("toggles", toggles) + ignoring = savefile.get_entry("ignoring", ignoring) + + // OOC commendations + hearted_until = savefile.get_entry("hearted_until", hearted_until) + if(hearted_until > world.realtime) + hearted = TRUE + //favorite outfits + favorite_outfits = savefile.get_entry("favorite_outfits", favorite_outfits) + + var/list/parsed_favs = list() + for(var/typetext in favorite_outfits) + var/datum/outfit/path = text2path(typetext) + if(ispath(path)) //whatever typepath fails this check probably doesn't exist anymore + parsed_favs += path + favorite_outfits = unique_list(parsed_favs) + + // Custom hotkeys + key_bindings = savefile.get_entry("key_bindings", key_bindings) + */ +/* + check_keybindings() // this apparently fails every time and overwrites any unloaded prefs with the default values, so don't load anything after this line or it won't actually save + key_bindings_by_key = get_key_bindings_by_key(key_bindings) */ +/* + //Sanitize + lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog)) + default_slot = sanitize_integer(default_slot, 1, max_save_slots, initial(default_slot)) + toggles = sanitize_integer(toggles, 0, (2**24)-1, initial(toggles)) + be_special = sanitize_be_special(SANITIZE_LIST(be_special)) + key_bindings = sanitize_keybindings(key_bindings) + favorite_outfits = SANITIZE_LIST(favorite_outfits) + */ + + return TRUE + +/datum/preferences/proc/save_preferences() + + for (var/preference_type in GLOB.preference_entries) + var/datum/preference/preference = GLOB.preference_entries[preference_type] + if (preference.preference_type != PREFERENCE_PLAYER) + continue + + if (!(preference.type in recently_updated_keys)) + continue + + recently_updated_keys -= preference.type + + if (preference_type in value_cache) + write_preference(preference, preference.serialize(value_cache[preference_type])) + +/* savefile.set_entry("lastchangelog", lastchangelog) + savefile.set_entry("be_special", be_special) + savefile.set_entry("default_slot", default_slot) + savefile.set_entry("toggles", toggles) + savefile.set_entry("chat_toggles", chat_toggles) + savefile.set_entry("ignoring", ignoring) + savefile.set_entry("key_bindings", key_bindings) + savefile.set_entry("hearted_until", (hearted_until > world.realtime ? hearted_until : null)) + savefile.set_entry("favorite_outfits", favorite_outfits) + savefile.save() */ + return TRUE + +/datum/preferences/proc/load_character(slot) + SHOULD_NOT_SLEEP(TRUE) + if(!slot) + slot = default_slot + slot = sanitize_integer(slot, 1, max_save_slots, initial(default_slot)) + if(slot != default_slot) + default_slot = slot + + // Read everything into cache + for (var/preference_type in GLOB.preference_entries) + var/datum/preference/preference = GLOB.preference_entries[preference_type] + if (preference.preference_type != PREFERENCE_CHARACTER) + continue + + value_cache -= preference_type + read_preference(preference_type) + +/* //Character + randomise = save_data?["randomise"] + + //Load prefs + job_preferences = save_data?["job_preferences"] + + //Quirks + all_quirks = save_data?["all_quirks"] + + //try to fix any outdated data if necessary + //preference updating will handle saving the updated data for us. + if(needs_update >= 0) + update_character(needs_update, save_data) //needs_update == savefile_version if we need an update (positive integer) + + //Sanitize + randomise = SANITIZE_LIST(randomise) + job_preferences = SANITIZE_LIST(job_preferences) + all_quirks = SANITIZE_LIST(all_quirks) */ + + //Validate job prefs +/* for(var/j in job_preferences) + if(job_preferences[j] != JP_LOW && job_preferences[j] != JP_MEDIUM && job_preferences[j] != JP_HIGH) + job_preferences -= j + + all_quirks = SSquirks.filter_invalid_quirks(SANITIZE_LIST(all_quirks)) + validate_quirks() */ + + return TRUE + +/datum/preferences/proc/save_character() + SHOULD_NOT_SLEEP(TRUE) + + for (var/datum/preference/preference as anything in get_preferences_in_priority_order()) + if (preference.preference_type != PREFERENCE_CHARACTER) + continue + + if (!(preference.type in recently_updated_keys)) + continue + + recently_updated_keys -= preference.type + + if (preference.type in value_cache) + write_preference(preference, preference.serialize(value_cache[preference.type])) + +/* //Character + save_data["randomise"] = randomise + + //Write prefs + save_data["job_preferences"] = job_preferences + + //Quirks + save_data["all_quirks"] = all_quirks */ + + return TRUE diff --git a/code/modules/client/verbs/etips.dm b/code/modules/client/verbs/etips.dm deleted file mode 100644 index f7e908f7ec042..0000000000000 --- a/code/modules/client/verbs/etips.dm +++ /dev/null @@ -1,20 +0,0 @@ -/client/verb/toggle_tips() - set name = "Toggle Examine Tooltips" - set desc = "Toggles examine hover-over tooltips" - set category = "Preferences" - - prefs.toggles2 ^= PREFTOGGLE_2_ENABLE_TIPS - prefs.save_preferences() - to_chat(usr, "Examine tooltips [(prefs.toggles2 & PREFTOGGLE_2_ENABLE_TIPS) ? "en" : "dis"]abled.") - -/client/verb/change_tip_delay() - set name = "Set Examine Tooltip Delay" - set desc = "Sets the delay in milliseconds before examine tooltips appear" - set category = "Preferences" - - var/indelay = stripped_input(usr, "Enter the tooltip delay in milliseconds (default: 500)", "Enter tooltip delay", "", 10) - indelay = text2num(indelay) - if(usr)//is this what you mean? - prefs.tip_delay = indelay - prefs.save_preferences() - to_chat(usr, "Tooltip delay set to [indelay] milliseconds.") diff --git a/code/modules/events/devil.dm b/code/modules/events/devil.dm index cb27ccce220ff..346554b60dbd2 100644 --- a/code/modules/events/devil.dm +++ b/code/modules/events/devil.dm @@ -46,8 +46,9 @@ var/mob/living/carbon/human/new_devil = new(spawn_loc) if(!spawn_loc) SSjob.SendToLateJoin(new_devil) - var/datum/character_save/CS = new() //Randomize appearance for the devil. - CS.copy_to(new_devil) + // TODO tgui-prefs + //var/datum/character_save/CS = new() //Randomize appearance for the devil. + //CS.copy_to(new_devil) new_devil.dna.update_dna_identity() return new_devil diff --git a/code/modules/jobs/job_types/assistant.dm b/code/modules/jobs/job_types/assistant.dm index 4e72a1b468b85..cf71a91d93f36 100644 --- a/code/modules/jobs/job_types/assistant.dm +++ b/code/modules/jobs/job_types/assistant.dm @@ -56,7 +56,7 @@ Assistant uniform = /obj/item/clothing/under/color/jumpskirt/grey /datum/outfit/job/assistant/consistent - name = "[JOB_NAME_ASSISTANT] - Always Grey" + name = "Assistant - Always Grey" /datum/outfit/job/assistant/consistent/pre_equip(mob/living/carbon/human/H) ..() diff --git a/code/modules/ninja/ninja_event.dm b/code/modules/ninja/ninja_event.dm index 8b59e0257bfe7..e85c11da8dd6a 100644 --- a/code/modules/ninja/ninja_event.dm +++ b/code/modules/ninja/ninja_event.dm @@ -78,8 +78,9 @@ Contents: /proc/create_space_ninja(spawn_loc) var/mob/living/carbon/human/new_ninja = new(spawn_loc) - var/datum/character_save/CS = new()//Randomize appearance for the ninja. - CS.real_name = "[pick(GLOB.ninja_titles)] [pick(GLOB.ninja_names)]" - CS.copy_to(new_ninja) + // TODO tgui-prefs + //var/datum/character_save/CS = new()//Randomize appearance for the ninja. + //CS.real_name = "[pick(GLOB.ninja_titles)] [pick(GLOB.ninja_names)]" + //CS.copy_to(new_ninja) new_ninja.dna.update_dna_identity() return new_ninja diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index 01ea414d4e107..a11344de7710b 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -263,8 +263,9 @@ if(chooseable_races.len) new_mob.set_species(pick(chooseable_races)) - var/datum/character_save/CS = new() //Randomize appearance for the human - CS.copy_to(new_mob, icon_updates=0) + // TODO tgui-prefs + //var/datum/character_save/CS = new() //Randomize appearance for the human + //CS.copy_to(new_mob, icon_updates=0) var/mob/living/carbon/human/H = new_mob H.update_hair() From ed34ca3bab8f873b592565f70cf732a52588d0fc Mon Sep 17 00:00:00 2001 From: itsmeow Date: Fri, 31 Mar 2023 08:26:06 -0500 Subject: [PATCH 023/269] Compile fixes --- beestation.dme | 1 + code/__DEFINES/colors.dm | 2 + code/__HELPERS/type2type.dm | 121 ++++++++++++++++++ .../dynamic/dynamic_rulesets_midround.dm | 16 +++ .../dynamic/dynamic_rulesets_roundstart.dm | 3 - code/game/machinery/computer/arena.dm | 2 +- code/game/machinery/computer/security.dm | 4 +- code/modules/admin/verbs/one_click_antag.dm | 2 +- code/modules/admin/verbs/randomverbs.dm | 10 +- .../antagonists/_common/antag_spawner.dm | 8 +- .../antagonists/changeling/changeling.dm | 12 +- code/modules/antagonists/creep/creep.dm | 2 +- code/modules/antagonists/cult/cult.dm | 3 +- .../antagonists/revolution/revolution.dm | 2 +- code/modules/asset_cache/asset_list_items.dm | 4 +- code/modules/client/preferences.dm | 80 ++++++------ .../modules/client/preferences/_preference.dm | 6 +- .../client/preferences/middleware/antags.dm | 10 +- .../client/preferences/middleware/species.dm | 2 +- code/modules/client/preferences/names.dm | 2 +- code/modules/client/preferences/species.dm | 12 +- .../preferences/species_features/basic.dm | 8 +- code/modules/client/preferences_character.dm | 90 +++++++++++++ code/modules/client/preferences_database.dm | 13 -- code/modules/client/verbs/ooc.dm | 13 +- code/modules/events/operative.dm | 5 +- code/modules/jobs/job_exp.dm | 4 +- code/modules/jobs/job_types/_job.dm | 58 ++++----- code/modules/jobs/job_types/chaplain.dm | 1 + code/modules/mob/dead/observer/observer.dm | 13 +- code/modules/mob/living/carbon/human/human.dm | 18 --- .../carbon/human/species_types/felinid.dm | 2 +- .../carbon/human/species_types/humans.dm | 2 +- .../mob/living/silicon/pai/personality.dm | 16 ++- code/modules/mob/mob_transformation_simple.dm | 3 +- .../file_system/programs/portrait_printer.dm | 8 +- .../wiremod/components/action/light.dm | 10 +- 37 files changed, 384 insertions(+), 184 deletions(-) create mode 100644 code/modules/client/preferences_character.dm diff --git a/beestation.dme b/beestation.dme index f38bfd770af6c..799b4c0c5ac90 100644 --- a/beestation.dme +++ b/beestation.dme @@ -2125,6 +2125,7 @@ #include "code\modules\client\message.dm" #include "code\modules\client\player_details.dm" #include "code\modules\client\preferences.dm" +#include "code\modules\client\preferences_character.dm" #include "code\modules\client\preferences_database.dm" #include "code\modules\client\preferences_menu.dm" #include "code\modules\client\loadout\loadout.dm" diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index 62d8f6a5de379..d13b2f33c95e7 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -48,6 +48,8 @@ #define COLOR_FADED_PINK "#ff80d5" #define COLOR_MAGENTA "#FF00FF" #define COLOR_PURPLE "#800080" +#define COLOR_VIOLET "#B900F7" +#define COLOR_STRONG_VIOLET "#6927C5" #define COLOR_ORANGE "#FF9900" #define COLOR_LIGHT_ORANGE "#ffc44d" #define COLOR_BEIGE "#CEB689" diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index 800628c4b73de..09fef155cba74 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -174,6 +174,73 @@ . = "NONE" return . +/// Converts an RGB color to an HSL color +/proc/rgb2hsl(red, green, blue) + red /= 255;green /= 255;blue /= 255; + var/max = max(red,green,blue) + var/min = min(red,green,blue) + var/range = max-min + + var/hue=0;var/saturation=0;var/lightness=0; + lightness = (max + min)/2 + if(range != 0) + if(lightness < 0.5) + saturation = range/(max+min) + else + saturation = range/(2-max-min) + + var/dred = ((max-red)/(6*max)) + 0.5 + var/dgreen = ((max-green)/(6*max)) + 0.5 + var/dblue = ((max-blue)/(6*max)) + 0.5 + + if(max==red) + hue = dblue - dgreen + else if(max==green) + hue = dred - dblue + (1/3) + else + hue = dgreen - dred + (2/3) + if(hue < 0) + hue++ + else if(hue > 1) + hue-- + + return list(hue, saturation, lightness) + +/// Converts an HSL color to an RGB color +/proc/hsl2rgb(hue, saturation, lightness) + var/red;var/green;var/blue; + if(saturation == 0) + red = lightness * 255 + green = red + blue = red + else + var/a;var/b; + if(lightness < 0.5) + b = lightness*(1+saturation) + else + b = (lightness+saturation) - (saturation*lightness) + a = 2*lightness - b + + red = round(255 * hue2rgb(a, b, hue+(1/3))) + green = round(255 * hue2rgb(a, b, hue)) + blue = round(255 * hue2rgb(a, b, hue-(1/3))) + + return list(red, green, blue) + +/// Converts an ABH color to an RGB color +/proc/hue2rgb(a, b, hue) + if(hue < 0) + hue++ + else if(hue > 1) + hue-- + if(6*hue < 1) + return (a+(b-a)*6*hue) + if(2*hue < 1) + return b + if(3*hue < 2) + return (a+(b-a)*((2/3)-hue)*6) + return a + /// Very ugly, BYOND doesn't support unix time and rounding errors make it really hard to convert it to BYOND time. returns "YYYY-MM-DD" by default /proc/unix2date(timestamp, seperator = "-") @@ -328,6 +395,51 @@ else . = max(0, min(255, 138.5177312231 * log(temp - 10) - 305.0447927307)) +/// Converts a text color like "red" to a hex color ("#FF0000") +/proc/color2hex(color) //web colors + if(!color) + return "#000000" + + switch(color) + if("white") + return "#FFFFFF" + if("black") + return "#000000" + if("gray") + return "#808080" + if("brown") + return "#A52A2A" + if("red") + return "#FF0000" + if("darkred") + return "#8B0000" + if("crimson") + return "#DC143C" + if("orange") + return "#FFA500" + if("yellow") + return "#FFFF00" + if("green") + return "#008000" + if("lime") + return "#00FF00" + if("darkgreen") + return "#006400" + if("cyan") + return "#00FFFF" + if("blue") + return "#0000FF" + if("navy") + return "#000080" + if("teal") + return "#008080" + if("purple") + return "#800080" + if("indigo") + return "#4B0082" + else + return "#FFFFFF" + /** This is a weird one: It returns a list of all var names found in the string. These vars must be in the [var_name] format It's only a proc because it's used in more than one place @@ -361,6 +473,15 @@ Takes a string and a datum. The string is well, obviously the string being check if(var_source.vars.Find(A)) . += A +/// Converts a hex code to a number +/proc/color_hex2num(A) + if(!A || length(A) != length_char(A)) + return 0 + var/R = hex2num(copytext(A, 2, 4)) + var/G = hex2num(copytext(A, 4, 6)) + var/B = hex2num(copytext(A, 6, 8)) + return R+G+B + //word of warning: using a matrix like this as a color value will simplify it back to a string after being set /proc/color_hex2color_matrix(string) var/length = length(string) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index 6d69b0ef72f58..a15174114188f 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -184,7 +184,11 @@ name = "Syndicate Sleeper Agent" midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT antag_datum = /datum/antagonist/traitor +<<<<<<< HEAD role_preference = /datum/role_preference/midround_living/traitor +======= + antag_flag = ROLE_TRAITOR +>>>>>>> 9f075c09c0b (Compile fixes) protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_roles = list(JOB_NAME_CYBORG, JOB_NAME_AI, "Positronic Brain") required_candidates = 1 @@ -239,7 +243,11 @@ name = "Malfunctioning AI" midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY antag_datum = /datum/antagonist/traitor +<<<<<<< HEAD role_preference = /datum/role_preference/midround_living/malfunctioning_ai +======= + antag_flag = ROLE_MALF +>>>>>>> 9f075c09c0b (Compile fixes) enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_SCIENTIST, JOB_NAME_CHEMIST, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CHIEFENGINEER) exclusive_roles = list(JOB_NAME_AI) required_enemies = list(3,3,2,2,2,1,1,1,1,0) @@ -300,7 +308,11 @@ name = "Wizard" midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY antag_datum = /datum/antagonist/wizard +<<<<<<< HEAD role_preference = /datum/role_preference/midround_ghost/wizard +======= + antag_flag = ROLE_WIZARD +>>>>>>> 9f075c09c0b (Compile fixes) enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_RESEARCHDIRECTOR) //RD doesn't believe in magic required_enemies = list(2,2,1,1,1,1,1,0,0,0) required_candidates = 1 @@ -331,7 +343,11 @@ /datum/dynamic_ruleset/midround/from_ghosts/nuclear name = "Nuclear Assault" midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY +<<<<<<< HEAD role_preference = /datum/role_preference/midround_ghost/nuclear_operative +======= + antag_flag = ROLE_OPERATIVE +>>>>>>> 9f075c09c0b (Compile fixes) antag_datum = /datum/antagonist/nukeop enemy_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(3,3,2,2,2,2,1,1,0,0) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index fe487b97e5c55..512f00ae76a2f 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -459,9 +459,6 @@ /datum/dynamic_ruleset/roundstart/nuclear/clown_ops name = "Clown Ops" antag_datum = /datum/antagonist/nukeop/clownop - antag_flag = ROLE_CLOWN_OPERATIVE - antag_preference = ROLE_OPERATIVE - antag_flag_override = ROLE_OPERATIVE antag_leader_datum = /datum/antagonist/nukeop/leader/clownop requirements = list(101,101,101,101,101,101,101,101,101,101) diff --git a/code/game/machinery/computer/arena.dm b/code/game/machinery/computer/arena.dm index 846971b4b7057..4203fc9f23004 100644 --- a/code/game/machinery/computer/arena.dm +++ b/code/game/machinery/computer/arena.dm @@ -191,7 +191,7 @@ if(!isobserver(oldbody)) return var/mob/living/carbon/human/M = new/mob/living/carbon/human(get_turf(spawnpoint)) - oldbody.client.prefs.active_character.copy_to(M) + //TODO tgui-prefs oldbody.client.prefs.active_character.copy_to(M) M.set_species(/datum/species/human) // Could use setting per team M.equipOutfit(outfits[team] ? outfits[team] : default_outfit) M.faction += team //In case anyone wants to add team based stuff to arena special effects diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index 6ff3aa5f5135a..0b6ee5041fe62 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -785,7 +785,7 @@ What a mess.*/ active1.fields["age"] = t1 if("species") if(istype(active1, /datum/data/record)) - var/t1 = input("Select a species", "Species Selection") as null|anything in get_selectable_species() + var/t1 = input("Select a species", "Species Selection") as null|anything in GLOB.roundstart_races if(!canUseSecurityRecordsConsole(usr, t1, a1)) return active1.fields["species"] = t1 @@ -1022,7 +1022,7 @@ What a mess.*/ if(6) R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") if(7) - R.fields["species"] = pick(get_selectable_species()) + R.fields["species"] = pick(GLOB.roundstart_races) if(8) var/datum/data/record/G = pick(GLOB.data_core.general) R.fields["photo_front"] = G.fields["photo_front"] diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm index 198b72d4d3d98..d79e0550cf320 100644 --- a/code/modules/admin/verbs/one_click_antag.dm +++ b/code/modules/admin/verbs/one_click_antag.dm @@ -379,7 +379,7 @@ //Spawn the body var/mob/living/carbon/human/ERTOperative = new ertemplate.mobtype(spawnloc) - chosen_candidate.client.prefs.active_character.copy_to(ERTOperative) + //TODO tgui-prefs chosen_candidate.client.prefs.active_character.copy_to(ERTOperative) ERTOperative.key = chosen_candidate.key log_objective(ERTOperative, missionobj.explanation_text) diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index dc3b449451663..bc3937b4e5ee5 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -1376,12 +1376,12 @@ Traitors and the like can also be revived with the previous role mostly intact. var/dbflags = C.prefs.db_flags var/newstate = FALSE - if(dbflags & DB_FLAG_EXEMPT || C.prefs.job_exempt) - newstate = FALSE - else - newstate = TRUE + //TODO tgui-prefs if(dbflags & DB_FLAG_EXEMPT || C.prefs.job_exempt) + //TODO tgui-prefs newstate = FALSE + //TODO tgui-prefs else + //TODO tgui-prefs newstate = TRUE - C.prefs.job_exempt = newstate + //TODO tgui-prefs C.prefs.job_exempt = newstate if(C.update_flag_db(DB_FLAG_EXEMPT, newstate)) to_chat(usr, "ERROR: Unable to update player flags. Please check logs.") diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm index 483ace1838954..4a5e5f8d32f6f 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -74,7 +74,7 @@ /obj/item/antag_spawner/contract/spawn_antag(client/C, turf/T, kind ,datum/mind/user) new /obj/effect/particle_effect/smoke(T) var/mob/living/carbon/human/M = new/mob/living/carbon/human(T) - C.prefs.active_character.copy_to(M) + //TODO tgui-prefs C.prefs.active_character.copy_to(M) M.key = C.key var/datum/mind/app_mind = M.mind @@ -134,7 +134,7 @@ /obj/item/antag_spawner/nuke_ops/spawn_antag(client/C, turf/T, kind, datum/mind/user) var/mob/living/carbon/human/M = new/mob/living/carbon/human(T) - C.prefs.active_character.copy_to(M) + //TODO tgui-prefs C.prefs.active_character.copy_to(M) M.key = C.key var/datum/antagonist/nukeop/new_op = new() @@ -153,7 +153,7 @@ /obj/item/antag_spawner/nuke_ops/clown/spawn_antag(client/C, turf/T, kind, datum/mind/user) var/mob/living/carbon/human/M = new/mob/living/carbon/human(T) - C.prefs.active_character.copy_to(M) + //TODO tgui-prefs C.prefs.active_character.copy_to(M) M.key = C.key var/datum/antagonist/nukeop/clownop/new_op = new /datum/antagonist/nukeop/clownop() @@ -315,7 +315,7 @@ /obj/item/antag_spawner/gangster/spawn_antag(client/C, turf/T, datum/mind/user) var/mob/living/carbon/human/M = new/mob/living/carbon/human(T) if (C) - C.prefs.active_character.copy_to(M) + //TODO tgui-prefs C.prefs.active_character.copy_to(M) M.key = C.key var/datum/antagonist/gang/alignment = user.has_antag_datum(/datum/antagonist/gang,TRUE) diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm index 5eb476291aeab..82dda4e57ed0a 100644 --- a/code/modules/antagonists/changeling/changeling.dm +++ b/code/modules/antagonists/changeling/changeling.dm @@ -348,10 +348,11 @@ if(isipc(C)) C.set_species(/datum/species/human) var/replacementName = random_unique_name(C.gender) - if(C.client.prefs.active_character.custom_names["human"]) - C.fully_replace_character_name(C.real_name, C.client.prefs.active_character.custom_names["human"]) - else - C.fully_replace_character_name(C.real_name, replacementName) + //TODO tgui-prefs + //if(C.client.prefs.active_character.custom_names["human"]) + // C.fully_replace_character_name(C.real_name, C.client.prefs.active_character.custom_names["human"]) + //else + C.fully_replace_character_name(C.real_name, replacementName) for(var/datum/data/record/E in GLOB.data_core.general) if(E.fields["name"] == C.real_name) E.fields["species"] = "\improper Human" @@ -608,7 +609,8 @@ return parts.Join("
    ") /datum/antagonist/changeling/get_preview_icon() - var/icon/final_icon = render_preview_outfit(/datum/outfit/changeling) + // TODO tgui-prefs + var/icon/final_icon = render_preview_outfit(/datum/outfit/ashwalker) // <--- var/icon/split_icon = render_preview_outfit(/datum/outfit/job/engineer) final_icon.Shift(WEST, world.icon_size / 2) diff --git a/code/modules/antagonists/creep/creep.dm b/code/modules/antagonists/creep/creep.dm index b002677a5d994..3cdc768f4e7d5 100644 --- a/code/modules/antagonists/creep/creep.dm +++ b/code/modules/antagonists/creep/creep.dm @@ -52,7 +52,7 @@ /datum/antagonist/obsessed/get_preview_icon() var/mob/living/carbon/human/dummy/consistent/victim_dummy = new victim_dummy.hair_color = "b96" // Brown - victim_dummy.hairstyle = "Messy" + victim_dummy.hair_style = "Messy" victim_dummy.update_hair() var/icon/obsessed_icon = render_preview_outfit(preview_outfit) diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm index 7b6bcdd6319de..911a4ec898ceb 100644 --- a/code/modules/antagonists/cult/cult.dm +++ b/code/modules/antagonists/cult/cult.dm @@ -6,6 +6,7 @@ antagpanel_category = "Cult" antag_moodlet = /datum/mood_event/cult preview_outfit = /datum/outfit/cultist + var/datum/action/innate/cult/comm/communion = new var/datum/action/innate/cult/mastervote/vote = new var/datum/action/innate/cult/blood_magic/magic = new banning_key = BAN_ROLE_CULTIST @@ -79,7 +80,7 @@ icon.Crop(-15, -15, 48, 48) var/obj/item/melee/cultblade/longsword = new - icon.Blend(icon(longsword.lefthand_file, longsword.inhand_icon_state), ICON_OVERLAY) + icon.Blend(icon(longsword.lefthand_file, longsword.icon_state), ICON_OVERLAY) qdel(longsword) // Move the guy back to the bottom left, 32x32. diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm index 17b13edbcfb70..e10c147dbaa23 100644 --- a/code/modules/antagonists/revolution/revolution.dm +++ b/code/modules/antagonists/revolution/revolution.dm @@ -198,7 +198,7 @@ /datum/antagonist/rev/head/proc/make_assistant_icon(hairstyle) var/mob/living/carbon/human/dummy/consistent/assistant = new - assistant.hairstyle = hairstyle + assistant.hair_style = hairstyle assistant.update_hair() var/icon/assistant_icon = render_preview_outfit(/datum/outfit/job/assistant/consistent, assistant) diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm index f663dce2ba395..8f02c89aa5228 100644 --- a/code/modules/asset_cache/asset_list_items.dm +++ b/code/modules/asset_cache/asset_list_items.dm @@ -505,9 +505,9 @@ assets = list() /datum/asset/simple/portraits/New() - if(!length(SSpersistent_paintings.paintings[tab])) + if(!length(SSpersistence.paintings[tab])) return - for(var/list/portrait as anything in SSpersistent_paintings.paintings[tab]) + for(var/list/portrait as anything in SSpersistence.paintings[tab]) var/png = "data/paintings/[tab]/[portrait["md5"]].png" if(fexists(png)) var/asset_name = "[tab]_[portrait["md5"]]" diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index bfedb14ecd7ad..f1bd3b30c181d 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -3,19 +3,22 @@ GLOBAL_LIST_EMPTY(preferences_datums) /datum/preferences var/client/parent - var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used - // TREAT THIS VAR AS PRIVATE. USE set_max_character_slots() PLEASE - var/max_usable_slots = 3 - - //non-preference stuff - var/muted = 0 + /// Ensures that we always load the last used save, QOL + var/default_slot = 1 + /// The maximum number of slots we're allowed to contain + var/max_save_slots = 3 + + /// Bitflags for communications that are muted + var/muted = NONE + /// Last IP that this client has connected from var/last_ip + /// Last CID that this client has connected from var/last_id - //game-preferences - var/lastchangelog = "" //Saved changlog filesize to detect if there was a change + /// Cached changelog size, to detect new changelogs since last join + var/lastchangelog = "" - //Antag preferences + /// List of ROLE_X that the client wants to be eligible for var/list/be_special = list() //Special role selection /// Custom keybindings. Map of keybind names to keyboard inputs. @@ -73,8 +76,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/list/recently_updated_keys = list() /// A cache of preference entries to values. - /// Used to avoid expensive READ_FILE every time a preference is retrieved. - var/value_cache = list() + /// Used to avoid expensive database query every time a preference is retrieved. + var/list/value_cache = list() /// If set to TRUE, will update character_profiles on the next ui_data tick. var/tainted_character_profiles = FALSE @@ -91,29 +94,28 @@ GLOBAL_LIST_EMPTY(preferences_datums) for (var/middleware_type in subtypesof(/datum/preference_middleware)) middleware += new middleware_type(src) - if(IS_CLIENT_OR_MOCK(parent)) - load_and_save = !is_guest_key(parent.key) - load_path(parent.ckey) - if(load_and_save && !fexists(path)) - try_savefile_type_migration() - unlock_content = !!parent.IsByondMember() - if(unlock_content) - max_save_slots = 8 + if(istype(parent)) + if(!IS_GUEST_KEY(parent.key)) + unlock_content = !!parent.IsByondMember() + if(unlock_content) + max_save_slots = 8 else - CRASH("attempted to create a preferences datum without a client or mock!") - load_database() + CRASH("attempted to create a preferences datum without a client!") // give them default keybinds and update their movement keys - key_bindings = deep_copy_list(GLOB.default_hotkeys) - key_bindings_by_key = get_key_bindings_by_key(key_bindings) - randomise = get_default_randomization() + //key_bindings = GLOB.default_hotkeys.Copy() + //key_bindings_by_key = get_key_bindings_by_key(key_bindings) + //randomise = get_default_randomization() var/loaded_preferences_successfully = load_preferences() if(loaded_preferences_successfully) + if("6030fe461e610e2be3a2c3e75c06067e" in purchased_gear) //MD5 hash of, "extra character slot" + set_max_character_slots(max_usable_slots + 1) if(load_character()) return + // TODO tgui-prefs implement fallback species //we couldn't load character data so just randomize the character appearance + name - randomise_appearance_prefs() //let's create a random character then - rather than a fat, bald and naked man. + //randomise_appearance_prefs() //let's create a random character then - rather than a fat, bald and naked man. if(parent) apply_all_client_preferences() parent.set_macros() @@ -208,7 +210,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) // SAFETY: `load_character` performs sanitization the slot number if (!load_character(params["slot"])) tainted_character_profiles = TRUE - randomise_appearance_prefs() + //randomise_appearance_prefs() save_character() for (var/datum/preference_middleware/preference_middleware as anything in middleware) @@ -330,12 +332,13 @@ GLOBAL_LIST_EMPTY(preferences_datums) return preferences -/// Applies all PREFERENCE_PLAYER preferences +/// Applies all PREFERENCE_PLAYER preferences, ignoring cached values /datum/preferences/proc/apply_all_client_preferences() for (var/datum/preference/preference as anything in get_preferences_in_priority_order()) - if (preference.prefence_type != PREFERENCE_PLAYER) + if (preference.preference_type != PREFERENCE_PLAYER) continue + // Remove the cached value value_cache -= preference.type preference.apply_to_client(parent, read_preference(preference.type)) @@ -455,25 +458,28 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/character_preview_view) return TRUE /datum/preferences/proc/GetQuirkBalance() - var/bal = 0 - for(var/V in active_character.all_quirks) + /*var/bal = 0 + for(var/V in all_quirks) var/datum/quirk/T = SSquirks.quirks[V] bal -= initial(T.value) - return bal + return bal*/ + return 0 /datum/preferences/proc/GetPositiveQuirkCount() - . = 0 - for(var/q in active_character.all_quirks) + /*. = 0 + for(var/q in all_quirks) if(SSquirks.quirk_points[q] > 0) - .++ + .++*/ + return 0 /datum/preferences/proc/validate_quirks() - if(GetQuirkBalance() < 0) - all_quirks = list() + //if(GetQuirkBalance() < 0) + // all_quirks = list() + return /// Sanitizes the preferences, applies the randomization prefs, and then applies the preference to the human mob. /datum/preferences/proc/safe_transfer_prefs_to(mob/living/carbon/human/character, icon_updates = TRUE, is_antag = FALSE) - apply_character_randomization_prefs(is_antag) + //apply_character_randomization_prefs(is_antag) apply_prefs_to(character, icon_updates) /// Applies the given preferences to a human mob. diff --git a/code/modules/client/preferences/_preference.dm b/code/modules/client/preferences/_preference.dm index 998e7873c64ea..7ccb3cbb8492d 100644 --- a/code/modules/client/preferences/_preference.dm +++ b/code/modules/client/preferences/_preference.dm @@ -223,7 +223,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) CRASH("`apply_to_human()` was not implemented for [type]!") /// Read a /datum/preference type and return its value. -/// This will write to the savefile if a value was not found with the new value. +/// This will write to the database with the default value if a value was not found. /datum/preferences/proc/read_preference(preference_type) var/datum/preference/preference_entry = GLOB.preference_entries[preference_type] if (isnull(preference_entry)) @@ -440,13 +440,13 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) abstract_type = /datum/preference/color /datum/preference/color/deserialize(input, datum/preferences/preferences) - return sanitize_color(input) + return input//TODO tgui-prefs sanitize_color(input) /datum/preference/color/create_default_value() return random_color() /datum/preference/color/is_valid(value) - return findtext(value, GLOB.is_color) + return TRUE //TODO tgui-prefs /// Takes an assoc list of names to /datum/sprite_accessory and returns a value /// fit for `/datum/preference/init_possible_values()` diff --git a/code/modules/client/preferences/middleware/antags.dm b/code/modules/client/preferences/middleware/antags.dm index 49ed275f0fdc1..e7b4a0d582530 100644 --- a/code/modules/client/preferences/middleware/antags.dm +++ b/code/modules/client/preferences/middleware/antags.dm @@ -84,12 +84,12 @@ if (isnull(antag_flag)) continue - var/days_needed = preferences.parent?.get_remaining_days( - GLOB.special_roles[antag_flag_override || antag_flag] - ) + //TODO tgui-prefs var/days_needed = preferences.parent?.get_remaining_days( + // GLOB.special_roles[antag_flag_override || antag_flag] + //) - if (days_needed > 0) - antag_days_left[serialize_antag_name(antag_flag)] = days_needed + //if (days_needed > 0) + // antag_days_left[serialize_antag_name(antag_flag)] = days_needed return antag_days_left diff --git a/code/modules/client/preferences/middleware/species.dm b/code/modules/client/preferences/middleware/species.dm index 02efe1e223a5b..111134e3acfcc 100644 --- a/code/modules/client/preferences/middleware/species.dm +++ b/code/modules/client/preferences/middleware/species.dm @@ -14,7 +14,7 @@ /datum/asset/spritesheet/species/create_spritesheets() var/list/to_insert = list() - for (var/species_id in get_selectable_species()) + for (var/species_id in GLOB.roundstart_races) var/datum/species/species_type = GLOB.species_list[species_id] var/mob/living/carbon/human/dummy/consistent/dummy = new diff --git a/code/modules/client/preferences/names.dm b/code/modules/client/preferences/names.dm index 7ca417bf40bbf..00b2375dd1a2d 100644 --- a/code/modules/client/preferences/names.dm +++ b/code/modules/client/preferences/names.dm @@ -129,7 +129,7 @@ group = "religion" /datum/preference/name/religion/create_default_value() - return pick(GLOB.religion_names) + return DEFAULT_RELIGION /datum/preference/name/deity db_key = "deity_name" diff --git a/code/modules/client/preferences/species.dm b/code/modules/client/preferences/species.dm index 3a8d61a3ebc67..d9035e6d5a0d5 100644 --- a/code/modules/client/preferences/species.dm +++ b/code/modules/client/preferences/species.dm @@ -21,7 +21,7 @@ /datum/preference/choiced/species/init_possible_values() var/list/values = list() - for (var/species_id in get_selectable_species()) + for (var/species_id in GLOB.roundstart_races) values += GLOB.species_list[species_id] return values @@ -34,7 +34,7 @@ var/list/food_flags = FOOD_FLAGS - for (var/species_id in get_selectable_species()) + for (var/species_id in GLOB.roundstart_races) var/species_type = GLOB.species_list[species_id] var/datum/species/species = new species_type @@ -42,9 +42,9 @@ if (!(TRAIT_NOHUNGER in species.inherent_traits)) diet = list( - "liked_food" = bitfield2list(species.liked_food, food_flags), - "disliked_food" = bitfield2list(species.disliked_food, food_flags), - "toxic_food" = bitfield2list(species.toxic_food, food_flags), + "liked_food" = bitfield2list(initial(species.mutanttongue.liked_food), food_flags), + "disliked_food" = bitfield2list(initial(species.mutanttongue.disliked_food), food_flags), + "toxic_food" = bitfield2list(initial(species.mutanttongue.toxic_food), food_flags), ) data[species_id] = list( @@ -54,7 +54,7 @@ "use_skintones" = species.use_skintones, "sexes" = species.sexes, - "enabled_features" = species.get_features(), + "enabled_features" = list()//TODO tgui-prefs species.get_features(), ) + diet return data diff --git a/code/modules/client/preferences/species_features/basic.dm b/code/modules/client/preferences/species_features/basic.dm index 361e393872469..d7f7b30fb40a0 100644 --- a/code/modules/client/preferences/species_features/basic.dm +++ b/code/modules/client/preferences/species_features/basic.dm @@ -49,10 +49,10 @@ relevant_species_trait = FACEHAIR /datum/preference/choiced/facial_hairstyle/init_possible_values() - return generate_possible_values_for_sprite_accessories_on_head(GLOB.facial_hairstyles_list) + return generate_possible_values_for_sprite_accessories_on_head(GLOB.facial_hair_styles_list) /datum/preference/choiced/facial_hairstyle/apply_to_human(mob/living/carbon/human/target, value) - target.facial_hairstyle = value + target.facial_hair_style = value /datum/preference/choiced/facial_hairstyle/compile_constant_data() var/list/data = ..() @@ -88,10 +88,10 @@ relevant_species_trait = HAIR /datum/preference/choiced/hairstyle/init_possible_values() - return generate_possible_values_for_sprite_accessories_on_head(GLOB.hairstyles_list) + return generate_possible_values_for_sprite_accessories_on_head(GLOB.hair_styles_list) /datum/preference/choiced/hairstyle/apply_to_human(mob/living/carbon/human/target, value) - target.hairstyle = value + target.hair_style = value /datum/preference/choiced/hairstyle/compile_constant_data() var/list/data = ..() diff --git a/code/modules/client/preferences_character.dm b/code/modules/client/preferences_character.dm new file mode 100644 index 0000000000000..0189fb897b840 --- /dev/null +++ b/code/modules/client/preferences_character.dm @@ -0,0 +1,90 @@ +/datum/preference_character + // Meta Vars // + /// INT: Slot number. Used for internal tracking. The slot number also correspnds to the number of slots in the characters list + var/slot_number = 0 + /// BOOL: Is this slot locked, likely due to not having enough character slots available + var/slot_locked = FALSE + /// STRING: The name of the character. + var/real_name + /// BOOL: If the name should be randomized + var/be_random_name = FALSE + /// BOOL: If the body should be randomized + var/be_random_body = FALSE + /// ENUM: The gender of the character + var/gender = MALE + /// INT: How old the character is + var/age = 30 + /// STRING: What underwear type the character should use + var/underwear = "Nude" + /// STRING: The color of the underwear + var/underwear_color = "000" + /// STRING: What undershirt type the character should use + var/undershirt = "Nude" + /// STRING: What socks type the character should use + var/socks = "Nude" + /// STRING: (Plasmaman) What helmet type the character should spawn with + var/helmet_style = HELMET_DEFAULT + /// STRING: What backpack style the character should spawn with + var/backbag = DBACKPACK + /// STRING: What jumpsuit style the character should spawn with (suit/skirt) + var/jumpsuit_style = PREF_SUIT + /// STRING: What hair style the character should use + var/hair_style = "Bald" + /// STRING: What hair color the character should use + var/hair_color = "000" + /// STRING: What hair gradient color the character should use + var/gradient_color = "000" + /// STRING: What hair gradient style the character should use + var/gradient_style = "None" + /// STRING: What facial hair style the character should use + var/facial_hair_style = "Shaved" + /// STRING: What facial hair color the character should use + var/facial_hair_color = "000" + /// STRING: What skin tone the character should use + var/skin_tone = "caucasian1" + /// STRING: What eye color the character should use + var/eye_color = "000" + /// /datum/species: What species datum the character should spawn as + var/datum/species/pref_species + /// list: A relational list of features the character should spawn with, used for species specific data. + var/list/features = list( + "body_size" = "Normal", + "mcolor" = "FFF", + "ethcolor" = "9c3030", + "tail_lizard" = "Smooth", + "tail_human" = "None", + "snout" = "Round", + "horns" = "None", + "ears" = "None", + "wings" = "None", + "frills" = "None", + "spines" = "None", + "body_markings" = "None", + "legs" = "Normal Legs", + "moth_wings" = "Plain", + "moth_antennae" = "Plain", + "moth_markings" = "None", + "ipc_screen" = "Blue", + "ipc_antenna" = "None", + "ipc_chassis" = "Morpheus Cyberkinetics(Greyscale)", + "insect_type" = "Common Fly", + "apid_antenna" = "Curled", + "apid_stripes" = "Thick", + "apid_headstripes" = "Thick", + "body_model" = MALE + ) + /// list: A relational list of special name types to name values + var/list/custom_names = list() + /// STRING: What AI core display should be used + var/preferred_ai_core_display = "Blue" + /// STRING: What security department assignment is preferred + var/preferred_security_department = SEC_DEPT_RANDOM + /// list: List of all selected quirks + var/list/all_quirks = list() + var/list/job_preferences = list() + /// list: List of all selected loadout equipment + var/list/equipped_gear = list() + /// STRING: What to do when the job you select is not available + var/joblessrole = BERANDOMJOB //defaults to 1 for fewer assistants + /// STRING: Where your uplink should spawn as traitor + var/uplink_spawn_loc = UPLINK_PDA diff --git a/code/modules/client/preferences_database.dm b/code/modules/client/preferences_database.dm index 5eeb93b622732..cf34829336593 100644 --- a/code/modules/client/preferences_database.dm +++ b/code/modules/client/preferences_database.dm @@ -1,16 +1,3 @@ - - - -/datum/preferences/proc/load_path(ckey, filename="preferences.json") - if(!ckey || !load_and_save) - return - path = "data/player_saves/[ckey[1]]/[ckey]/[filename]" - -/datum/preferences/proc/load_savefile() - if(load_and_save && !path) - CRASH("Attempted to load savefile without first loading a path!") - savefile = new /datum/json_savefile(load_and_save ? path : null) - /datum/preferences/proc/load_preferences() apply_all_client_preferences() diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm index 42afece6a650f..6c599b651c797 100644 --- a/code/modules/client/verbs/ooc.dm +++ b/code/modules/client/verbs/ooc.dm @@ -76,9 +76,10 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") mob.log_talk(raw_msg, LOG_OOC) var/keyname = key + var/ooccolor = prefs.read_preference(/datum/preference/color/ooc_color) if(prefs.unlock_content) if(prefs.toggles & PREFTOGGLE_MEMBER_PUBLIC) - keyname = "[icon2html('icons/member_content.dmi', world, "blag")][keyname]" + keyname = "[icon2html('icons/member_content.dmi', world, "blag")][keyname]" //Get client badges var/badge_data = badge_parse(get_badges()) //The linkify span classes and linkify=TRUE below make ooc text get clickable chat href links if you pass in something resembling a url @@ -87,7 +88,6 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") if(holder) if(!holder.fakekey || C.holder) if(check_rights_for(src, R_ADMIN)) - var/ooccolor = prefs.read_preference(/datum/preference/color/ooc_color) to_chat(C, "[badge_data][CONFIG_GET(flag/allow_admin_ooccolor) && ooccolor ? "" :"" ]OOC: [keyname][holder.fakekey ? "/([holder.fakekey])" : ""]: [msg]", allow_linkify = TRUE) else to_chat(C, "[badge_data]OOC: [keyname][holder.fakekey ? "/([holder.fakekey])" : ""]: [msg]") @@ -150,10 +150,11 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") set category = "Fun" GLOB.OOC_COLOR = null +// TODO tgui-prefs /client/verb/colorooc() set name = "Set Your OOC Color" set category = "Preferences" - +/* if(!holder || !check_rights_for(src, R_ADMIN)) if(!is_content_unlocked()) return @@ -163,19 +164,21 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") prefs.ooccolor = sanitize_ooccolor(new_ooccolor) prefs.save_preferences() SSblackbox.record_feedback("tally", "admin_verb", 1, "Set OOC Color") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return + return*/ +//TODO tgui-prefs /client/verb/resetcolorooc() set name = "Reset Your OOC Color" set desc = "Returns your OOC Color to default" set category = "Preferences" - +/* if(!holder || !check_rights_for(src, R_ADMIN)) if(!is_content_unlocked()) return prefs.ooccolor = initial(prefs.ooccolor) prefs.save_preferences() +*/ //Checks admin notice /client/verb/admin_notice() diff --git a/code/modules/events/operative.dm b/code/modules/events/operative.dm index d8366aed3447a..5a57e98f22dda 100644 --- a/code/modules/events/operative.dm +++ b/code/modules/events/operative.dm @@ -24,8 +24,9 @@ return MAP_ERROR var/mob/living/carbon/human/operative = new(pick(spawn_locs)) - var/datum/character_save/CS = new - CS.copy_to(operative) + // TODO tgui-prefs + //var/datum/character_save/CS = new + //CS.copy_to(operative) operative.dna.update_dna_identity() var/datum/mind/Mind = new /datum/mind(selected.key) Mind.assigned_role = "Lone Operative" diff --git a/code/modules/jobs/job_exp.dm b/code/modules/jobs/job_exp.dm index 8596a9af07e2b..f8d03674651d2 100644 --- a/code/modules/jobs/job_exp.dm +++ b/code/modules/jobs/job_exp.dm @@ -18,8 +18,8 @@ GLOBAL_PROTECT(exp_to_update) var/isexempt = C.prefs.db_flags & DB_FLAG_EXEMPT if(isexempt) return 0 - if(C.prefs.job_exempt) - return 0 + //TODO tgui-prefs if(C.prefs.job_exempt) + // return 0 var/my_exp = C.calc_exp_type(get_exp_req_type()) var/job_requirement = get_exp_req_amount() if(my_exp >= job_requirement) diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm index f7f3f0cb0dc57..eb87e6b563760 100644 --- a/code/modules/jobs/job_types/_job.dm +++ b/code/modules/jobs/job_types/_job.dm @@ -106,7 +106,8 @@ if(!ishuman(H)) return - var/mob/living/carbon/human/human = H + // TODO tgui-prefs + /*var/mob/living/carbon/human/human = H var/list/gear_leftovers = list() if(M.client && LAZYLEN(M.client.prefs.active_character.equipped_gear)) for(var/gear in M.client.prefs.active_character.equipped_gear) @@ -169,7 +170,7 @@ continue to_chat(M, "Failed to locate a storage object on your mob, either you spawned with no hands free and no backpack or this is a bug.") - qdel(item) + qdel(item)*/ /datum/job/proc/announce(mob/living/carbon/human/H) if(head_announce) @@ -390,48 +391,32 @@ scandisease.visibility_flags |= HIDDEN_SCANNER H.ForceContractDisease(scandisease) -/// Spawns the mob to be played as, taking into account preferences and the desired spawn point. -/datum/job/proc/get_spawn_mob(client/player_client, atom/spawn_point) - var/mob/living/spawn_instance - if(ispath(spawn_type, /mob/living/silicon/ai)) - // This is unfortunately necessary because of snowflake AI init code. To be refactored. - spawn_instance = new spawn_type(get_turf(spawn_point), null, player_client.mob) - else - spawn_instance = new spawn_type(player_client.mob.loc) - spawn_point.JoinPlayerHere(spawn_instance, TRUE) - spawn_instance.apply_prefs_job(player_client, src) - if(!player_client) - qdel(spawn_instance) - return // Disconnected while checking for the appearance ban. - return spawn_instance - - /// Applies the preference options to the spawning mob, taking the job into account. Assumes the client has the proper mind. /mob/living/proc/apply_prefs_job(client/player_client, datum/job/job) - +// TODO tgui-prefs /mob/living/carbon/human/apply_prefs_job(client/player_client, datum/job/job) - var/fully_randomize = GLOB.current_anonymous_theme || player_client.prefs.should_be_random_hardcore(job, player_client.mob.mind) || is_banned_from(player_client.ckey, "Appearance") + var/fully_randomize = /*GLOB.current_anonymous_theme ||*/ player_client.prefs.should_be_random_hardcore(job, player_client.mob.mind) || is_banned_from(player_client.ckey, "Appearance") if(!player_client) return // Disconnected while checking for the appearance ban. - var/require_human = CONFIG_GET(flag/enforce_human_authority) && (job.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND) + var/require_human = FALSE//CONFIG_GET(flag/enforce_human_authority) && (job.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND) if(fully_randomize) - if(require_human) + /*if(require_human) player_client.prefs.randomise_appearance_prefs(~RANDOMIZE_SPECIES) else - player_client.prefs.randomise_appearance_prefs() + player_client.prefs.randomise_appearance_prefs()*/ player_client.prefs.apply_prefs_to(src) if (require_human) set_species(/datum/species/human) - if(GLOB.current_anonymous_theme) - fully_replace_character_name(null, GLOB.current_anonymous_theme.anonymous_name(src)) + //if(GLOB.current_anonymous_theme) + // fully_replace_character_name(null, GLOB.current_anonymous_theme.anonymous_name(src)) else - var/is_antag = (player_client.mob.mind in GLOB.pre_setup_antags) + var/is_antag = FALSE//(player_client.mob.mind in GLOB.pre_setup_antags) if(require_human) player_client.prefs.randomise["species"] = FALSE player_client.prefs.safe_transfer_prefs_to(src, TRUE, is_antag) @@ -445,21 +430,22 @@ real_name = species.random_name(gender, TRUE) dna.update_dna_identity() - +// TODO tgui-prefs /mob/living/silicon/ai/apply_prefs_job(client/player_client, datum/job/job) - if(GLOB.current_anonymous_theme) - fully_replace_character_name(real_name, GLOB.current_anonymous_theme.anonymous_ai_name(TRUE)) - return + //if(GLOB.current_anonymous_theme) + // fully_replace_character_name(real_name, GLOB.current_anonymous_theme.anonymous_ai_name(TRUE)) + // return apply_pref_name(/datum/preference/name/ai, player_client) // This proc already checks if the player is appearance banned. set_core_display_icon(null, player_client) - +// TODO tgui-prefs /mob/living/silicon/robot/apply_prefs_job(client/player_client, datum/job/job) if(mmi) var/organic_name - if(GLOB.current_anonymous_theme) - organic_name = GLOB.current_anonymous_theme.anonymous_name(src) - else if(player_client.prefs.read_preference(/datum/preference/choiced/random_name) == RANDOM_ENABLED || CONFIG_GET(flag/force_random_names) || is_banned_from(player_client.ckey, "Appearance")) + //if(GLOB.current_anonymous_theme) + // organic_name = GLOB.current_anonymous_theme.anonymous_name(src) + /*else if below */ + if(player_client.prefs.read_preference(/datum/preference/choiced/random_name) == RANDOM_ENABLED || CONFIG_GET(flag/force_random_names) || is_banned_from(player_client.ckey, "Appearance")) if(!player_client) return // Disconnected while checking the appearance ban. @@ -478,5 +464,5 @@ mmi.brainmob.real_name = organic_name //the name of the brain inside the cyborg is the robotized human's name. mmi.brainmob.name = organic_name // If this checks fails, then the name will have been handled during initialization. - if(!GLOB.current_anonymous_theme && player_client.prefs.read_preference(/datum/preference/name/cyborg) != DEFAULT_CYBORG_NAME) - apply_pref_name(/datum/preference/name/cyborg, player_client) + //if(!GLOB.current_anonymous_theme && player_client.prefs.read_preference(/datum/preference/name/cyborg) != DEFAULT_CYBORG_NAME) + // apply_pref_name(/datum/preference/name/cyborg, player_client) diff --git a/code/modules/jobs/job_types/chaplain.dm b/code/modules/jobs/job_types/chaplain.dm index b59565967c94e..b777b9dc4363c 100644 --- a/code/modules/jobs/job_types/chaplain.dm +++ b/code/modules/jobs/job_types/chaplain.dm @@ -43,6 +43,7 @@ return H.mind?.holy_role = HOLY_ROLE_HIGHPRIEST + var/client/player_client = H.client var/new_religion = player_client?.prefs?.read_preference(/datum/preference/name/religion) || DEFAULT_RELIGION var/new_deity = player_client?.prefs?.read_preference(/datum/preference/name/deity) || DEFAULT_DEITY diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index d9593ca5b8012..86a8d2551c14b 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -803,20 +803,21 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp if((!client) || (!client.prefs)) return - if(client.prefs.active_character.be_random_name) - client.prefs.active_character.real_name = random_unique_name(gender) - if(client.prefs.active_character.be_random_body) - client.prefs.active_character.randomise(gender) + // TODO tgui-prefs + //if(client.prefs.active_character.be_random_name) + // client.prefs.active_character.real_name = random_unique_name(gender) + //if(client.prefs.active_character.be_random_body) + // client.prefs.active_character.randomise(gender) var/species_type = client.prefs.read_preference(/datum/preference/choiced/species) var/datum/species/species = new species_type if(HAIR in species.species_traits) - hairstyle = client.prefs.read_preference(/datum/preference/choiced/hairstyle) + hair_style = client.prefs.read_preference(/datum/preference/choiced/hairstyle) hair_color = brighten_color(client.prefs.read_preference(/datum/preference/color_legacy/hair_color)) if(FACEHAIR in species.species_traits) - facial_hairstyle = client.prefs.read_preference(/datum/preference/choiced/facial_hairstyle) + facial_hair_style = client.prefs.read_preference(/datum/preference/choiced/facial_hairstyle) facial_hair_color = brighten_color(client.prefs.read_preference(/datum/preference/color_legacy/facial_hair_color)) qdel(species) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 6fe7a155b458b..f68be65f70582 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1101,24 +1101,6 @@ src.apply_damage(power, BRUTE, def_zone = pick(BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_PRECISE_L_FOOT)) src.Paralyze(10 * power) -/mob/living/carbon/human/proc/copy_features(var/datum/character_save/CS) - dna.features = CS.features - gender = CS.gender - age = CS.age - underwear = CS.underwear - underwear_color = CS.underwear_color - undershirt = CS.undershirt - socks = CS.socks - hair_style = CS.hair_style - hair_color = CS.hair_color - gradient_color = CS.gradient_color - gradient_style = CS.gradient_style - facial_hair_style = CS.facial_hair_style - facial_hair_color = CS.facial_hair_color - skin_tone = CS.skin_tone - eye_color = CS.eye_color - updateappearance(TRUE, TRUE, TRUE) - /mob/living/carbon/human/monkeybrain ai_controller = /datum/ai_controller/monkey diff --git a/code/modules/mob/living/carbon/human/species_types/felinid.dm b/code/modules/mob/living/carbon/human/species_types/felinid.dm index 0cafa73b94f45..d57f5967409f6 100644 --- a/code/modules/mob/living/carbon/human/species_types/felinid.dm +++ b/code/modules/mob/living/carbon/human/species_types/felinid.dm @@ -173,7 +173,7 @@ to_chat(H, "You are no longer a cat.") /datum/species/human/felinid/prepare_human_for_preview(mob/living/carbon/human/human) - human.hairstyle = "Hime Cut" + human.hair_style = "Hime Cut" human.hair_color = "fcc" // pink human.update_hair() diff --git a/code/modules/mob/living/carbon/human/species_types/humans.dm b/code/modules/mob/living/carbon/human/species_types/humans.dm index 1a7236a2b17d7..728ce351717a2 100644 --- a/code/modules/mob/living/carbon/human/species_types/humans.dm +++ b/code/modules/mob/living/carbon/human/species_types/humans.dm @@ -44,6 +44,6 @@ return SPECIES_DEFAULT_SNIFF_SOUND(user) /datum/species/human/prepare_human_for_preview(mob/living/carbon/human/human) - human.hairstyle = "Business Hair" + human.hair_style = "Business Hair" human.hair_color = "b96" // brown human.update_hair() diff --git a/code/modules/mob/living/silicon/pai/personality.dm b/code/modules/mob/living/silicon/pai/personality.dm index 056a46db85426..b57edeecf1d34 100644 --- a/code/modules/mob/living/silicon/pai/personality.dm +++ b/code/modules/mob/living/silicon/pai/personality.dm @@ -17,10 +17,11 @@ return FALSE if(!user.client) return FALSE - user.client.prefs.pai_name = name - user.client.prefs.pai_description = description - user.client.prefs.pai_comment = comments - user.client.prefs.save_preferences() + //TODO tgui-prefs + //user.client.prefs.pai_name = name + //user.client.prefs.pai_description = description + //user.client.prefs.pai_comment = comments + //user.client.prefs.save_preferences() to_chat(usr, "You have saved pAI information.") return TRUE @@ -34,7 +35,8 @@ if(!user.client) return FALSE - name = user.client.prefs.pai_name - description = user.client.prefs.pai_description - comments = user.client.prefs.pai_comment + //TODO tgui-prefs + //name = user.client.prefs.pai_name + //description = user.client.prefs.pai_description + //comments = user.client.prefs.pai_comment return TRUE diff --git a/code/modules/mob/mob_transformation_simple.dm b/code/modules/mob/mob_transformation_simple.dm index 85627464c6076..0d29df68079b3 100644 --- a/code/modules/mob/mob_transformation_simple.dm +++ b/code/modules/mob/mob_transformation_simple.dm @@ -47,7 +47,8 @@ D.updateappearance(mutcolor_update=1, mutations_overlay_update=1) else if(ishuman(M)) var/mob/living/carbon/human/H = M - client.prefs.active_character.copy_to(H) + // TODO tgui-prefs + //client.prefs.active_character.copy_to(H) H.dna.update_dna_identity() if(mind && isliving(M)) diff --git a/code/modules/modular_computers/file_system/programs/portrait_printer.dm b/code/modules/modular_computers/file_system/programs/portrait_printer.dm index 3146e223f0be2..d55ea45b59eba 100644 --- a/code/modules/modular_computers/file_system/programs/portrait_printer.dm +++ b/code/modules/modular_computers/file_system/programs/portrait_printer.dm @@ -23,9 +23,9 @@ /datum/computer_file/program/portrait_printer/ui_data(mob/user) var/list/data = list() - data["library"] = SSpersistent_paintings.paintings["library"] ? SSpersistent_paintings.paintings["library"] : 0 - data["library_secure"] = SSpersistent_paintings.paintings["library_secure"] ? SSpersistent_paintings.paintings["library_secure"] : 0 - data["library_private"] = SSpersistent_paintings.paintings["library_private"] ? SSpersistent_paintings.paintings["library_private"] : 0 //i'm gonna regret this, won't i? + data["library"] = SSpersistence.paintings["library"] ? SSpersistence.paintings["library"] : 0 + data["library_secure"] = SSpersistence.paintings["library_secure"] ? SSpersistence.paintings["library_secure"] : 0 + data["library_private"] = SSpersistence.paintings["library_private"] ? SSpersistence.paintings["library_private"] : 0 //i'm gonna regret this, won't i? return data /datum/computer_file/program/portrait_printer/ui_assets(mob/user) @@ -55,7 +55,7 @@ //canvas printing! var/list/tab2key = list(TAB_LIBRARY = "library", TAB_SECURE = "library_secure", TAB_PRIVATE = "library_private") var/folder = tab2key[params["tab"]] - var/list/current_list = SSpersistent_paintings.paintings[folder] + var/list/current_list = SSpersistence.paintings[folder] var/list/chosen_portrait = current_list[params["selected"]] var/author = chosen_portrait["author"] var/title = chosen_portrait["title"] diff --git a/code/modules/wiremod/components/action/light.dm b/code/modules/wiremod/components/action/light.dm index a192a0444bffd..8e72470defce7 100644 --- a/code/modules/wiremod/components/action/light.dm +++ b/code/modules/wiremod/components/action/light.dm @@ -54,11 +54,11 @@ /obj/item/circuit_component/light/input_received(datum/port/input/port) . = ..() - brightness.set_value(clamp(brightness.value || 0, 0, max_power)) - red.set_value(clamp(red.value, 0, 255)) - blue.set_value(clamp(blue.value, 0, 255)) - green.set_value(clamp(green.value, 0, 255)) - var/list/hsl = rgb2num(rgb(red.value || 0, green.value || 0, blue.value || 0), COLORSPACE_HSL) + brightness.set_input(clamp(brightness.input_value || 0, 0, max_power)) + red.set_input(clamp(red.input_value, 0, 255)) + blue.set_input(clamp(blue.input_value, 0, 255)) + green.set_input(clamp(green.input_value, 0, 255)) + var/list/hsl = rgb2num(rgb(red.input_value || 0, green.input_value || 0, blue.input_value || 0), COLORSPACE_HSL) shell_light_color = rgb(hsl[1], hsl[2], max(min_lightness, hsl[3]), space=COLORSPACE_HSL) if(.) return From b66a14f693367fa1a0d04d526731b6c1544e19f3 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Sun, 2 Apr 2023 15:29:56 -0500 Subject: [PATCH 024/269] It fucking runs, this is amazing --- beestation.dme | 2 +- code/__HELPERS/game.dm | 2 +- code/controllers/subsystem/job.dm | 6 +++--- .../subsystem/processing/quirks.dm | 3 +++ code/datums/chatmessage.dm | 2 +- code/datums/datacore.dm | 4 ++-- code/datums/keybinding/client.dm | 3 ++- code/game/objects/items.dm | 8 +++----- code/game/objects/structures/life_candle.dm | 5 +++-- code/modules/admin/admin_verbs.dm | 2 +- code/modules/admin/secrets.dm | 2 +- code/modules/admin/verbs/randomverbs.dm | 20 ++++++++++--------- code/modules/antagonists/ert/ert.dm | 4 ++-- .../antagonists/traitor/datum_traitor.dm | 5 ++--- code/modules/awaymissions/capture_the_flag.dm | 2 +- code/modules/client/client_procs.dm | 2 +- code/modules/client/loadout/loadout_ooc.dm | 2 +- code/modules/client/preferences.dm | 11 ++++++---- .../modules/client/preferences/_preference.dm | 10 +++++----- code/modules/client/preferences/body_type.dm | 4 +++- code/modules/client/preferences/fps.dm | 4 ++-- .../client/preferences/middleware/antags.dm | 2 +- .../client/preferences/middleware/jobs.dm | 15 +++++++------- .../preferences/middleware/keybindings.dm | 5 +++-- .../client/preferences/middleware/quirks.dm | 6 +++++- .../client/preferences/middleware/random.dm | 4 +++- code/modules/client/preferences/names.dm | 2 +- code/modules/client/preferences/pda.dm | 8 ++++---- code/modules/client/preferences/phobia.dm | 4 +++- .../preferences/playtime_reward_cloak.dm | 20 ------------------- code/modules/client/preferences/random.dm | 4 +++- code/modules/client/preferences/screentips.dm | 18 +++-------------- .../preferences/species_features/moth.dm | 2 +- code/modules/client/verbs/ooc.dm | 2 +- .../jobs/job_types/security_officer.dm | 2 +- .../modules/mob/dead/new_player/new_player.dm | 9 +++++---- .../mob/dead/new_player/preferences_setup.dm | 13 +++++++----- .../carbon/human/species_types/ethereal.dm | 5 +++-- .../carbon/human/species_types/plasmamen.dm | 4 +++- code/modules/mob/mob.dm | 10 ++++++---- .../computers/item/tablet.dm | 4 ++-- .../playtime_reward_cloak.tsx | 7 ------- 42 files changed, 120 insertions(+), 129 deletions(-) delete mode 100644 code/modules/client/preferences/playtime_reward_cloak.dm delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/playtime_reward_cloak.tsx diff --git a/beestation.dme b/beestation.dme index 799b4c0c5ac90..85976b000213f 100644 --- a/beestation.dme +++ b/beestation.dme @@ -2162,7 +2162,6 @@ #include "code\modules\client\preferences\pda.dm" #include "code\modules\client\preferences\phobia.dm" #include "code\modules\client\preferences\pixel_size.dm" -#include "code\modules\client\preferences\playtime_reward_cloak.dm" #include "code\modules\client\preferences\preferred_map.dm" #include "code\modules\client\preferences\random.dm" #include "code\modules\client\preferences\runechat.dm" @@ -2716,6 +2715,7 @@ #include "code\modules\mob\dead\new_player\logout.dm" #include "code\modules\mob\dead\new_player\new_player.dm" #include "code\modules\mob\dead\new_player\poll.dm" +#include "code\modules\mob\dead\new_player\preferences_setup.dm" #include "code\modules\mob\dead\new_player\sprite_accessories.dm" #include "code\modules\mob\dead\observer\login.dm" #include "code\modules\mob\dead\observer\logout.dm" diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 825703bac0ce7..5eaedc9d9b44c 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -499,7 +499,7 @@ var/mob/living/carbon/human/new_character = new//The mob being spawned. SSjob.SendToLateJoin(new_character) - G_found.client.prefs.active_character.copy_to(new_character) + // TODO tgui-prefs G_found.client.prefs.active_character.copy_to(new_character) new_character.dna.update_dna_identity() new_character.key = G_found.key diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index e88344a4099ec..661cc12190e7c 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -170,7 +170,7 @@ SUBSYSTEM_DEF(job) if(player.mind && (job.title in player.mind.restricted_roles)) JobDebug("FOC incompatible with antagonist role, Player: [player]") continue - if(player.client.prefs.active_character.job_preferences[job.title] == level) + if(player.client.prefs.job_preferences[job.title] == level) JobDebug("FOC pass, Player: [player], Level:[level]") candidates += player return candidates @@ -390,7 +390,7 @@ SUBSYSTEM_DEF(job) continue // If the player wants that job on this level, then try give it to him. - if(player.client.prefs.active_character.job_preferences[job.title] == level || (job.gimmick && player.client.prefs.active_character.job_preferences["Gimmick"] == level)) + if(player.client.prefs.job_preferences[job.title] == level || (job.gimmick && player.client.prefs.job_preferences["Gimmick"] == level)) // If the job isn't filled if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1) JobDebug("DO pass, Player: [player], Level:[level], Job:[job.title]") @@ -617,7 +617,7 @@ SUBSYSTEM_DEF(job) if(job.required_playtime_remaining(player.client)) young++ continue - switch(player.client.prefs.active_character.job_preferences[job.title]) + switch(player.client.prefs.job_preferences[job.title]) if(JP_HIGH) high++ if(JP_MEDIUM) diff --git a/code/controllers/subsystem/processing/quirks.dm b/code/controllers/subsystem/processing/quirks.dm index fe6874da4694e..18ff5c044347b 100644 --- a/code/controllers/subsystem/processing/quirks.dm +++ b/code/controllers/subsystem/processing/quirks.dm @@ -37,6 +37,8 @@ PROCESSING_SUBSYSTEM_DEF(quirks) quirk_points[initial(T.name)] = initial(T.value) /datum/controller/subsystem/processing/quirks/proc/AssignQuirks(datum/mind/user, client/cli, spawn_effects) +// TODO tgui-prefs +/* var/bad_quirk_checker = 0 var/list/bad_quirks = list() for(var/V in cli.prefs.active_character.all_quirks) @@ -51,3 +53,4 @@ PROCESSING_SUBSYSTEM_DEF(quirks) cli.prefs.active_character.all_quirks = list() cli.prefs.active_character.save(cli) client_alert(cli, "You have one or more outdated quirks[length(bad_quirks) ? ": [english_list(bad_quirks)]" : ""]. Your eligible quirks are kept at this round, but your character preference has been reset. Please review them at any time.", "Oh, no!") +*/ diff --git a/code/datums/chatmessage.dm b/code/datums/chatmessage.dm index fa3ff7ce3c98d..2f3afc0fbe0f1 100644 --- a/code/datums/chatmessage.dm +++ b/code/datums/chatmessage.dm @@ -458,7 +458,7 @@ /atom/proc/balloon_alert(mob/viewer, text, color = null) if(!viewer?.client) return - switch(viewer.client.prefs.see_balloon_alerts) + switch(viewer.client.prefs.read_preference(/datum/preference/toggle/auto_fit_viewport)) // TODO tgui-prefs show_balloon_alerts if(BALLOON_ALERT_ALWAYS) new /datum/chatmessage/balloon_alert(text, src, viewer, color) if(BALLOON_ALERT_WITH_CHAT) diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index 5241f028ae7e9..b6cb7bc5b8fa6 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -388,9 +388,9 @@ return security_records_out /datum/datacore/proc/get_id_photo(mob/living/carbon/human/H, client/C, show_directions = list(SOUTH), humanoverride = FALSE) - var/datum/job/J = SSjob.GetJob(H.mind.assigned_role) // TODO tgui-prefs - /*var/datum/character_save/CS + /*var/datum/job/J = SSjob.GetJob(H.mind.assigned_role) + var/datum/character_save/CS if(!C) C = H.client if(C) diff --git a/code/datums/keybinding/client.dm b/code/datums/keybinding/client.dm index 90d12e7015957..a5263b6019d8f 100644 --- a/code/datums/keybinding/client.dm +++ b/code/datums/keybinding/client.dm @@ -62,4 +62,5 @@ winset(user, "mapwindow.map", "zoom=[PIXEL_SCALING_4X]") /datum/keybinding/client/zoomin/up(client/user) - winset(user, "mapwindow.map", "zoom=[user.prefs.pixel_size]") + // TODO tgui-prefs + //winset(user, "mapwindow.map", "zoom=[user.prefs.pixel_size]") diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 58270da4d7236..5252f45ff7d0a 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1045,7 +1045,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) /obj/item/MouseEntered(location, control, params) if((item_flags & PICKED_UP || item_flags & IN_STORAGE) && usr.client.prefs.read_preference(/datum/preference/toggle/enable_tooltips) && !QDELETED(src)) - var/timedelay = usr.client.prefs.tip_delay/100 + var/timedelay = usr.client.prefs.read_preference(/datum/preference/numeric/tooltip_delay)/100 var/user = usr tip_timer = addtimer(CALLBACK(src, PROC_REF(openTip), location, control, params, user), timedelay, TIMER_STOPPABLE)//timer takes delay in deciseconds, but the pref is in milliseconds. dividing by 100 converts it. var/mob/living/L = usr @@ -1069,10 +1069,8 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) if(!usr.client?.prefs?.read_preference(/datum/preference/toggle/item_outlines)) return if(!colour) - if(usr.client) - colour = usr.client.prefs.outline_color - if(!colour) - colour = COLOR_BLUE_GRAY + if(usr?.client?.prefs) + colour = usr.client.prefs.read_preference(/datum/preference/color/outline_color) else colour = COLOR_BLUE_GRAY add_filter(HOVER_OUTLINE_FILTER, 1, list(type="outline", size=1, color=colour)) diff --git a/code/game/objects/structures/life_candle.dm b/code/game/objects/structures/life_candle.dm index 76fe98d8ffe48..652c2714918cc 100644 --- a/code/game/objects/structures/life_candle.dm +++ b/code/game/objects/structures/life_candle.dm @@ -79,9 +79,10 @@ body = mind.current if(!body) body = new mob_type(T) - var/mob/ghostie = mind.get_ghost(TRUE) + // TODO tgui-prefs + /*var/mob/ghostie = mind.get_ghost(TRUE) if(ghostie.client?.prefs) - ghostie.client.prefs.active_character.copy_to(body) + ghostie.client.prefs.active_character.copy_to(body)*/ mind.transfer_to(body) else body.forceMove(T) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 84031e4e6548c..ca87c44d44aa7 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -12,7 +12,7 @@ GLOBAL_PROTECT(admin_verbs_default) /client/proc/dsay, /*talk in deadchat using our ckey/fakekey*/ /client/proc/investigate_show, /*various admintools for investigation. Such as a singulo grief-log*/ /client/proc/secrets, /*from useful quick commands, to memes*/ - /client/proc/toggle_hear_radio, /*allows admins to hide all radio output*/ + // TODO tgui-prefs /client/proc/toggle_hear_radio, /*allows admins to hide all radio output*/ /client/proc/reload_admins, /client/proc/reestablish_db_connection, /*reattempt a connection to the database*/ /client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/ diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm index 300a6dd20b81f..2a2d629e42aa3 100644 --- a/code/modules/admin/secrets.dm +++ b/code/modules/admin/secrets.dm @@ -844,7 +844,7 @@ GLOBAL_DATUM_INIT(admin_secrets, /datum/admin_secrets, new) if (length(players)) var/mob/chosen = players[1] if (chosen.client) - chosen.client.prefs.active_character.copy_to(spawnedMob) + // TODO tgui-prefs chosen.client.prefs.active_character.copy_to(spawnedMob) spawnedMob.key = chosen.key players -= chosen if (ishuman(spawnedMob) && ispath(humanoutfit, /datum/outfit)) diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index bc3937b4e5ee5..c2ffcb5ac38d6 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -431,14 +431,16 @@ Traitors and the like can also be revived with the previous role mostly intact. new_character.gender = record_found.fields["gender"] new_character.age = record_found.fields["age"] new_character.hardset_dna(record_found.fields["identity"], record_found.fields["enzymes"], record_found.fields["name"], record_found.fields["blood_type"], new record_found.fields["species"], record_found.fields["features"], null) - else + //else + // TODO tgui-prefs + /* var/datum/character_save/CS = new() CS.randomise() CS.pref_species.random_name(CS.gender, TRUE) CS.copy_to(new_character) - new_character.dna.update_dna_identity() + new_character.dna.update_dna_identity()*/ - new_character.name = new_character.real_name + //new_character.name = new_character.real_name if(G_found.mind && !G_found.mind.active) G_found.mind.transfer_to(new_character) //be careful when doing stuff like this! I've already checked the mind isn't in use @@ -1373,15 +1375,15 @@ Traitors and the like can also be revived with the previous role mostly intact. if(!C.set_db_player_flags()) to_chat(usr, "ERROR: Unable read player flags from database. Please check logs.") - var/dbflags = C.prefs.db_flags + // TODO tgui-prefs var/dbflags = C.prefs.db_flags var/newstate = FALSE - //TODO tgui-prefs if(dbflags & DB_FLAG_EXEMPT || C.prefs.job_exempt) - //TODO tgui-prefs newstate = FALSE - //TODO tgui-prefs else - //TODO tgui-prefs newstate = TRUE + //if(dbflags & DB_FLAG_EXEMPT || C.prefs.job_exempt) + // newstate = FALSE + //else + // newstate = TRUE - //TODO tgui-prefs C.prefs.job_exempt = newstate + //C.prefs.job_exempt = newstate if(C.update_flag_db(DB_FLAG_EXEMPT, newstate)) to_chat(usr, "ERROR: Unable to update player flags. Please check logs.") diff --git a/code/modules/antagonists/ert/ert.dm b/code/modules/antagonists/ert/ert.dm index 528250607b1bf..b60e166976170 100644 --- a/code/modules/antagonists/ert/ert.dm +++ b/code/modules/antagonists/ert/ert.dm @@ -37,8 +37,8 @@ /datum/antagonist/ert/proc/update_name() var/name = pick(name_source) - if (!name) - name = owner.current.client?.prefs.active_character.custom_names["human"] || pick(GLOB.last_names) +// TODO tgui-prefs if (!name) +// name = owner.current.client?.prefs.active_character.custom_names["human"] || pick(GLOB.last_names) owner.current.fully_replace_character_name(owner.current.real_name,"[role] [name]") /datum/antagonist/ert/deathsquad/New() diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 56a3e9bf97438..e0416e768897f 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -440,7 +440,6 @@ r_hand = /obj/item/gun/energy/kinetic_accelerator/crossbow /datum/outfit/traitor/post_equip(mob/living/carbon/human/H, visualsOnly) - var/obj/item/melee/energy/sword/sword = locate() in H.held_items - sword.icon_state = "e_sword_on_red" - sword.worn_icon_state = "e_sword_on_red" + var/obj/item/melee/transforming/energy/sword/sword = locate() in H.held_items + sword.icon_state = "swordred" H.update_inv_hands() diff --git a/code/modules/awaymissions/capture_the_flag.dm b/code/modules/awaymissions/capture_the_flag.dm index 5470795c4c51a..c384a63b05af3 100644 --- a/code/modules/awaymissions/capture_the_flag.dm +++ b/code/modules/awaymissions/capture_the_flag.dm @@ -284,7 +284,7 @@ /obj/machinery/capture_the_flag/proc/spawn_team_member(client/new_team_member) var/mob/living/carbon/human/M = new/mob/living/carbon/human(get_turf(src)) - new_team_member.prefs.active_character.copy_to(M) + // TODO tgui-prefs new_team_member.prefs.active_character.copy_to(M) if(!(M.dna.species.type in allowed_species)) M.set_species(/datum/species/human) //default to human if not whitelisted M.key = new_team_member.key diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index d5a22bd4ae15a..fd0df3d89ce15 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -255,7 +255,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( prefs.last_ip = address //these are gonna be used for banning prefs.last_id = computer_id //these are gonna be used for banning - prefs.handle_donator_items() + // TODO tgui-prefs prefs.handle_donator_items() if(fexists(roundend_report_file())) add_verb(/client/proc/show_previous_roundend_report) diff --git a/code/modules/client/loadout/loadout_ooc.dm b/code/modules/client/loadout/loadout_ooc.dm index 475fc188cfa52..38a1a860a1145 100644 --- a/code/modules/client/loadout/loadout_ooc.dm +++ b/code/modules/client/loadout/loadout_ooc.dm @@ -9,7 +9,7 @@ cost = 10000 /datum/gear/ooc/char_slot/purchase(var/client/C) - C?.prefs?.set_max_character_slots(C.prefs.max_usable_slots + 1) +// TODO tgui-prefs C?.prefs?.set_max_character_slots(C.prefs.max_usable_slots + 1) /datum/gear/ooc/real_antagtoken display_name = "antag token" diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index f1bd3b30c181d..d37eceef97f45 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -7,6 +7,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/default_slot = 1 /// The maximum number of slots we're allowed to contain var/max_save_slots = 3 + /// The current active character slot + var/selected_slot = 1 /// Bitflags for communications that are muted var/muted = NONE @@ -109,8 +111,9 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/loaded_preferences_successfully = load_preferences() if(loaded_preferences_successfully) - if("6030fe461e610e2be3a2c3e75c06067e" in purchased_gear) //MD5 hash of, "extra character slot" - set_max_character_slots(max_usable_slots + 1) + // TODO tgui-prefs + //if("6030fe461e610e2be3a2c3e75c06067e" in purchased_gear) //MD5 hash of, "extra character slot" + // set_max_character_slots(max_usable_slots + 1) if(load_character()) return // TODO tgui-prefs implement fallback species @@ -176,7 +179,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) data["character_profiles"] = create_character_profiles() data["character_preview_view"] = character_preview_view.assigned_map - data["overflow_role"] = SSjob.GetJobType(SSjob.overflow_role).title + data["overflow_role"] = SSjob.GetJob(SSjob.overflow_role).title data["window"] = current_window data["content_unlocked"] = unlock_content @@ -396,7 +399,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/character_preview_view) create_body() else body.wipe_state() - appearance = preferences.render_new_preview_appearance(body) + // TODO tgui-prefs appearance = preferences.render_new_preview_appearance(body) /atom/movable/screen/character_preview_view/proc/create_body() QDEL_NULL(body) diff --git a/code/modules/client/preferences/_preference.dm b/code/modules/client/preferences/_preference.dm index 7ccb3cbb8492d..b8af4eaa50a98 100644 --- a/code/modules/client/preferences/_preference.dm +++ b/code/modules/client/preferences/_preference.dm @@ -308,12 +308,12 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) SHOULD_CALL_PARENT(TRUE) SHOULD_NOT_SLEEP(TRUE) - if (!isnull(relevant_mutant_bodypart) || !isnull(relevant_species_trait)) - var/species_type = preferences.read_preference(/datum/preference/choiced/species) +// if (!isnull(relevant_mutant_bodypart) || !isnull(relevant_species_trait)) +// var/species_type = preferences.read_preference(/datum/preference/choiced/species) - var/datum/species/species = new species_type - if (!(db_key in species.get_features())) - return FALSE +// var/datum/species/species = new species_type +// TODO tgui-prefs if (!(db_key in species.get_features())) +// return FALSE if (!should_show_on_page(preferences.current_window)) return FALSE diff --git a/code/modules/client/preferences/body_type.dm b/code/modules/client/preferences/body_type.dm index b9dbd6e0acaba..f79f8d3470274 100644 --- a/code/modules/client/preferences/body_type.dm +++ b/code/modules/client/preferences/body_type.dm @@ -6,7 +6,8 @@ /datum/preference/choiced/body_type/init_possible_values() return list(MALE, FEMALE) - +// TODO tgui-prefs +/* /datum/preference/choiced/body_type/apply_to_human(mob/living/carbon/human/target, value) if (target.gender != MALE && target.gender != FEMALE) target.body_type = value @@ -19,3 +20,4 @@ var/gender = preferences.read_preference(/datum/preference/choiced/gender) return gender != MALE && gender != FEMALE +*/ diff --git a/code/modules/client/preferences/fps.dm b/code/modules/client/preferences/fps.dm index 4987b429cf4bc..709d45c40e622 100644 --- a/code/modules/client/preferences/fps.dm +++ b/code/modules/client/preferences/fps.dm @@ -7,11 +7,11 @@ maximum = 240 /datum/preference/numeric/fps/apply_to_client(client/client, value) - client.fps = (value < 0) ? RECOMMENDED_FPS : value + client.fps = (value < 0) ? 20 : value /datum/preference/numeric/fps/compile_constant_data() var/list/data = ..() - data["recommended_fps"] = RECOMMENDED_FPS + data["recommended_fps"] = 20 return data diff --git a/code/modules/client/preferences/middleware/antags.dm b/code/modules/client/preferences/middleware/antags.dm index e7b4a0d582530..7cb11544f05bd 100644 --- a/code/modules/client/preferences/middleware/antags.dm +++ b/code/modules/client/preferences/middleware/antags.dm @@ -79,7 +79,7 @@ for (var/datum/dynamic_ruleset/dynamic_ruleset as anything in subtypesof(/datum/dynamic_ruleset)) var/antag_flag = initial(dynamic_ruleset.antag_flag) - var/antag_flag_override = initial(dynamic_ruleset.antag_flag_override) + // var/antag_flag_override = initial(dynamic_ruleset.antag_flag_override) if (isnull(antag_flag)) continue diff --git a/code/modules/client/preferences/middleware/jobs.dm b/code/modules/client/preferences/middleware/jobs.dm index 50e8b4b0d4581..788e767556bb0 100644 --- a/code/modules/client/preferences/middleware/jobs.dm +++ b/code/modules/client/preferences/middleware/jobs.dm @@ -15,7 +15,7 @@ if (isnull(job)) return FALSE - if (job.faction != FACTION_STATION) + if (job.faction != "Station") return FALSE if (!preferences.set_job_preference_level(job, level)) @@ -50,7 +50,8 @@ var/list/job_days_left = list() var/list/job_required_experience = list() - +// TODO tgui_prefs +/* for (var/datum/job/job as anything in SSjob.all_occupations) var/required_playtime_remaining = job.required_playtime_remaining(user.client) if (required_playtime_remaining) @@ -63,7 +64,7 @@ if (!job.player_old_enough(user.client)) job_days_left[job.title] = job.available_in_days(user.client) - +*/ if (job_days_left.len) data["job_days_left"] = job_days_left @@ -74,9 +75,9 @@ /datum/preference_middleware/jobs/proc/get_job_bans(mob/user) var/list/data = list() - - for (var/datum/job/job as anything in SSjob.all_occupations) - if (is_banned_from(user.client?.ckey, job.title)) - data += job.title +// TODO tgui-prefs + //for (var/datum/job/job as anything in SSjob.all_occupations) + // if (is_banned_from(user.client?.ckey, job.title)) + // data += job.title return data diff --git a/code/modules/client/preferences/middleware/keybindings.dm b/code/modules/client/preferences/middleware/keybindings.dm index e51852b408b83..ff64d0df52f94 100644 --- a/code/modules/client/preferences/middleware/keybindings.dm +++ b/code/modules/client/preferences/middleware/keybindings.dm @@ -29,8 +29,9 @@ if (isnull(keybinding)) return FALSE - preferences.key_bindings[keybind_name] = preferences.parent.hotkeys ? keybinding.hotkey_keys : keybinding.classic_keys - preferences.key_bindings_by_key = preferences.get_key_bindings_by_key(preferences.key_bindings) + // TODO tgui-prefs + //preferences.key_bindings[keybind_name] = preferences.parent.hotkeys ? keybinding.hotkey_keys : keybinding.classic_keys + //preferences.key_bindings_by_key = preferences.get_key_bindings_by_key(preferences.key_bindings) preferences.update_static_data(user) diff --git a/code/modules/client/preferences/middleware/quirks.dm b/code/modules/client/preferences/middleware/quirks.dm index f508c204ab5a4..b4b2dc9092478 100644 --- a/code/modules/client/preferences/middleware/quirks.dm +++ b/code/modules/client/preferences/middleware/quirks.dm @@ -1,4 +1,5 @@ /// Middleware to handle quirks +/* /datum/preference_middleware/quirks var/tainted = FALSE @@ -25,7 +26,9 @@ data["selected_quirks"] = get_selected_quirks() return data - +*/ +// TODO tgui-prefs +/* /datum/preference_middleware/quirks/get_constant_data() var/list/quirk_info = list() @@ -87,3 +90,4 @@ selected_quirks += sanitize_css_class_name(quirk) return selected_quirks +*/ diff --git a/code/modules/client/preferences/middleware/random.dm b/code/modules/client/preferences/middleware/random.dm index 716f4c6713a15..60e59e7ec9e38 100644 --- a/code/modules/client/preferences/middleware/random.dm +++ b/code/modules/client/preferences/middleware/random.dm @@ -76,9 +76,11 @@ /// Given randomization flags, will return whether or not this preference should be randomized. /datum/preference/proc/included_in_randomization_flags(randomize_flags) return TRUE - +// TODO tgui_prefs +/* /datum/preference/name/included_in_randomization_flags(randomize_flags) return !!(randomize_flags & RANDOMIZE_NAME) /datum/preference/choiced/species/included_in_randomization_flags(randomize_flags) return !!(randomize_flags & RANDOMIZE_SPECIES) +*/ diff --git a/code/modules/client/preferences/names.dm b/code/modules/client/preferences/names.dm index 00b2375dd1a2d..bd1f3bffdd2f4 100644 --- a/code/modules/client/preferences/names.dm +++ b/code/modules/client/preferences/names.dm @@ -153,4 +153,4 @@ group = "religion" /datum/preference/name/bible/create_default_value() - return DEFAULT_BIBLE + return "The Bible" // TODO tgui-prefs diff --git a/code/modules/client/preferences/pda.dm b/code/modules/client/preferences/pda.dm index 5f658f28676d5..25f1a15277768 100644 --- a/code/modules/client/preferences/pda.dm +++ b/code/modules/client/preferences/pda.dm @@ -8,10 +8,10 @@ return COLOR_OLIVE /// The visual style of a PDA -/datum/preference/choiced/pda_style +/datum/preference/choiced/pda_theme category = PREFERENCE_CATEGORY_GAME_PREFERENCES - db_key = "pda_style" + db_key = "pda_theme" preference_type = PREFERENCE_PLAYER -/datum/preference/choiced/pda_style/init_possible_values() - return GLOB.pda_styles +/datum/preference/choiced/pda_theme/init_possible_values() + return GLOB.ntos_device_themes_default diff --git a/code/modules/client/preferences/phobia.dm b/code/modules/client/preferences/phobia.dm index 5fba81631d638..e1cd258852896 100644 --- a/code/modules/client/preferences/phobia.dm +++ b/code/modules/client/preferences/phobia.dm @@ -2,7 +2,8 @@ category = PREFERENCE_CATEGORY_SECONDARY_FEATURES db_key = "phobia" preference_type = PREFERENCE_CHARACTER - +// TODO tgui-prefs +/* /datum/preference/choiced/phobia/init_possible_values() return GLOB.phobia_types @@ -14,3 +15,4 @@ /datum/preference/choiced/phobia/apply_to_human(mob/living/carbon/human/target, value) return +*/ diff --git a/code/modules/client/preferences/playtime_reward_cloak.dm b/code/modules/client/preferences/playtime_reward_cloak.dm deleted file mode 100644 index e17c879556941..0000000000000 --- a/code/modules/client/preferences/playtime_reward_cloak.dm +++ /dev/null @@ -1,20 +0,0 @@ -/// This can be set to TRUE from the prefs menu only once the user has -/// gained over 5K playtime hours. -/// If true, it allows the user to get a cool looking roundstart cloak. -/datum/preference/toggle/playtime_reward_cloak - category = PREFERENCE_CATEGORY_NON_CONTEXTUAL - preference_type = PREFERENCE_CHARACTER - db_key = "playtime_reward_cloak" - -/datum/preference/toggle/playtime_reward_cloak/is_accessible(datum/preferences/preferences) - if (!..(preferences)) - return FALSE - - return preferences.parent?.is_veteran() - -/datum/preference/toggle/playtime_reward_cloak/apply_to_human(mob/living/carbon/human/target, value) - return - -/// Returns whether the client should receive the gamer cloak -/client/proc/is_veteran() - return get_exp_living(pure_numeric = TRUE) >= PLAYTIME_VETERAN diff --git a/code/modules/client/preferences/random.dm b/code/modules/client/preferences/random.dm index 083c95b9a02e0..e103da5e3508f 100644 --- a/code/modules/client/preferences/random.dm +++ b/code/modules/client/preferences/random.dm @@ -27,12 +27,14 @@ /datum/preference/toggle/random_hardcore/apply_to_human(mob/living/carbon/human/target, value) return +// TODO tgui-prefs +/* /datum/preference/toggle/random_hardcore/is_accessible(datum/preferences/preferences) if (!..(preferences)) return FALSE return preferences.parent.get_exp_living(pure_numeric = TRUE) >= PLAYTIME_HARDCORE_RANDOM - +*/ /datum/preference/choiced/random_name category = PREFERENCE_CATEGORY_NON_CONTEXTUAL db_key = "random_name" diff --git a/code/modules/client/preferences/screentips.dm b/code/modules/client/preferences/screentips.dm index 068f2b86e4cca..e1176b9fa3d56 100644 --- a/code/modules/client/preferences/screentips.dm +++ b/code/modules/client/preferences/screentips.dm @@ -1,18 +1,6 @@ -/datum/preference/toggle/enable_screentips +/datum/preference/color/outline_color category = PREFERENCE_CATEGORY_GAME_PREFERENCES - db_key = "screentip_pref" + db_key = "outline_color" preference_type = PREFERENCE_PLAYER -/datum/preference/toggle/enable_screentips/apply_to_client(client/client, value) - client.mob?.hud_used?.screentips_enabled = value - -/datum/preference/color/screentip_color - category = PREFERENCE_CATEGORY_GAME_PREFERENCES - db_key = "screentip_color" - preference_type = PREFERENCE_PLAYER - -/datum/preference/color/screentip_color/apply_to_client(client/client, value) - client.mob?.hud_used?.screentip_color = value - -/datum/preference/color/screentip_color/create_default_value() - return "#ffd391" +// TODO tgui-prefs diff --git a/code/modules/client/preferences/species_features/moth.dm b/code/modules/client/preferences/species_features/moth.dm index a724dbdf9c013..d99576cb80d08 100644 --- a/code/modules/client/preferences/species_features/moth.dm +++ b/code/modules/client/preferences/species_features/moth.dm @@ -37,7 +37,7 @@ /datum/preference/choiced/moth_markings/init_possible_values() var/list/values = list() - var/icon/moth_body = icon('icons/blanks/32x32.dmi', "nothing") + var/icon/moth_body = icon('icons/effects/effects.dmi', "nothing") moth_body.Blend(icon('icons/mob/moth_wings.dmi', "m_moth_wings_plain_BEHIND"), ICON_OVERLAY) diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm index 6c599b651c797..b78c9c8ba4ec0 100644 --- a/code/modules/client/verbs/ooc.dm +++ b/code/modules/client/verbs/ooc.dm @@ -142,7 +142,7 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") set name = "Set Player OOC Color" set desc = "Modifies player OOC Color" set category = "Fun" - GLOB.OOC_COLOR = sanitize_ooccolor(newColor) + // TODO tgui-prefs GLOB.OOC_COLOR = sanitize_ooccolor(newColor) /client/proc/reset_ooc() set name = "Reset Player OOC Color" diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm index 325cc0eb4e2c6..a33ba0db4af17 100644 --- a/code/modules/jobs/job_types/security_officer.dm +++ b/code/modules/jobs/job_types/security_officer.dm @@ -46,7 +46,7 @@ GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, S // Assign department security var/department if(M?.client?.prefs) - department = player_client.prefs.read_preference(/datum/preference/choiced/security_department) + department = M.client.prefs.read_preference(/datum/preference/choiced/security_department) if(!LAZYLEN(GLOB.available_depts) || department == "None") return else if(department in GLOB.available_depts) diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 0ab01dc502ecd..678e7e8eefd39 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -446,14 +446,15 @@ spawning = TRUE close_spawn_windows() - var/mob/living/carbon/human/H = new(loc) + //var/mob/living/carbon/human/H = new(loc) var/frn = CONFIG_GET(flag/force_random_names) if(!frn) frn = is_banned_from(ckey, "Appearance") if(QDELETED(src)) return - if(frn) + // TODO tgui-prefs +/* if(frn) client.prefs.active_character.randomise() client.prefs.active_character.real_name = client.prefs.active_character.pref_species.random_name(gender,1) client.prefs.active_character.copy_to(H) @@ -470,7 +471,7 @@ new_character = . if(transfer_after) transfer_character() - +*/ /mob/dead/new_player/proc/transfer_character() . = new_character if(.) @@ -518,7 +519,7 @@ var/has_antags = FALSE if(client.prefs.be_special.len > 0) has_antags = TRUE - if(!length(client.prefs.active_character.job_preferences)) + if(!length(client.prefs.job_preferences)) if(!ineligible_for_roles) to_chat(src, "You have no jobs enabled, along with return to lobby if job is unavailable. This makes you ineligible for any round start role, please update your job preferences.") ineligible_for_roles = TRUE diff --git a/code/modules/mob/dead/new_player/preferences_setup.dm b/code/modules/mob/dead/new_player/preferences_setup.dm index 9069823374a07..2cb659b388d77 100644 --- a/code/modules/mob/dead/new_player/preferences_setup.dm +++ b/code/modules/mob/dead/new_player/preferences_setup.dm @@ -21,10 +21,12 @@ if (should_randomize(preference, antag_override)) write_preference(preference, preference.create_random_value(src)) +// TODO tgui_prefs + ///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() - character.hardcore_survival_score = next_hardcore_score ** 1.2 //30 points would be about 60 score + //var/next_hardcore_score = select_hardcore_quirks() + //character.hardcore_survival_score = next_hardcore_score ** 1.2 //30 points would be about 60 score /** @@ -32,7 +34,8 @@ * Returns the new value to be gained with this setup, plus the previously earned score. **/ /datum/preferences/proc/select_hardcore_quirks() - . = 0 +// TODO tgui_prefs +/* . = 0 var/quirk_budget = rand(8, 35) @@ -75,7 +78,7 @@ quirk_budget -= available_hardcore_quirks[picked_quirk] . += available_hardcore_quirks[picked_quirk] available_hardcore_quirks -= picked_quirk - +*/ /// Returns what job is marked as highest /datum/preferences/proc/get_highest_priority_job() var/datum/job/preview_job @@ -103,7 +106,7 @@ if(preview_job) mannequin.job = preview_job.title - mannequin.dress_up_as_job(preview_job, TRUE) + // TODO tgui_prefs mannequin.dress_up_as_job(preview_job, TRUE) COMPILE_OVERLAYS(mannequin) return mannequin.appearance diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm index 592309f15eacc..edecb1879169b 100644 --- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm +++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm @@ -185,9 +185,10 @@ /datum/species/ethereal/get_sniff_sound(mob/living/carbon/user) return SPECIES_DEFAULT_SNIFF_SOUND(user) -/datum/species/ethereal/get_features() +// TODO tgui-prefs +/*/datum/species/ethereal/get_features() var/list/features = ..() features += "feature_ethcolor" - return features + return features*/ diff --git a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm index 4fe66c385e94b..53b1c255f5554 100644 --- a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm +++ b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm @@ -69,6 +69,8 @@ if(!preference_source) return + // TODO tgui-prefs + /* var/path = J.species_outfits?[SPECIES_PLASMAMAN] if (!path) //Somehow we were given a job without a plasmaman suit, use the default one so we don't go in naked! path = /datum/outfit/plasmaman @@ -80,7 +82,7 @@ var/helmet = O.helmet_variants[CS.helmet_style] qdel(H.head) H.equip_to_slot(new helmet, ITEM_SLOT_HEAD) - H.open_internals(H.get_item_for_held_index(2)) + H.open_internals(H.get_item_for_held_index(2))*/ /datum/species/plasmaman/qualifies_for_rank(rank, list/features) if(rank in GLOB.security_positions) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index ae7f63b2eb09d..1f34725819b92 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -285,15 +285,17 @@ return FALSE if (!target.client?.prefs.read_preference(/datum/preference/toggle/enable_runechat_non_mobs)) return FALSE - if(LAZYFIND(visible_message_flags, CHATMESSAGE_EMOTE) && !target.client.prefs.read_preference(/datum/preference/toggle/see_rc_emotes)) - return FALSE + // TODO tgui-prefs + //if(LAZYFIND(visible_message_flags, CHATMESSAGE_EMOTE) && !target.client.prefs.read_preference(/datum/preference/toggle/see_rc_emotes)) + // return FALSE return TRUE /mob/runechat_prefs_check(mob/target, visible_message_flags = NONE) if(!target.client?.prefs.read_preference(/datum/preference/toggle/enable_runechat)) return FALSE - if(LAZYFIND(visible_message_flags, CHATMESSAGE_EMOTE) && !target.client.prefs.read_preference(/datum/preference/toggle/see_rc_emotes)) - return FALSE + // TODO tgui-prefs + /*if(LAZYFIND(visible_message_flags, CHATMESSAGE_EMOTE) && !target.client.prefs.read_preference(/datum/preference/toggle/see_rc_emotes)) + return FALSE*/ return TRUE ///Get the item on the mob in the storage slot identified by the id passed in diff --git a/code/modules/modular_computers/computers/item/tablet.dm b/code/modules/modular_computers/computers/item/tablet.dm index c26c7d280b6fc..ed2cc06134507 100644 --- a/code/modules/modular_computers/computers/item/tablet.dm +++ b/code/modules/modular_computers/computers/item/tablet.dm @@ -363,13 +363,13 @@ equipped = TRUE if(!user.client.prefs) return - var/pref_theme = user.client.prefs.pda_theme + var/pref_theme = user.client.prefs.read_preference(/datum/preference/choiced/pda_theme) if(!theme_locked && !ignore_theme_pref) for(var/key in allowed_themes) // i am going to scream. DM lists stop sucking please if(allowed_themes[key] == pref_theme) device_theme = pref_theme break - classic_color = user.client.prefs.pda_color + classic_color = user.client.prefs.read_preference(/datum/preference/color/pda_color) /obj/item/modular_computer/tablet/pda/update_icon() ..() diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/playtime_reward_cloak.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/playtime_reward_cloak.tsx deleted file mode 100644 index af24a49b7f4df..0000000000000 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/playtime_reward_cloak.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { CheckboxInput, FeatureToggle } from "../base"; - -export const playtime_reward_cloak: FeatureToggle = { - name: "Don gamer cloak", - component: CheckboxInput, -}; - From 92233929a173af5ae17f433e4eb30b5dc450a8d5 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Mon, 3 Apr 2023 01:50:54 -0500 Subject: [PATCH 025/269] Fixes --- code/__DEFINES/preferences.dm | 2 +- code/__DEFINES/subsystems.dm | 1 - code/__DEFINES/traits.dm | 3 --- code/__HELPERS/roundend.dm | 1 - code/_globalvars/lists/objects.dm | 2 +- code/_onclick/hud/hud.dm | 12 ------------ code/controllers/subsystem/processing/quirks.dm | 9 +++------ code/datums/keybinding/client.dm | 3 +-- code/modules/client/preferences.dm | 17 +++++------------ code/modules/client/preferences/_preference.dm | 7 ++++--- .../client/preferences/middleware/jobs.dm | 2 +- .../client/preferences/middleware/quirks.dm | 17 +++++++---------- .../client/preferences/middleware/random.dm | 2 +- code/modules/client/preferences/phobia.dm | 4 ++++ code/modules/client/preferences_menu.dm | 4 ++-- .../mob/dead/new_player/preferences_setup.dm | 6 +++--- 16 files changed, 33 insertions(+), 59 deletions(-) diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm index d41f78f0842ad..a526a0b069199 100644 --- a/code/__DEFINES/preferences.dm +++ b/code/__DEFINES/preferences.dm @@ -29,7 +29,7 @@ #define PREFTOGGLE_RUNECHAT_NONMOBS (1<<22) #define PREFTOGGLE_RUNECHAT_EMOTES (1<<23) -#define TOGGLES_DEFAULT (PREFTOGGLE_SOUND_ADMINHELP|PREFTOGGLE_SOUND_MIDI|PREFTOGGLE_SOUND_AMBIENCE|PREFTOGGLE_SOUND_LOBBY|PREFTOGGLE_MEMBER_PUBLIC|PREFTOGGLE_INTENT_STYLE|PREFTOGGLE_SOUND_INSTRUMENTS|PREFTOGGLE_SOUND_SHIP_AMBIENCE|PREFTOGGLE_SOUND_PRAYERS|PREFTOGGLE_SOUND_ANNOUNCEMENTS|PREFTOGGLE_OUTLINE_ENABLED|PREFTOGGLE_RUNECHAT_GLOBAL|PREFTOGGLE_RUNECHAT_NONMOBS|PREFTOGGLE_RUNECHAT_EMOTES) +#define TOGGLES_DEFAULT (PREFTOGGLE_SOUND_ADMINHELP|PREFTOGGLE_SOUND_MIDI|PREFTOGGLE_SOUND_AMBIENCE/*|TODO tgui-prefs PREFTOGGLE_SOUND_LOBBY*/|PREFTOGGLE_MEMBER_PUBLIC|PREFTOGGLE_INTENT_STYLE|PREFTOGGLE_SOUND_INSTRUMENTS|PREFTOGGLE_SOUND_SHIP_AMBIENCE|PREFTOGGLE_SOUND_PRAYERS|PREFTOGGLE_SOUND_ANNOUNCEMENTS|PREFTOGGLE_OUTLINE_ENABLED|PREFTOGGLE_RUNECHAT_GLOBAL|PREFTOGGLE_RUNECHAT_NONMOBS|PREFTOGGLE_RUNECHAT_EMOTES) // You CANNOT go above 1<<23 in BYOND due to integer limits // Please add subsequent ones as PREFTOGGLE_2_[name] diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 258cf4b307479..759ee395807cc 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -142,7 +142,6 @@ #define INIT_ORDER_DEFAULT 0 #define INIT_ORDER_AIR -1 #define INIT_ORDER_PERSISTENCE -2 //before assets because some assets take data from SSPersistence -#define INIT_ORDER_PERSISTENT_PAINTINGS -3 // Assets relies on this #define INIT_ORDER_ASSETS -4 #define INIT_ORDER_ICON_SMOOTHING -5 #define INIT_ORDER_OVERLAY -6 diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index a0926be3f8987..ddf2d8a2770e4 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -417,6 +417,3 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_SUIT_SENSORS "suit_sensors" ///Mob is tracked by nanites, and on glob suit sensors list #define TRAIT_NANITE_SENSORS "nanite_sensors" - -/// This human wants to see the color of their glasses, for some reason -#define TRAIT_SEE_GLASS_COLORS "see_glass_colors" diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index 01a209f60536a..b012dc4f73c63 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -292,7 +292,6 @@ CHECK_TICK SSdbcore.SetRoundEnd() - //Collects persistence features if(mode.allow_persistence_save) SSpersistence.CollectData() diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index de4297056a5bc..cd99d734c3663 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -16,7 +16,7 @@ GLOBAL_LIST_EMPTY_TYPED(singularities, /datum/component/singularity) //li GLOBAL_LIST_EMPTY(uploads_list) //list of all silicon uploads GLOBAL_LIST(chemical_reactions_list) //list of all /datum/chemical_reaction datums. Used during chemical reactions -GLOBAL_LIST(chemical_reagents_list) //list of all /datum/reagent datums indexed by reagent id. Used by chemistry stuff +GLOBAL_LIST(chemical_reagents_list) //list of all /datum/reagent datums indexed by reagent id. Used by chemistry stuff GLOBAL_LIST_EMPTY(tech_list) //list of all /datum/tech datums indexed by id. GLOBAL_LIST_EMPTY(surgeries_list) //list of all surgeries by name, associated with their path. GLOBAL_LIST_EMPTY(crafting_recipes) //list of all table craft recipes diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index 5a3b7a888ea52..7bb19c5db1fb2 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -55,18 +55,6 @@ GLOBAL_LIST_INIT(available_ui_styles, list( var/list/atom/movable/plane_master_controller/plane_master_controllers = list() var/list/team_finder_arrows = list() - /// Whether or not screentips are enabled. - /// This is updated by the preference for cheaper reads than would be - /// had with a proc call, especially on one of the hottest procs in the - /// game (MouseEntered). - var/screentips_enabled = TRUE - - /// The color to use for the screentips. - /// This is updated by the preference for cheaper reads than would be - /// had with a proc call, especially on one of the hottest procs in the - /// game (MouseEntered). - var/screentip_color - var/atom/movable/screen/movable/action_button/hide_toggle/hide_actions_toggle var/action_buttons_hidden = FALSE diff --git a/code/controllers/subsystem/processing/quirks.dm b/code/controllers/subsystem/processing/quirks.dm index 18ff5c044347b..3f06f289c9a19 100644 --- a/code/controllers/subsystem/processing/quirks.dm +++ b/code/controllers/subsystem/processing/quirks.dm @@ -37,11 +37,9 @@ PROCESSING_SUBSYSTEM_DEF(quirks) quirk_points[initial(T.name)] = initial(T.value) /datum/controller/subsystem/processing/quirks/proc/AssignQuirks(datum/mind/user, client/cli, spawn_effects) -// TODO tgui-prefs -/* var/bad_quirk_checker = 0 var/list/bad_quirks = list() - for(var/V in cli.prefs.active_character.all_quirks) + for(var/V in cli.prefs.all_quirks) var/datum/quirk/Q = quirks[V] if(Q) user.add_quirk(Q, spawn_effects) @@ -50,7 +48,6 @@ PROCESSING_SUBSYSTEM_DEF(quirks) stack_trace("Invalid quirk \"[V]\" in client [cli.ckey] preferences. the game has reset their quirks automatically.") bad_quirks += V if(bad_quirk_checker > 0 || length(bad_quirks)) // negative & zero value = calculation good / positive quirk value = something's wrong - cli.prefs.active_character.all_quirks = list() - cli.prefs.active_character.save(cli) + cli.prefs.all_quirks = list() + //TODO tgui-prefs write_preference() client_alert(cli, "You have one or more outdated quirks[length(bad_quirks) ? ": [english_list(bad_quirks)]" : ""]. Your eligible quirks are kept at this round, but your character preference has been reset. Please review them at any time.", "Oh, no!") -*/ diff --git a/code/datums/keybinding/client.dm b/code/datums/keybinding/client.dm index a5263b6019d8f..f86427c6ca144 100644 --- a/code/datums/keybinding/client.dm +++ b/code/datums/keybinding/client.dm @@ -62,5 +62,4 @@ winset(user, "mapwindow.map", "zoom=[PIXEL_SCALING_4X]") /datum/keybinding/client/zoomin/up(client/user) - // TODO tgui-prefs - //winset(user, "mapwindow.map", "zoom=[user.prefs.pixel_size]") + winset(user, "mapwindow.map", "zoom=[user.prefs.read_preference(/datum/preference/numeric/pixel_size)]") diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index d37eceef97f45..07797708cb6e8 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -118,7 +118,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) return // TODO tgui-prefs implement fallback species //we couldn't load character data so just randomize the character appearance + name - //randomise_appearance_prefs() //let's create a random character then - rather than a fat, bald and naked man. + randomise_appearance_prefs() //let's create a random character then - rather than a fat, bald and naked man. if(parent) apply_all_client_preferences() parent.set_macros() @@ -399,7 +399,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/character_preview_view) create_body() else body.wipe_state() - // TODO tgui-prefs appearance = preferences.render_new_preview_appearance(body) + appearance = preferences.render_new_preview_appearance(body) /atom/movable/screen/character_preview_view/proc/create_body() QDEL_NULL(body) @@ -431,13 +431,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/character_preview_view) var/list/profiles = list() for(var/index in 1 to TRUE_MAX_SAVE_SLOTS) - // It won't be updated in the savefile yet, so just read the name directly - if (index == default_slot) - profiles += read_preference(/datum/preference/name/real_name) - continue - - // TODO tgui-prefs - var/name = "testing" + var/name = read_preference(/datum/preference/name/real_name, index) if (isnull(name)) profiles += null @@ -507,9 +501,8 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/character_preview_view) /datum/preferences/proc/should_be_random_hardcore(datum/job/job, datum/mind/mind) if(!read_preference(/datum/preference/toggle/random_hardcore)) return FALSE - // TODO tgui-prefs - //if(job.department_flag & DEPARTMENT_BITFLAG_COMMAND) //No command staff - // return FALSE + if(job.departments & DEPT_BITFLAG_COM) //No command staff + return FALSE for(var/datum/antagonist/antag as anything in mind.antag_datums) if(antag.get_team()) //No team antags return FALSE diff --git a/code/modules/client/preferences/_preference.dm b/code/modules/client/preferences/_preference.dm index b8af4eaa50a98..bc21564896c09 100644 --- a/code/modules/client/preferences/_preference.dm +++ b/code/modules/client/preferences/_preference.dm @@ -224,7 +224,8 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) /// Read a /datum/preference type and return its value. /// This will write to the database with the default value if a value was not found. -/datum/preferences/proc/read_preference(preference_type) +/// Specifying a slot will query the database directly, only do this if absolutely necessary +/datum/preferences/proc/read_preference(preference_type, slot = null) var/datum/preference/preference_entry = GLOB.preference_entries[preference_type] if (isnull(preference_entry)) var/extra_info = "" @@ -236,10 +237,10 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) CRASH("Preference type `[preference_type]` is invalid! [extra_info]") - if (preference_type in value_cache) + if ((isnull(slot) || slot == selected_slot) && preference_type in value_cache) return value_cache[preference_type] - var/value = preference_entry.read(parent.ckey, src) + var/value = preference_entry.read(parent.ckey, src, !isnull(slot) ? slot : selected_slot) if (isnull(value)) value = preference_entry.create_informed_default_value(src) if (write_preference(preference_entry, value)) diff --git a/code/modules/client/preferences/middleware/jobs.dm b/code/modules/client/preferences/middleware/jobs.dm index 788e767556bb0..aa73e80d05cf5 100644 --- a/code/modules/client/preferences/middleware/jobs.dm +++ b/code/modules/client/preferences/middleware/jobs.dm @@ -50,7 +50,7 @@ var/list/job_days_left = list() var/list/job_required_experience = list() -// TODO tgui_prefs +// TODO tgui-prefs /* for (var/datum/job/job as anything in SSjob.all_occupations) var/required_playtime_remaining = job.required_playtime_remaining(user.client) diff --git a/code/modules/client/preferences/middleware/quirks.dm b/code/modules/client/preferences/middleware/quirks.dm index b4b2dc9092478..5e7194ea43cdd 100644 --- a/code/modules/client/preferences/middleware/quirks.dm +++ b/code/modules/client/preferences/middleware/quirks.dm @@ -1,5 +1,5 @@ /// Middleware to handle quirks -/* + /datum/preference_middleware/quirks var/tainted = FALSE @@ -26,9 +26,7 @@ data["selected_quirks"] = get_selected_quirks() return data -*/ -// TODO tgui-prefs -/* + /datum/preference_middleware/quirks/get_constant_data() var/list/quirk_info = list() @@ -36,7 +34,7 @@ var/datum/quirk/quirk = SSquirks.quirks[quirk_name] quirk_info[sanitize_css_class_name(quirk_name)] = list( "description" = initial(quirk.desc), - "icon" = initial(quirk.icon), + "icon" = null /* TODO tgui-prefs */, "name" = quirk_name, "value" = initial(quirk.value), ) @@ -54,12 +52,12 @@ var/quirk_name = params["quirk"] var/list/new_quirks = preferences.all_quirks | quirk_name - if (SSquirks.filter_invalid_quirks(new_quirks) != new_quirks) + /*if (SSquirks.filter_invalid_quirks(new_quirks) != new_quirks) // If the client is sending an invalid give_quirk, that means that // something went wrong with the client prediction, so we should // catch it back up to speed. preferences.update_static_data(user) - return TRUE + return TRUE*/ preferences.all_quirks = new_quirks @@ -69,7 +67,7 @@ var/quirk_name = params["quirk"] var/list/new_quirks = preferences.all_quirks - quirk_name - if ( \ + /*if ( \ !(quirk_name in preferences.all_quirks) \ || SSquirks.filter_invalid_quirks(new_quirks) != new_quirks \ ) @@ -77,7 +75,7 @@ // something went wrong with the client prediction, so we should // catch it back up to speed. preferences.update_static_data(user) - return TRUE + return TRUE*/ preferences.all_quirks = new_quirks @@ -90,4 +88,3 @@ selected_quirks += sanitize_css_class_name(quirk) return selected_quirks -*/ diff --git a/code/modules/client/preferences/middleware/random.dm b/code/modules/client/preferences/middleware/random.dm index 60e59e7ec9e38..e8efba2a54ae3 100644 --- a/code/modules/client/preferences/middleware/random.dm +++ b/code/modules/client/preferences/middleware/random.dm @@ -76,7 +76,7 @@ /// Given randomization flags, will return whether or not this preference should be randomized. /datum/preference/proc/included_in_randomization_flags(randomize_flags) return TRUE -// TODO tgui_prefs +// TODO tgui-prefs /* /datum/preference/name/included_in_randomization_flags(randomize_flags) return !!(randomize_flags & RANDOMIZE_NAME) diff --git a/code/modules/client/preferences/phobia.dm b/code/modules/client/preferences/phobia.dm index e1cd258852896..242a054610128 100644 --- a/code/modules/client/preferences/phobia.dm +++ b/code/modules/client/preferences/phobia.dm @@ -2,6 +2,10 @@ category = PREFERENCE_CATEGORY_SECONDARY_FEATURES db_key = "phobia" preference_type = PREFERENCE_CHARACTER + +/datum/preference/choiced/phobia/init_possible_values() + return list() + // TODO tgui-prefs /* /datum/preference/choiced/phobia/init_possible_values() diff --git a/code/modules/client/preferences_menu.dm b/code/modules/client/preferences_menu.dm index 887816a0a0af9..5c9bd3f2c942a 100644 --- a/code/modules/client/preferences_menu.dm +++ b/code/modules/client/preferences_menu.dm @@ -1,4 +1,4 @@ -/datum/verbs/menu/Preferences/verb/open_character_preferences() +/client/verb/open_character_preferences() set category = "OOC" set name = "Open Character Preferences" set desc = "Open Character Preferences" @@ -11,7 +11,7 @@ preferences.update_static_data(usr) preferences.ui_interact(usr) -/datum/verbs/menu/Preferences/verb/open_game_preferences() +/client/verb/open_game_preferences() set category = "OOC" set name = "Open Game Preferences" set desc = "Open Game Preferences" diff --git a/code/modules/mob/dead/new_player/preferences_setup.dm b/code/modules/mob/dead/new_player/preferences_setup.dm index 2cb659b388d77..98e21e179b00d 100644 --- a/code/modules/mob/dead/new_player/preferences_setup.dm +++ b/code/modules/mob/dead/new_player/preferences_setup.dm @@ -21,7 +21,7 @@ if (should_randomize(preference, antag_override)) write_preference(preference, preference.create_random_value(src)) -// TODO tgui_prefs +// TODO tgui-prefs ///Setup the random hardcore quirks and give the character the new score prize. /datum/preferences/proc/hardcore_random_setup(mob/living/carbon/human/character) @@ -34,7 +34,7 @@ * Returns the new value to be gained with this setup, plus the previously earned score. **/ /datum/preferences/proc/select_hardcore_quirks() -// TODO tgui_prefs +// TODO tgui-prefs /* . = 0 var/quirk_budget = rand(8, 35) @@ -106,7 +106,7 @@ if(preview_job) mannequin.job = preview_job.title - // TODO tgui_prefs mannequin.dress_up_as_job(preview_job, TRUE) + // TODO tgui-prefs mannequin.dress_up_as_job(preview_job, TRUE) COMPILE_OVERLAYS(mannequin) return mannequin.appearance From 5db01464578d9fed41809468ded15c04a13c5457 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Mon, 3 Apr 2023 03:13:31 -0500 Subject: [PATCH 026/269] More fixes and runtime --- code/__HELPERS/game.dm | 2 +- code/__HELPERS/icons.dm | 7 +- code/datums/datacore.dm | 15 +- code/game/objects/structures/life_candle.dm | 5 +- code/modules/admin/secrets.dm | 3 +- code/modules/admin/verbs/one_click_antag.dm | 2 +- code/modules/admin/verbs/randomverbs.dm | 18 +- .../antagonists/_common/antag_spawner.dm | 8 +- .../antagonists/changeling/changeling.dm | 9 +- code/modules/antagonists/ert/ert.dm | 6 +- code/modules/awaymissions/capture_the_flag.dm | 2 +- code/modules/client/preferences/body_type.dm | 6 +- .../client/preferences/middleware/quirks.dm | 2 +- code/modules/client/preferences/pda.dm | 14 +- code/modules/client/preferences/phobia.dm | 3 +- code/modules/client/preferences/random.dm | 4 +- .../client/preferences/security_department.dm | 2 +- .../preferences/species_features/basic.dm | 2 +- code/modules/clothing/spacesuits/hardsuit.dm | 10 +- code/modules/reagents/chemistry/holder.dm | 1 + .../organs/external/_external_organs.dm | 257 ------------------ code/modules/surgery/organs/external/wings.dm | 226 --------------- 22 files changed, 63 insertions(+), 541 deletions(-) delete mode 100644 code/modules/surgery/organs/external/_external_organs.dm delete mode 100644 code/modules/surgery/organs/external/wings.dm diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 5eaedc9d9b44c..b0edceb5cd1ba 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -499,7 +499,7 @@ var/mob/living/carbon/human/new_character = new//The mob being spawned. SSjob.SendToLateJoin(new_character) - // TODO tgui-prefs G_found.client.prefs.active_character.copy_to(new_character) + G_found.client.prefs.apply_prefs_to(new_character) new_character.dna.update_dna_identity() new_character.key = G_found.key diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 878efde64dff3..eacb59672fe52 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -1109,14 +1109,13 @@ GLOBAL_LIST_EMPTY(friendly_animal_types) /// # If you already have a human and need to get its flat icon, call `get_flat_existing_human_icon()` instead. /// For creating consistent icons for human looking simple animals. -/proc/get_flat_human_icon(icon_id, datum/job/J, cs, dummy_key, showDirs = GLOB.cardinals, outfit_override = null) +/proc/get_flat_human_icon(icon_id, datum/job/J, datum/preferences/prefs, dummy_key, showDirs = GLOB.cardinals, outfit_override = null) var/static/list/humanoid_icon_cache = list() if(!icon_id || !humanoid_icon_cache[icon_id]) var/mob/living/carbon/human/dummy/body = generate_or_wait_for_human_dummy(dummy_key) - // TODO tgui-prefs - //if(CS) - // CS.copy_to(body,TRUE,FALSE) + if(prefs) + prefs.apply_prefs_to(body, icon_updates = TRUE) if(J) J.equip(body, TRUE, FALSE, outfit_override = outfit_override) else if (outfit_override) diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index b6cb7bc5b8fa6..3587428aa7fd5 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -387,15 +387,12 @@ security_records_out += list(crew_record) return security_records_out -/datum/datacore/proc/get_id_photo(mob/living/carbon/human/H, client/C, show_directions = list(SOUTH), humanoverride = FALSE) - // TODO tgui-prefs - /*var/datum/job/J = SSjob.GetJob(H.mind.assigned_role) - var/datum/character_save/CS +/datum/datacore/proc/get_id_photo(mob/living/carbon/human/H, client/C, show_directions = list(SOUTH)) + var/datum/job/J = SSjob.GetJob(H.mind.assigned_role) + var/datum/preferences/prefs if(!C) C = H.client if(C) - CS = C.prefs.active_character - if(humanoverride) - CS.pref_species = new /datum/species/human - H.copy_features(CS) - return get_flat_human_icon(null, J, CS, DUMMY_HUMAN_SLOT_MANIFEST, show_directions)*/ + prefs = C.prefs + // TODO tguip-test IPC changeling + return get_flat_human_icon(null, J, prefs, DUMMY_HUMAN_SLOT_MANIFEST, show_directions) diff --git a/code/game/objects/structures/life_candle.dm b/code/game/objects/structures/life_candle.dm index 652c2714918cc..b904d4795fa46 100644 --- a/code/game/objects/structures/life_candle.dm +++ b/code/game/objects/structures/life_candle.dm @@ -79,10 +79,9 @@ body = mind.current if(!body) body = new mob_type(T) - // TODO tgui-prefs - /*var/mob/ghostie = mind.get_ghost(TRUE) + var/mob/ghostie = mind.get_ghost(TRUE) if(ghostie.client?.prefs) - ghostie.client.prefs.active_character.copy_to(body)*/ + ghostie.client.prefs.apply_prefs_to(body) mind.transfer_to(body) else body.forceMove(T) diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm index 2a2d629e42aa3..dcbf61da6eeb7 100644 --- a/code/modules/admin/secrets.dm +++ b/code/modules/admin/secrets.dm @@ -844,7 +844,8 @@ GLOBAL_DATUM_INIT(admin_secrets, /datum/admin_secrets, new) if (length(players)) var/mob/chosen = players[1] if (chosen.client) - // TODO tgui-prefs chosen.client.prefs.active_character.copy_to(spawnedMob) + if(ishuman(spawnedMob)) + chosen.client.prefs.apply_prefs_to(spawnedMob) spawnedMob.key = chosen.key players -= chosen if (ishuman(spawnedMob) && ispath(humanoutfit, /datum/outfit)) diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm index d79e0550cf320..4bccad0bb4a9a 100644 --- a/code/modules/admin/verbs/one_click_antag.dm +++ b/code/modules/admin/verbs/one_click_antag.dm @@ -379,7 +379,7 @@ //Spawn the body var/mob/living/carbon/human/ERTOperative = new ertemplate.mobtype(spawnloc) - //TODO tgui-prefs chosen_candidate.client.prefs.active_character.copy_to(ERTOperative) + chosen_candidate.client.prefs.apply_prefs_to(ERTOperative) ERTOperative.key = chosen_candidate.key log_objective(ERTOperative, missionobj.explanation_text) diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index c2ffcb5ac38d6..3583c249c4179 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -431,16 +431,14 @@ Traitors and the like can also be revived with the previous role mostly intact. new_character.gender = record_found.fields["gender"] new_character.age = record_found.fields["age"] new_character.hardset_dna(record_found.fields["identity"], record_found.fields["enzymes"], record_found.fields["name"], record_found.fields["blood_type"], new record_found.fields["species"], record_found.fields["features"], null) - //else - // TODO tgui-prefs - /* - var/datum/character_save/CS = new() - CS.randomise() - CS.pref_species.random_name(CS.gender, TRUE) - CS.copy_to(new_character) - new_character.dna.update_dna_identity()*/ - - //new_character.name = new_character.real_name + else + // TODO tguip-test + randomize_human(new_character) + new_character.real_name = new_character.dna.species.random_name(new_character.gender, TRUE) + new_character.name = new_character.real_name + new_character.dna.update_dna_identity() + + new_character.name = new_character.real_name if(G_found.mind && !G_found.mind.active) G_found.mind.transfer_to(new_character) //be careful when doing stuff like this! I've already checked the mind isn't in use diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm index 4a5e5f8d32f6f..8b0ff3f480b1c 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -74,7 +74,7 @@ /obj/item/antag_spawner/contract/spawn_antag(client/C, turf/T, kind ,datum/mind/user) new /obj/effect/particle_effect/smoke(T) var/mob/living/carbon/human/M = new/mob/living/carbon/human(T) - //TODO tgui-prefs C.prefs.active_character.copy_to(M) + C.prefs.apply_prefs_to(M) M.key = C.key var/datum/mind/app_mind = M.mind @@ -134,7 +134,7 @@ /obj/item/antag_spawner/nuke_ops/spawn_antag(client/C, turf/T, kind, datum/mind/user) var/mob/living/carbon/human/M = new/mob/living/carbon/human(T) - //TODO tgui-prefs C.prefs.active_character.copy_to(M) + C.prefs.apply_prefs_to(M) M.key = C.key var/datum/antagonist/nukeop/new_op = new() @@ -153,7 +153,7 @@ /obj/item/antag_spawner/nuke_ops/clown/spawn_antag(client/C, turf/T, kind, datum/mind/user) var/mob/living/carbon/human/M = new/mob/living/carbon/human(T) - //TODO tgui-prefs C.prefs.active_character.copy_to(M) + C.prefs.apply_prefs_to(M) M.key = C.key var/datum/antagonist/nukeop/clownop/new_op = new /datum/antagonist/nukeop/clownop() @@ -315,7 +315,7 @@ /obj/item/antag_spawner/gangster/spawn_antag(client/C, turf/T, datum/mind/user) var/mob/living/carbon/human/M = new/mob/living/carbon/human(T) if (C) - //TODO tgui-prefs C.prefs.active_character.copy_to(M) + C.prefs.apply_prefs_to(M) M.key = C.key var/datum/antagonist/gang/alignment = user.has_antag_datum(/datum/antagonist/gang,TRUE) diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm index 82dda4e57ed0a..0103ccb27bf45 100644 --- a/code/modules/antagonists/changeling/changeling.dm +++ b/code/modules/antagonists/changeling/changeling.dm @@ -347,18 +347,13 @@ var/mob/living/carbon/C = owner.current //only carbons have dna now, so we have to typecaste if(isipc(C)) C.set_species(/datum/species/human) - var/replacementName = random_unique_name(C.gender) - //TODO tgui-prefs - //if(C.client.prefs.active_character.custom_names["human"]) - // C.fully_replace_character_name(C.real_name, C.client.prefs.active_character.custom_names["human"]) - //else - C.fully_replace_character_name(C.real_name, replacementName) + C.fully_replace_character_name(C.real_name, C.client.prefs.read_preference(/datum/preference/name/backup_human)) for(var/datum/data/record/E in GLOB.data_core.general) if(E.fields["name"] == C.real_name) E.fields["species"] = "\improper Human" var/client/Clt = C.client var/static/list/show_directions = list(SOUTH, WEST) - var/image = GLOB.data_core.get_id_photo(C, Clt, show_directions, TRUE) + var/image = GLOB.data_core.get_id_photo(C, Clt, show_directions)// TODO tguip-test var/datum/picture/pf = new var/datum/picture/ps = new pf.picture_name = "[C]" diff --git a/code/modules/antagonists/ert/ert.dm b/code/modules/antagonists/ert/ert.dm index b60e166976170..d73f4a8939a73 100644 --- a/code/modules/antagonists/ert/ert.dm +++ b/code/modules/antagonists/ert/ert.dm @@ -37,9 +37,9 @@ /datum/antagonist/ert/proc/update_name() var/name = pick(name_source) -// TODO tgui-prefs if (!name) -// name = owner.current.client?.prefs.active_character.custom_names["human"] || pick(GLOB.last_names) - owner.current.fully_replace_character_name(owner.current.real_name,"[role] [name]") + if (!name) + name = owner.current.client?.prefs.read_preference(/datum/preference/name/backup_human) || pick(GLOB.last_names) + owner.current.fully_replace_character_name(owner.current.real_name, "[role] [name]") /datum/antagonist/ert/deathsquad/New() . = ..() diff --git a/code/modules/awaymissions/capture_the_flag.dm b/code/modules/awaymissions/capture_the_flag.dm index c384a63b05af3..1ef3dffae666d 100644 --- a/code/modules/awaymissions/capture_the_flag.dm +++ b/code/modules/awaymissions/capture_the_flag.dm @@ -284,7 +284,7 @@ /obj/machinery/capture_the_flag/proc/spawn_team_member(client/new_team_member) var/mob/living/carbon/human/M = new/mob/living/carbon/human(get_turf(src)) - // TODO tgui-prefs new_team_member.prefs.active_character.copy_to(M) + new_team_member.prefs.apply_prefs_to(M) if(!(M.dna.species.type in allowed_species)) M.set_species(/datum/species/human) //default to human if not whitelisted M.key = new_team_member.key diff --git a/code/modules/client/preferences/body_type.dm b/code/modules/client/preferences/body_type.dm index f79f8d3470274..f43a9c886ac1f 100644 --- a/code/modules/client/preferences/body_type.dm +++ b/code/modules/client/preferences/body_type.dm @@ -1,4 +1,4 @@ -/datum/preference/choiced/body_type +/*/datum/preference/choiced/body_type category = PREFERENCE_CATEGORY_NON_CONTEXTUAL priority = PREFERENCE_PRIORITY_BODY_TYPE db_key = "body_type" @@ -6,8 +6,7 @@ /datum/preference/choiced/body_type/init_possible_values() return list(MALE, FEMALE) -// TODO tgui-prefs -/* + /datum/preference/choiced/body_type/apply_to_human(mob/living/carbon/human/target, value) if (target.gender != MALE && target.gender != FEMALE) target.body_type = value @@ -21,3 +20,4 @@ var/gender = preferences.read_preference(/datum/preference/choiced/gender) return gender != MALE && gender != FEMALE */ +// TODO tgui-prefs diff --git a/code/modules/client/preferences/middleware/quirks.dm b/code/modules/client/preferences/middleware/quirks.dm index 5e7194ea43cdd..fe00d7226f6f5 100644 --- a/code/modules/client/preferences/middleware/quirks.dm +++ b/code/modules/client/preferences/middleware/quirks.dm @@ -34,7 +34,7 @@ var/datum/quirk/quirk = SSquirks.quirks[quirk_name] quirk_info[sanitize_css_class_name(quirk_name)] = list( "description" = initial(quirk.desc), - "icon" = null /* TODO tgui-prefs */, + "icon" = "" /* TODO tgui-prefs */, "name" = quirk_name, "value" = initial(quirk.value), ) diff --git a/code/modules/client/preferences/pda.dm b/code/modules/client/preferences/pda.dm index 25f1a15277768..0f7018d8c34c6 100644 --- a/code/modules/client/preferences/pda.dm +++ b/code/modules/client/preferences/pda.dm @@ -13,5 +13,17 @@ db_key = "pda_theme" preference_type = PREFERENCE_PLAYER +/datum/preference/choiced/pda_theme/deserialize(input, datum/preferences/preferences) + for(var/key in GLOB.ntos_device_themes_default) // if only there was a associative list flip proc that I could locate. Unfortunately, I could not locate one. + if(GLOB.ntos_device_themes_default[key] == input) + return key + return "NtOS Default" + +/datum/preference/choiced/pda_theme/serialize(input) + return GLOB.ntos_device_themes_default[sanitize_inlist(input, GLOB.ntos_device_themes_default, "NtOS Default")] + +/datum/preference/choiced/pda_theme/create_default_value() + return THEME_NTOS + /datum/preference/choiced/pda_theme/init_possible_values() - return GLOB.ntos_device_themes_default + return GLOB.ntos_device_themes_default_content diff --git a/code/modules/client/preferences/phobia.dm b/code/modules/client/preferences/phobia.dm index 242a054610128..5e81a97bae490 100644 --- a/code/modules/client/preferences/phobia.dm +++ b/code/modules/client/preferences/phobia.dm @@ -1,10 +1,11 @@ -/datum/preference/choiced/phobia +/*/datum/preference/choiced/phobia category = PREFERENCE_CATEGORY_SECONDARY_FEATURES db_key = "phobia" preference_type = PREFERENCE_CHARACTER /datum/preference/choiced/phobia/init_possible_values() return list() +*/ // TODO tgui-prefs /* diff --git a/code/modules/client/preferences/random.dm b/code/modules/client/preferences/random.dm index e103da5e3508f..7518620715e1c 100644 --- a/code/modules/client/preferences/random.dm +++ b/code/modules/client/preferences/random.dm @@ -1,6 +1,6 @@ /datum/preference/choiced/random_body category = PREFERENCE_CATEGORY_NON_CONTEXTUAL - db_key = "random_body" + db_key = "body_is_always_random" preference_type = PREFERENCE_CHARACTER can_randomize = FALSE @@ -37,7 +37,7 @@ */ /datum/preference/choiced/random_name category = PREFERENCE_CATEGORY_NON_CONTEXTUAL - db_key = "random_name" + db_key = "name_is_always_random" preference_type = PREFERENCE_CHARACTER can_randomize = FALSE diff --git a/code/modules/client/preferences/security_department.dm b/code/modules/client/preferences/security_department.dm index 9d97c88fe02a8..6a04bc34185a1 100644 --- a/code/modules/client/preferences/security_department.dm +++ b/code/modules/client/preferences/security_department.dm @@ -3,7 +3,7 @@ category = PREFERENCE_CATEGORY_NON_CONTEXTUAL can_randomize = FALSE preference_type = PREFERENCE_CHARACTER - db_key = "prefered_security_department" + db_key = "preferred_security_department" // This is what that #warn wants you to remove :) /datum/preference/choiced/security_department/deserialize(input, datum/preferences/preferences) diff --git a/code/modules/client/preferences/species_features/basic.dm b/code/modules/client/preferences/species_features/basic.dm index d7f7b30fb40a0..21141983b79a7 100644 --- a/code/modules/client/preferences/species_features/basic.dm +++ b/code/modules/client/preferences/species_features/basic.dm @@ -80,7 +80,7 @@ target.hair_color = value /datum/preference/choiced/hairstyle - db_key = "hairstyle_name" + db_key = "hair_style_name" preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Hairstyle" diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index d383251ef3763..d99ebfe701192 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -59,7 +59,8 @@ ..() if(suit) suit.RemoveHelmet() - soundloop.stop(user) + if(user.client) + soundloop.stop(user) /obj/item/clothing/head/helmet/space/hardsuit/item_action_slot_check(slot) if(slot == ITEM_SLOT_HEAD) @@ -70,10 +71,11 @@ if(slot != ITEM_SLOT_HEAD) if(suit) suit.RemoveHelmet() - soundloop.stop(user) + if(user.client) + soundloop.stop(user) else qdel(src) - else + else if(user.client) soundloop.start(user) /obj/item/clothing/head/helmet/space/hardsuit/proc/toggle_hud(mob/user) @@ -179,7 +181,7 @@ /obj/item/clothing/suit/space/hardsuit/equipped(mob/user, slot) ..() - if(jetpack) + if(isatom(jetpack)) if(slot == ITEM_SLOT_OCLOTHING) for(var/X in jetpack.actions) var/datum/action/A = X diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm index ec2aacfd2ca2d..ed01d09278635 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -7,6 +7,7 @@ return var/paths = subtypesof(/datum/reagent) + GLOB.chemical_reagents_list = list() for(var/path in paths) var/datum/reagent/D = new path() diff --git a/code/modules/surgery/organs/external/_external_organs.dm b/code/modules/surgery/organs/external/_external_organs.dm deleted file mode 100644 index ef1198317ceb4..0000000000000 --- a/code/modules/surgery/organs/external/_external_organs.dm +++ /dev/null @@ -1,257 +0,0 @@ -/** -* System for drawing organs with overlays. These overlays are drawn directly on the bodypart, attached to a person or not -* Works in tandem with the /datum/sprite_accessory datum to generate sprites -* Unlike normal organs, we're actually inside a persons limbs at all times -*/ -/obj/item/organ/external - name = "external organ" - desc = "An external organ that is too external." - - ///Unremovable is until the features are completely finished - organ_flags = ORGAN_UNREMOVABLE | ORGAN_EDIBLE - - ///Sometimes we need multiple layers, for like the back, middle and front of the person - var/layers - ///Convert the bitflag define into the actual layer define - var/static/list/all_layers = list(EXTERNAL_FRONT, EXTERNAL_ADJACENT, EXTERNAL_BEHIND) - - ///Defines what kind of 'organ' we're looking at. Sprites have names like 'm_firemoth_mothwings'. 'mothwings' would then be feature_key - var/feature_key = "" - - /// The db_key of the preference this relates to. Used for the preferences UI. - var/preference - - ///Sprite datum we use to draw on the bodypart - var/datum/sprite_accessory/sprite_datum - ///Key of the icon states of all the sprite_datums for easy caching - var/cache_key = "" - - ///With what DNA block do we mutate in mutate_feature() ? For genetics - var/dna_block - - ///Reference to the limb we're inside of - var/obj/item/bodypart/ownerlimb - -/**mob_sprite is optional if you havent set sprite_datums for the object, and is used mostly to generate sprite_datums from a persons DNA -* For _mob_sprite we make a distinction between "Round Snout" and "round". Round Snout is the name of the sprite datum, while "round" would be part of the sprite -* I'm sorry -*/ -/obj/item/organ/external/Initialize(mapload, mob_sprite) - . = ..() - if(mob_sprite) - set_sprite(mob_sprite) - -/obj/item/organ/external/Insert(mob/living/carbon/reciever, special, drop_if_replaced) - var/obj/item/bodypart/limb = reciever.get_bodypart(zone) - - if(!limb) - return FALSE - - limb.external_organs.Add(src) - ownerlimb = limb - - . = ..() - - limb.contents.Add(src) - - reciever.update_body_parts() - -/obj/item/organ/external/Remove(mob/living/carbon/organ_owner, special) - . = ..() - - if(ownerlimb) - ownerlimb.external_organs.Remove(src) - ownerlimb.contents.Remove(src) - ownerlimb = null - - organ_owner.update_body_parts() - -/obj/item/organ/external/transfer_to_limb(obj/item/bodypart/bodypart, mob/living/carbon/bodypart_owner) - . = ..() - - bodypart.external_organs.Add(src) - bodypart.contents.Add(src) - -///Add the overlays we need to draw on a person. Called from _bodyparts.dm -/obj/item/organ/external/proc/get_overlays(list/overlay_list, image_dir, image_layer, body_type, image_color) - if(!sprite_datum) - return - - var/gender = (body_type == FEMALE) ? "f" : "m" - var/finished_icon_state = (sprite_datum.gender_specific ? gender : "m") + "_" + feature_key + "_" + sprite_datum.icon_state + mutant_bodyparts_layertext(image_layer) - var/mutable_appearance/appearance = mutable_appearance(sprite_datum.icon, finished_icon_state, layer = -image_layer) - appearance.dir = image_dir - - if(sprite_datum.color_src) //There are multiple flags, but only one is ever used so meh :/ - appearance.color = image_color - - if(sprite_datum.center) - center_image(appearance, sprite_datum.dimension_x, sprite_datum.dimension_y) - - overlay_list += appearance - -/obj/item/organ/external/proc/set_sprite(sprite_name) - sprite_datum = get_sprite_datum(sprite_name) - cache_key = generate_icon_cache() - -///Generate a unique key based on our sprites. So that if we've aleady drawn these sprites, they can be found in the cache and wont have to be drawn again (blessing and curse) -/obj/item/organ/external/proc/generate_icon_cache() - return "[sprite_datum.icon_state]_[feature_key]" - -/**This exists so sprite accessories can still be per-layer without having to include that layer's -* number in their sprite name, which causes issues when those numbers change. -*/ -/obj/item/organ/external/proc/mutant_bodyparts_layertext(layer) - switch(layer) - if(BODY_BEHIND_LAYER) - return "_BEHIND" - if(BODY_ADJ_LAYER) - return "_ADJ" - if(BODY_FRONT_LAYER) - return "_FRONT" - -///Converts a bitflag to the right layer. I'd love to make this a static index list, but byond made an attempt on my life when i did -/obj/item/organ/external/proc/bitflag_to_layer(layer) - switch(layer) - if(EXTERNAL_BEHIND) - return BODY_BEHIND_LAYER - if(EXTERNAL_ADJACENT) - return BODY_ADJ_LAYER - if(EXTERNAL_FRONT) - return BODY_FRONT_LAYER - -///Because all the preferences have names like "Beautiful Sharp Snout" we need to get the sprite datum with the actual important info -/obj/item/organ/external/proc/get_sprite_datum(sprite) - var/list/feature_list = get_global_feature_list() - return feature_list[sprite] - -///Return a dumb glob list for this specific feature (called from parse_sprite) -/obj/item/organ/external/proc/get_global_feature_list() - return null - -///Check whether we can draw the overlays. You generally don't want lizard snouts to draw over an EVA suit -/obj/item/organ/external/proc/can_draw_on_bodypart(mob/living/carbon/human/human) - return TRUE - -///Update our features after something changed our appearance -/obj/item/organ/external/proc/mutate_feature(features, mob/living/carbon/human/human) - if(!dna_block || !get_global_feature_list()) - return - - var/list/feature_list = get_global_feature_list() - - set_sprite(feature_list[deconstruct_block(getblock(features, dna_block), feature_list.len)]) - -///The horns of a lizard! -/obj/item/organ/external/horns - zone = BODY_ZONE_HEAD - slot = ORGAN_SLOT_EXTERNAL_HORNS - layers = EXTERNAL_ADJACENT - - feature_key = "horns" - preference = "feature_lizard_horns" - - dna_block = DNA_HORNS_BLOCK - -/obj/item/organ/external/horns/can_draw_on_bodypart(mob/living/carbon/human/human) - if(!(human.head?.flags_inv & HIDEHAIR) || (human.wear_mask?.flags_inv & HIDEHAIR)) - return TRUE - return FALSE - -/obj/item/organ/external/horns/get_global_feature_list() - return GLOB.horns_list - -///The frills of a lizard (like weird fin ears) -/obj/item/organ/external/frills - zone = BODY_ZONE_HEAD - slot = ORGAN_SLOT_EXTERNAL_FRILLS - layers = EXTERNAL_ADJACENT - - feature_key = "frills" - preference = "feature_lizard_frills" - - dna_block = DNA_FRILLS_BLOCK - -/obj/item/organ/external/frills/can_draw_on_bodypart(mob/living/carbon/human/human) - if(!(human.head?.flags_inv & HIDEEARS)) - return TRUE - return FALSE - - -/obj/item/organ/external/frills/get_global_feature_list() - return GLOB.frills_list - -///Guess what part of the lizard this is? -/obj/item/organ/external/snout - zone = BODY_ZONE_HEAD - slot = ORGAN_SLOT_EXTERNAL_SNOUT - layers = EXTERNAL_ADJACENT - - feature_key = "snout" - preference = "feature_lizard_snout" - - dna_block = DNA_SNOUT_BLOCK - -/obj/item/organ/external/snout/can_draw_on_bodypart(mob/living/carbon/human/human) - if(!(human.wear_mask?.flags_inv & HIDESNOUT) && !(human.head?.flags_inv & HIDESNOUT)) - return TRUE - return FALSE - -/obj/item/organ/external/snout/get_global_feature_list() - return GLOB.snouts_list - -///A moth's antennae -/obj/item/organ/external/antennae - zone = BODY_ZONE_HEAD - slot = ORGAN_SLOT_EXTERNAL_ANTENNAE - layers = EXTERNAL_FRONT | EXTERNAL_BEHIND - - feature_key = "moth_antennae" - preference = "feature_moth_antennae" - - dna_block = DNA_MOTH_ANTENNAE_BLOCK - - ///Are we burned? - var/burnt = FALSE - ///Store our old sprite here for if our antennae wings are healed - var/original_sprite = "" - -/obj/item/organ/external/antennae/Insert(mob/living/carbon/reciever, special, drop_if_replaced) - . = ..() - - RegisterSignal(reciever, COMSIG_HUMAN_BURNING, .proc/try_burn_antennae) - RegisterSignal(reciever, COMSIG_LIVING_POST_FULLY_HEAL, .proc/heal_antennae) - -/obj/item/organ/external/antennae/Remove(mob/living/carbon/organ_owner, special) - . = ..() - - UnregisterSignal(organ_owner, list(COMSIG_HUMAN_BURNING, COMSIG_LIVING_POST_FULLY_HEAL)) - -/obj/item/organ/external/antennae/get_global_feature_list() - return GLOB.moth_antennae_list - -/obj/item/organ/external/antennae/can_draw_on_bodypart(mob/living/carbon/human/human) - return TRUE - -///check if our antennae can burn off ;_; -/obj/item/organ/external/antennae/proc/try_burn_antennae(mob/living/carbon/human/human) - SIGNAL_HANDLER - - if(!burnt && human.bodytemperature >= 800 && human.fire_stacks > 0) //do not go into the extremely hot light. you will not survive - to_chat(human, span_danger("Your precious antennae burn to a crisp!")) - - burn_antennae() - human.update_body_parts() - -/obj/item/organ/external/antennae/proc/burn_antennae() - burnt = TRUE - original_sprite = sprite_datum.name - set_sprite("Burnt Off") - -///heal our antennae back up!! -/obj/item/organ/external/antennae/proc/heal_antennae() - SIGNAL_HANDLER - - if(burnt) - burnt = FALSE - set_sprite(original_sprite) diff --git a/code/modules/surgery/organs/external/wings.dm b/code/modules/surgery/organs/external/wings.dm deleted file mode 100644 index 9b7a2e7a4e43b..0000000000000 --- a/code/modules/surgery/organs/external/wings.dm +++ /dev/null @@ -1,226 +0,0 @@ -///Wing base type. doesn't really do anything -/obj/item/organ/external/wings - zone = BODY_ZONE_CHEST - slot = ORGAN_SLOT_EXTERNAL_WINGS - layers = EXTERNAL_BEHIND | EXTERNAL_ADJACENT | EXTERNAL_FRONT - - feature_key = "wings" - -/obj/item/organ/external/wings/can_draw_on_bodypart(mob/living/carbon/human/human) - if(!human.wear_suit) - return TRUE - if(!(human.wear_suit.flags_inv & HIDEJUMPSUIT)) - return TRUE - if(human.wear_suit.species_exception && is_type_in_list(src, human.wear_suit.species_exception)) - return TRUE - return FALSE - -///The true wings that you can use to fly and shit (you cant actually shit with them) -/obj/item/organ/external/wings/functional - ///The flight action object - var/datum/action/innate/flight/fly - - ///The preference type for opened wings - var/wings_open_feature_key = "wingsopen" - ///The preference type for closed wings - var/wings_closed_feature_key = "wings" - - ///Are our wings open or closed? - var/wings_open = FALSE - -/obj/item/organ/external/wings/functional/get_global_feature_list() - if(wings_open) - return GLOB.wings_open_list - else - return GLOB.wings_list - -/obj/item/organ/external/wings/functional/Insert(mob/living/carbon/reciever, special, drop_if_replaced) - . = ..() - - if(isnull(fly)) - fly = new - fly.Grant(reciever) - -/obj/item/organ/external/wings/functional/Remove(mob/living/carbon/organ_owner, special) - . = ..() - - fly.Remove(organ_owner) - -/obj/item/organ/external/wings/functional/on_life(delta_time, times_fired) - . = ..() - - handle_flight(owner) - -///Called on_life(). Handle flight code and check if we're still flying -/obj/item/organ/external/wings/functional/proc/handle_flight(mob/living/carbon/human/human) - if(human.movement_type & ~FLYING) - return FALSE - if(!can_fly(human)) - toggle_flight(human) - return FALSE - return TRUE - - -///Check if we're still eligible for flight (wings covered, atmosphere too thin, etc) -/obj/item/organ/external/wings/functional/proc/can_fly(mob/living/carbon/human/human) - if(human.stat || human.body_position == LYING_DOWN) - return FALSE - //Jumpsuits have tail holes, so it makes sense they have wing holes too - if(human.wear_suit && ((human.wear_suit.flags_inv & HIDEJUMPSUIT) && (!human.wear_suit.species_exception || !is_type_in_list(src, human.wear_suit.species_exception)))) - to_chat(human, span_warning("Your suit blocks your wings from extending!")) - return FALSE - var/turf/location = get_turf(human) - if(!location) - return FALSE - - var/datum/gas_mixture/environment = location.return_air() - if(environment && !(environment.return_pressure() > 30)) - to_chat(human, span_warning("The atmosphere is too thin for you to fly!")) - return FALSE - else - return TRUE - -///Slipping but in the air? -/obj/item/organ/external/wings/functional/proc/fly_slip(mob/living/carbon/human/human) - var/obj/buckled_obj - if(human.buckled) - buckled_obj = human.buckled - - to_chat(human, span_notice("Your wings spazz out and launch you!")) - - playsound(human.loc, 'sound/misc/slip.ogg', 50, TRUE, -3) - - for(var/obj/item/choking_hazard in human.held_items) - human.accident(choking_hazard) - - var/olddir = human.dir - - human.stop_pulling() - if(buckled_obj) - buckled_obj.unbuckle_mob(human) - step(buckled_obj, olddir) - else - new /datum/forced_movement(human, get_ranged_target_turf(human, olddir, 4), 1, FALSE, CALLBACK(human, /mob/living/carbon/.proc/spin, 1, 1)) - return TRUE - -///UNSAFE PROC, should only be called through the Activate or other sources that check for CanFly -/obj/item/organ/external/wings/functional/proc/toggle_flight(mob/living/carbon/human/human) - if(!HAS_TRAIT_FROM(human, TRAIT_MOVE_FLYING, SPECIES_FLIGHT_TRAIT)) - human.physiology.stun_mod *= 2 - ADD_TRAIT(human, TRAIT_NO_FLOATING_ANIM, SPECIES_FLIGHT_TRAIT) - ADD_TRAIT(human, TRAIT_MOVE_FLYING, SPECIES_FLIGHT_TRAIT) - passtable_on(human, SPECIES_TRAIT) - open_wings() - else - human.physiology.stun_mod *= 0.5 - REMOVE_TRAIT(human, TRAIT_NO_FLOATING_ANIM, SPECIES_FLIGHT_TRAIT) - REMOVE_TRAIT(human, TRAIT_MOVE_FLYING, SPECIES_FLIGHT_TRAIT) - passtable_off(human, SPECIES_TRAIT) - close_wings() - -///SPREAD OUR WINGS AND FLLLLLYYYYYY -/obj/item/organ/external/wings/functional/proc/open_wings() - feature_key = wings_open_feature_key - wings_open = TRUE - - cache_key = generate_icon_cache() //we've changed preference to open, so we only need to update the key and ask for an update to change our sprite - owner.update_body_parts() - -///close our wings -/obj/item/organ/external/wings/functional/proc/close_wings() - feature_key = wings_closed_feature_key - wings_open = FALSE - - cache_key = generate_icon_cache() - owner.update_body_parts() - if(isturf(owner?.loc)) - var/turf/location = loc - location.Entered(src, NONE) - -///hud action for starting and stopping flight -/datum/action/innate/flight - name = "Toggle Flight" - check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_IMMOBILE - icon_icon = 'icons/mob/actions/actions_items.dmi' - button_icon_state = "flight" - -/datum/action/innate/flight/Activate() - var/mob/living/carbon/human/human = owner - var/obj/item/organ/external/wings/functional/wings = human.getorganslot(ORGAN_SLOT_EXTERNAL_WINGS) - if(wings && wings.can_fly(human)) - wings.toggle_flight(human) - if(!(human.movement_type & FLYING)) - to_chat(human, span_notice("You settle gently back onto the ground...")) - else - to_chat(human, span_notice("You beat your wings and begin to hover gently above the ground...")) - human.set_resting(FALSE, TRUE) - -///Moth wings! They can flutter in low-grav and burn off in heat -/obj/item/organ/external/wings/moth - feature_key = "moth_wings" - preference = "feature_moth_wings" - layers = EXTERNAL_BEHIND | EXTERNAL_FRONT - - dna_block = DNA_MOTH_WINGS_BLOCK - - ///Are we burned? - var/burnt = FALSE - ///Store our old sprite here for if our burned wings are healed - var/original_sprite = "" - -/obj/item/organ/external/wings/moth/get_global_feature_list() - return GLOB.moth_wings_list - -/obj/item/organ/external/wings/moth/can_draw_on_bodypart(mob/living/carbon/human/human) - return TRUE - -/obj/item/organ/external/wings/moth/Insert(mob/living/carbon/reciever, special, drop_if_replaced) - . = ..() - - RegisterSignal(reciever, COMSIG_HUMAN_BURNING, .proc/try_burn_wings) - RegisterSignal(reciever, COMSIG_LIVING_POST_FULLY_HEAL, .proc/heal_wings) - RegisterSignal(reciever, COMSIG_MOVABLE_PRE_MOVE, .proc/update_float_move) - -/obj/item/organ/external/wings/moth/Remove(mob/living/carbon/organ_owner, special) - . = ..() - - UnregisterSignal(organ_owner, list(COMSIG_HUMAN_BURNING, COMSIG_LIVING_POST_FULLY_HEAL, COMSIG_MOVABLE_PRE_MOVE)) - REMOVE_TRAIT(organ_owner, TRAIT_FREE_FLOAT_MOVEMENT, src) - -///Check if we can flutter around -/obj/item/organ/external/wings/moth/proc/update_float_move() - SIGNAL_HANDLER - - if(!isspaceturf(owner.loc) && !burnt) - var/datum/gas_mixture/current = owner.loc.return_air() - if(current && (current.return_pressure() >= ONE_ATMOSPHERE*0.85)) //as long as there's reasonable pressure and no gravity, flight is possible - ADD_TRAIT(owner, TRAIT_FREE_FLOAT_MOVEMENT, src) - return - - REMOVE_TRAIT(owner, TRAIT_FREE_FLOAT_MOVEMENT, src) - -///check if our wings can burn off ;_; -/obj/item/organ/external/wings/moth/proc/try_burn_wings(mob/living/carbon/human/human) - SIGNAL_HANDLER - - if(!burnt && human.bodytemperature >= 800 && human.fire_stacks > 0) //do not go into the extremely hot light. you will not survive - to_chat(human, span_danger("Your precious wings burn to a crisp!")) - SEND_SIGNAL(human, COMSIG_ADD_MOOD_EVENT, "burnt_wings", /datum/mood_event/burnt_wings) - - burn_wings() - human.update_body_parts() - -///burn the wings off -/obj/item/organ/external/wings/moth/proc/burn_wings() - burnt = TRUE - - original_sprite = sprite_datum.name - set_sprite("Burnt Off") - -///heal our wings back up!! -/obj/item/organ/external/wings/moth/proc/heal_wings() - SIGNAL_HANDLER - - if(burnt) - burnt = FALSE - set_sprite(original_sprite) From f7b396c37f6d27cad2dbef554a4a0869a6f0b54c Mon Sep 17 00:00:00 2001 From: itsmeow Date: Mon, 3 Apr 2023 17:39:27 -0500 Subject: [PATCH 027/269] Fixes --- code/controllers/subsystem/job.dm | 2 ++ code/modules/client/preferences/_preference.dm | 2 +- code/modules/client/preferences/body_type.dm | 8 +++----- code/modules/client/preferences/pda.dm | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index 661cc12190e7c..10d863244b153 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -95,6 +95,7 @@ SUBSYSTEM_DEF(job) /datum/controller/subsystem/job/proc/GetJob(rank) + RETURN_TYPE(/datum/job) if(!rank) CRASH("proc has taken no job name") if(!occupations.len) @@ -104,6 +105,7 @@ SUBSYSTEM_DEF(job) return name_occupations[rank] /datum/controller/subsystem/job/proc/GetJobType(jobtype) + RETURN_TYPE(/datum/job) if(!jobtype) CRASH("proc has taken no job type") if(!occupations.len) diff --git a/code/modules/client/preferences/_preference.dm b/code/modules/client/preferences/_preference.dm index bc21564896c09..fb47368192d61 100644 --- a/code/modules/client/preferences/_preference.dm +++ b/code/modules/client/preferences/_preference.dm @@ -237,7 +237,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) CRASH("Preference type `[preference_type]` is invalid! [extra_info]") - if ((isnull(slot) || slot == selected_slot) && preference_type in value_cache) + if ((isnull(slot) || slot == selected_slot) && (preference_type in value_cache)) return value_cache[preference_type] var/value = preference_entry.read(parent.ckey, src, !isnull(slot) ? slot : selected_slot) diff --git a/code/modules/client/preferences/body_type.dm b/code/modules/client/preferences/body_type.dm index f43a9c886ac1f..5b6709804d253 100644 --- a/code/modules/client/preferences/body_type.dm +++ b/code/modules/client/preferences/body_type.dm @@ -1,4 +1,4 @@ -/*/datum/preference/choiced/body_type +/datum/preference/choiced/body_type category = PREFERENCE_CATEGORY_NON_CONTEXTUAL priority = PREFERENCE_PRIORITY_BODY_TYPE db_key = "body_type" @@ -9,9 +9,9 @@ /datum/preference/choiced/body_type/apply_to_human(mob/living/carbon/human/target, value) if (target.gender != MALE && target.gender != FEMALE) - target.body_type = value + target.dna.features["body_model"] = value else - target.body_type = target.gender + target.dna.features["body_model"] = target.gender /datum/preference/choiced/body_type/is_accessible(datum/preferences/preferences) if (!..(preferences)) @@ -19,5 +19,3 @@ var/gender = preferences.read_preference(/datum/preference/choiced/gender) return gender != MALE && gender != FEMALE -*/ -// TODO tgui-prefs diff --git a/code/modules/client/preferences/pda.dm b/code/modules/client/preferences/pda.dm index 0f7018d8c34c6..da53ead9bab30 100644 --- a/code/modules/client/preferences/pda.dm +++ b/code/modules/client/preferences/pda.dm @@ -23,7 +23,7 @@ return GLOB.ntos_device_themes_default[sanitize_inlist(input, GLOB.ntos_device_themes_default, "NtOS Default")] /datum/preference/choiced/pda_theme/create_default_value() - return THEME_NTOS + return "NtOS Default" /datum/preference/choiced/pda_theme/init_possible_values() return GLOB.ntos_device_themes_default_content From 5fdd24bdfce8be9c7bc8dd6f7fc91fd4b50f80d7 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Tue, 4 Apr 2023 00:37:33 -0500 Subject: [PATCH 028/269] Major rewrite to how read/write pref work --- beestation.dme | 2 +- code/__HELPERS/game.dm | 2 +- code/__HELPERS/mobs.dm | 4 +- code/_onclick/hud/action_button.dm | 2 +- code/_onclick/hud/ghost.dm | 2 +- code/_onclick/hud/hud.dm | 4 +- code/_onclick/hud/parallax.dm | 2 +- code/_onclick/hud/rendering/plane_master.dm | 4 +- code/_onclick/observer.dm | 2 +- code/controllers/subsystem/job.dm | 2 +- code/controllers/subsystem/mapping.dm | 2 +- code/controllers/subsystem/vote.dm | 2 +- code/datums/action.dm | 2 +- code/datums/brain_damage/imaginary_friend.dm | 4 +- code/datums/chatmessage.dm | 2 +- code/datums/keybinding/client.dm | 2 +- code/datums/mind.dm | 2 +- code/datums/view.dm | 4 +- code/game/objects/items.dm | 8 +- code/modules/admin/poll_management.dm | 4 +- code/modules/admin/sql_ban_system.dm | 2 +- code/modules/admin/verbs/adminsay.dm | 2 +- .../antagonists/changeling/changeling.dm | 2 +- code/modules/antagonists/ert/ert.dm | 2 +- code/modules/asset_cache/asset_list.dm | 2 + code/modules/client/client_procs.dm | 2 +- code/modules/client/preferences.dm | 40 +-- .../modules/client/preferences/_preference.dm | 138 ++++------- code/modules/client/preferences/body_type.dm | 2 +- code/modules/client/preferences/clothing.dm | 2 +- .../client/preferences/item_outlines.dm | 5 + .../client/preferences/middleware/random.dm | 2 +- code/modules/client/preferences/names.dm | 10 +- code/modules/client/preferences/pda.dm | 6 +- code/modules/client/preferences/random.dm | 18 -- code/modules/client/preferences/screentips.dm | 6 - code/modules/client/preferences/skin_tone.dm | 2 +- .../preferences/species_features/basic.dm | 30 +++ .../preferences/species_features/ethereal.dm | 3 +- .../preferences/species_features/felinid.dm | 3 +- .../preferences/species_features/lizard.dm | 3 +- .../preferences/species_features/moth.dm | 3 +- .../preferences/species_features/mutants.dm | 3 +- .../client/preferences/underwear_color.dm | 2 +- code/modules/client/preferences_character.dm | 231 +++++++++++------- code/modules/client/preferences_database.dm | 76 ++++-- code/modules/client/preferences_player.dm | 59 +++++ code/modules/client/preferences_toggles.dm | 1 + code/modules/client/verbs/ooc.dm | 2 +- code/modules/flufftext/Hallucination.dm | 4 +- code/modules/jobs/job_types/_job.dm | 18 +- code/modules/jobs/job_types/ai.dm | 2 +- code/modules/jobs/job_types/chaplain.dm | 4 +- .../jobs/job_types/security_officer.dm | 2 +- .../modules/mob/dead/new_player/new_player.dm | 4 +- .../mob/dead/new_player/preferences_setup.dm | 4 +- code/modules/mob/dead/observer/login.dm | 8 +- code/modules/mob/dead/observer/observer.dm | 22 +- code/modules/mob/living/silicon/ai/ai.dm | 4 +- .../modules/mob/living/silicon/robot/robot.dm | 2 +- code/modules/mob/login.dm | 2 +- code/modules/mob/mob.dm | 10 +- code/modules/mob/mob_helpers.dm | 2 +- .../computers/item/tablet.dm | 4 +- code/modules/tgui/tgui.dm | 8 +- code/modules/tgui_input/alert.dm | 8 +- code/modules/tgui_input/list.dm | 8 +- code/modules/tgui_input/number.dm | 8 +- code/modules/tgui_input/text.dm | 8 +- code/modules/tooltip/tooltip.dm | 2 +- tgui/docs/tgui-for-custom-html-popups.md | 16 +- .../interfaces/PreferencesMenu/MainPage.tsx | 4 +- .../tgui/interfaces/PreferencesMenu/data.ts | 2 +- .../features/character_preferences/pda.tsx | 8 +- .../game_preferences/item_outlines.tsx | 15 +- .../features/game_preferences/screentips.tsx | 20 -- .../preferences/features/randomization.tsx | 57 ++--- .../preferences/features/species_features.tsx | 5 + 78 files changed, 546 insertions(+), 432 deletions(-) delete mode 100644 code/modules/client/preferences/screentips.dm create mode 100644 code/modules/client/preferences_player.dm delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/screentips.tsx diff --git a/beestation.dme b/beestation.dme index 85976b000213f..0ca1eecac787e 100644 --- a/beestation.dme +++ b/beestation.dme @@ -2128,6 +2128,7 @@ #include "code\modules\client\preferences_character.dm" #include "code\modules\client\preferences_database.dm" #include "code\modules\client\preferences_menu.dm" +#include "code\modules\client\preferences_player.dm" #include "code\modules\client\loadout\loadout.dm" #include "code\modules\client\loadout\loadout_accessories.dm" #include "code\modules\client\loadout\loadout_colorizers.dm" @@ -2166,7 +2167,6 @@ #include "code\modules\client\preferences\random.dm" #include "code\modules\client\preferences\runechat.dm" #include "code\modules\client\preferences\scaling_method.dm" -#include "code\modules\client\preferences\screentips.dm" #include "code\modules\client\preferences\security_department.dm" #include "code\modules\client\preferences\skin_tone.dm" #include "code\modules\client\preferences\species.dm" diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index b0edceb5cd1ba..264d7e8f91a4c 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -515,7 +515,7 @@ var/mob/M = C if(M.client) C = M.client - if(!C || (!C.prefs.read_preference(/datum/preference/toggle/window_flashing) && !ignorepref)) + if(!C || (!C.prefs.read_player_preference(/datum/preference/toggle/window_flashing) && !ignorepref)) return winset(C, "mainwindow", "flash=5") diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 5f21a35629607..defa336b4d109 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -245,7 +245,7 @@ GLOBAL_LIST_EMPTY(species_list) * * Checks that `user` does not move, change hands, get stunned, etc. for the * given `delay`. Returns `TRUE` on success or `FALSE` on failure. - * + * * Arguments: * * user - the primary "user" of the do_after. * * delay - how long the do_after takes. Defaults to 3 SECONDS. @@ -710,7 +710,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) while(loop && safety < 5) if(!safety && !banned) - newname = C?.prefs?.read_preference(preference_type) + newname = C?.prefs?.read_player_preference(preference_type) else var/datum/preference/preference = GLOB.preference_entries[preference_type] newname = preference.create_informed_default_value(C.prefs) diff --git a/code/_onclick/hud/action_button.dm b/code/_onclick/hud/action_button.dm index cdf91bb54289d..c8def1368f9df 100644 --- a/code/_onclick/hud/action_button.dm +++ b/code/_onclick/hud/action_button.dm @@ -99,7 +99,7 @@ usr.client.prefs.action_buttons_screen_locs["[name]_[id]"] = locked ? moved : null return TRUE if(modifiers["alt"]) - var/buttons_locked = usr.client.prefs.read_preference(/datum/preference/toggle/buttons_locked) + var/buttons_locked = usr.client.prefs.read_player_preference(/datum/preference/toggle/buttons_locked) for(var/V in usr.actions) var/datum/action/A = V var/atom/movable/screen/movable/action_button/B = A.button diff --git a/code/_onclick/hud/ghost.dm b/code/_onclick/hud/ghost.dm index 230de1afcd050..d9d566d5c656c 100644 --- a/code/_onclick/hud/ghost.dm +++ b/code/_onclick/hud/ghost.dm @@ -89,7 +89,7 @@ if(!.) return var/mob/screenmob = viewmob || mymob - if(screenmob.client.prefs.read_preference(/datum/preference/toggle/ghost_hud)) + if(screenmob.client.prefs.read_player_preference(/datum/preference/toggle/ghost_hud)) screenmob.client.screen += static_inventory else screenmob.client.screen -= static_inventory diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index 7bb19c5db1fb2..d17a4653273ca 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -70,12 +70,12 @@ GLOBAL_LIST_INIT(available_ui_styles, list( if (!ui_style) // will fall back to the default if any of these are null - ui_style = ui_style2icon(owner.client?.prefs?.read_preference(/datum/preference/choiced/ui_style)) + ui_style = ui_style2icon(owner.client?.prefs?.read_player_preference(/datum/preference/choiced/ui_style)) hide_actions_toggle = new hide_actions_toggle.InitialiseIcon(src) if(mymob.client) - hide_actions_toggle.locked = mymob.client.prefs.read_preference(/datum/preference/toggle/buttons_locked) + hide_actions_toggle.locked = mymob.client.prefs.read_player_preference(/datum/preference/toggle/buttons_locked) hand_slots = list() diff --git a/code/_onclick/hud/parallax.dm b/code/_onclick/hud/parallax.dm index 55d09176f40f9..5911d481e93c6 100755 --- a/code/_onclick/hud/parallax.dm +++ b/code/_onclick/hud/parallax.dm @@ -57,7 +57,7 @@ var/mob/screenmob = viewmob || mymob var/client/C = screenmob.client if(C.prefs) - var/pref = C.prefs.read_preference(/datum/preference/choiced/parallax) + var/pref = C.prefs.read_player_preference(/datum/preference/choiced/parallax) if (isnull(pref)) pref = PARALLAX_HIGH switch(pref) diff --git a/code/_onclick/hud/rendering/plane_master.dm b/code/_onclick/hud/rendering/plane_master.dm index b2527bfa837d2..aedcc8f2f2359 100644 --- a/code/_onclick/hud/rendering/plane_master.dm +++ b/code/_onclick/hud/rendering/plane_master.dm @@ -65,7 +65,7 @@ /atom/movable/screen/plane_master/game_world/backdrop(mob/mymob) . = ..() remove_filter("AO") - if(istype(mymob) && mymob.client?.prefs?.read_preference(/datum/preference/toggle/ambient_occlusion)) + if(istype(mymob) && mymob.client?.prefs?.read_player_preference(/datum/preference/toggle/ambient_occlusion)) add_filter("AO", 1, drop_shadow_filter(x = 0, y = -2, size = 4, color = "#04080FAA")) remove_filter("eye_blur") if(istype(mymob) && mymob.eye_blurry) @@ -184,7 +184,7 @@ /atom/movable/screen/plane_master/runechat/backdrop(mob/mymob) . = ..() remove_filter("AO") - if(istype(mymob) && mymob.client?.prefs?.read_preference(/datum/preference/toggle/ambient_occlusion)) + if(istype(mymob) && mymob.client?.prefs?.read_player_preference(/datum/preference/toggle/ambient_occlusion)) add_filter("AO", 1, drop_shadow_filter(x = 0, y = -2, size = 4, color = "#04080FAA")) /atom/movable/screen/plane_master/gravpulse diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm index 6a9edddcd9ae3..ba2cbbe6c936e 100644 --- a/code/_onclick/observer.dm +++ b/code/_onclick/observer.dm @@ -54,7 +54,7 @@ return TRUE else if(IsAdminGhost(user)) attack_ai(user) - else if(user.client.prefs.read_preference(/datum/preference/toggle/inquisitive_ghost)) + else if(user.client.prefs.read_player_preference(/datum/preference/toggle/inquisitive_ghost)) user.examinate(src) return FALSE diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index 10d863244b153..527f892cb2b03 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -436,7 +436,7 @@ SUBSYSTEM_DEF(job) //We couldn't find a job from prefs for this guy. /datum/controller/subsystem/job/proc/HandleUnassigned(mob/dead/new_player/player) - var/jobless_role = player.client.prefs.read_preference(/datum/preference/choiced/jobless_role) + var/jobless_role = player.client.prefs.read_player_preference(/datum/preference/choiced/jobless_role) if(PopcapReached() && !IS_PATRON(player.ckey)) RejectPlayer(player) diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 2c839ec78c133..c432d1e687662 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -343,7 +343,7 @@ GLOBAL_LIST_EMPTY(the_station_areas) var/pmv = CONFIG_GET(flag/preference_map_voting) if(pmv) for (var/client/c in GLOB.clients) - var/vote = c.prefs.read_preference(/datum/preference/choiced/preferred_map) + var/vote = c.prefs.read_player_preference(/datum/preference/choiced/preferred_map) if (!vote) if (global.config.defaultmap) mapvotes[global.config.defaultmap.map_name] += 1 diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm index 1ca0c4da965f1..ba5679e0a45fd 100644 --- a/code/controllers/subsystem/vote.dm +++ b/code/controllers/subsystem/vote.dm @@ -68,7 +68,7 @@ SUBSYSTEM_DEF(vote) else if(mode == "map") for (var/non_voter_ckey in non_voters) var/client/C = non_voters[non_voter_ckey] - var/preferred_map = C.prefs.read_preference(/datum/preference/choiced/preferred_map) + var/preferred_map = C.prefs.read_player_preference(/datum/preference/choiced/preferred_map) if(preferred_map) choices[preferred_map] += 1 greatest_votes = max(greatest_votes, choices[preferred_map]) diff --git a/code/datums/action.dm b/code/datums/action.dm index 29f9f89030848..e5c25ede92010 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -68,7 +68,7 @@ M.actions += src if(M.client) M.client.screen += button - button.locked = M.client.prefs.read_preference(/datum/preference/toggle/buttons_locked) || button.id ? M.client.prefs.action_buttons_screen_locs["[name]_[button.id]"] : FALSE //even if it's not defaultly locked we should remember we locked it before + button.locked = M.client.prefs.read_player_preference(/datum/preference/toggle/buttons_locked) || button.id ? M.client.prefs.action_buttons_screen_locs["[name]_[button.id]"] : FALSE //even if it's not defaultly locked we should remember we locked it before button.moved = button.id ? M.client.prefs.action_buttons_screen_locs["[name]_[button.id]"] : FALSE var/obj/effect/proc_holder/spell/spell_proc_holder = button.linked_action.target if(istype(spell_proc_holder) && spell_proc_holder.text_overlay) diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index eaeab5f50d167..0f368769d61e6 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -186,8 +186,8 @@ src.log_talk(message, LOG_SAY, tag="imaginary friend") // Display message - var/owner_chat_map = owner.client?.prefs.read_preference(/datum/preference/toggle/enable_runechat) && owner.client.prefs.read_preference(/datum/preference/toggle/enable_runechat_non_mobs) - var/friend_chat_map = client?.prefs.read_preference(/datum/preference/toggle/enable_runechat) && client.prefs.read_preference(/datum/preference/toggle/enable_runechat_non_mobs) + var/owner_chat_map = owner.client?.prefs.read_player_preference(/datum/preference/toggle/enable_runechat) && owner.client.prefs.read_player_preference(/datum/preference/toggle/enable_runechat_non_mobs) + var/friend_chat_map = client?.prefs.read_player_preference(/datum/preference/toggle/enable_runechat) && client.prefs.read_player_preference(/datum/preference/toggle/enable_runechat_non_mobs) if (!owner_chat_map) var/mutable_appearance/MA = mutable_appearance('icons/mob/talk.dmi', src, "default[say_test(message)]", FLY_LAYER) MA.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA diff --git a/code/datums/chatmessage.dm b/code/datums/chatmessage.dm index 2f3afc0fbe0f1..99ba843f3c968 100644 --- a/code/datums/chatmessage.dm +++ b/code/datums/chatmessage.dm @@ -458,7 +458,7 @@ /atom/proc/balloon_alert(mob/viewer, text, color = null) if(!viewer?.client) return - switch(viewer.client.prefs.read_preference(/datum/preference/toggle/auto_fit_viewport)) // TODO tgui-prefs show_balloon_alerts + switch(viewer.client.prefs.read_player_preference(/datum/preference/toggle/auto_fit_viewport)) // TODO tgui-prefs show_balloon_alerts if(BALLOON_ALERT_ALWAYS) new /datum/chatmessage/balloon_alert(text, src, viewer, color) if(BALLOON_ALERT_WITH_CHAT) diff --git a/code/datums/keybinding/client.dm b/code/datums/keybinding/client.dm index f86427c6ca144..af8ba5cdb00b4 100644 --- a/code/datums/keybinding/client.dm +++ b/code/datums/keybinding/client.dm @@ -62,4 +62,4 @@ winset(user, "mapwindow.map", "zoom=[PIXEL_SCALING_4X]") /datum/keybinding/client/zoomin/up(client/user) - winset(user, "mapwindow.map", "zoom=[user.prefs.read_preference(/datum/preference/numeric/pixel_size)]") + winset(user, "mapwindow.map", "zoom=[user.prefs.read_player_preference(/datum/preference/numeric/pixel_size)]") diff --git a/code/datums/mind.dm b/code/datums/mind.dm index e84b19a512272..b362b33514a74 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -307,7 +307,7 @@ var/obj/item/uplink_loc var/implant = FALSE - var/uplink_spawn_location = traitor_mob.client?.prefs?.read_preference(/datum/preference/choiced/uplink_location) + var/uplink_spawn_location = traitor_mob.client?.prefs?.read_character_preference(/datum/preference/choiced/uplink_location) switch(uplink_spawn_location) if(UPLINK_PDA) uplink_loc = PDA diff --git a/code/datums/view.dm b/code/datums/view.dm index 6053297541f87..7cc142149aecb 100644 --- a/code/datums/view.dm +++ b/code/datums/view.dm @@ -26,10 +26,10 @@ winset(chief, "mapwindow.map", "zoom=0") /datum/viewData/proc/resetFormat() - winset(chief, "mapwindow.map", "zoom=[chief.prefs.read_preference(/datum/preference/numeric/pixel_size)]") + winset(chief, "mapwindow.map", "zoom=[chief.prefs.read_player_preference(/datum/preference/numeric/pixel_size)]") /datum/viewData/proc/setZoomMode() - winset(chief, "mapwindow.map", "zoom-mode=[chief.prefs.read_preference(/datum/preference/choiced/scaling_method)]") + winset(chief, "mapwindow.map", "zoom-mode=[chief.prefs.read_player_preference(/datum/preference/choiced/scaling_method)]") /datum/viewData/proc/isZooming() return (width || height) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 5252f45ff7d0a..6ff05401d0873 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1044,8 +1044,8 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) openToolTip(user,src,params,title = name,content = "[desc]
    Force: [force_string]",theme = "") /obj/item/MouseEntered(location, control, params) - if((item_flags & PICKED_UP || item_flags & IN_STORAGE) && usr.client.prefs.read_preference(/datum/preference/toggle/enable_tooltips) && !QDELETED(src)) - var/timedelay = usr.client.prefs.read_preference(/datum/preference/numeric/tooltip_delay)/100 + if((item_flags & PICKED_UP || item_flags & IN_STORAGE) && usr.client.prefs.read_player_preference(/datum/preference/toggle/enable_tooltips) && !QDELETED(src)) + var/timedelay = usr.client.prefs.read_player_preference(/datum/preference/numeric/tooltip_delay)/100 var/user = usr tip_timer = addtimer(CALLBACK(src, PROC_REF(openTip), location, control, params, user), timedelay, TIMER_STOPPABLE)//timer takes delay in deciseconds, but the pref is in milliseconds. dividing by 100 converts it. var/mob/living/L = usr @@ -1066,11 +1066,11 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) /obj/item/proc/apply_outline(colour = null) if(!(item_flags & PICKED_UP || item_flags & IN_STORAGE) || QDELETED(src) || isobserver(usr)) return - if(!usr.client?.prefs?.read_preference(/datum/preference/toggle/item_outlines)) + if(!usr.client?.prefs?.read_player_preference(/datum/preference/toggle/item_outlines)) return if(!colour) if(usr?.client?.prefs) - colour = usr.client.prefs.read_preference(/datum/preference/color/outline_color) + colour = usr.client.prefs.read_player_preference(/datum/preference/color/outline_color) else colour = COLOR_BLUE_GRAY add_filter(HOVER_OUTLINE_FILTER, 1, list(type="outline", size=1, color=colour)) diff --git a/code/modules/admin/poll_management.dm b/code/modules/admin/poll_management.dm index 593833ac84aed..9f7bb8e12c940 100644 --- a/code/modules/admin/poll_management.dm +++ b/code/modules/admin/poll_management.dm @@ -328,7 +328,7 @@ SELECT p.text, pv.rating, COUNT(*) output += "
    " var/datum/browser/panel = new(usr, "pmpanel", "Poll Management Panel", 780, 640) panel.add_stylesheet("admin_panelscss", 'html/admin/admin_panels.css') - if(usr.client.prefs.read_preference(/datum/preference/toggle/tgui_fancy)) //some browsers (IE8) have trouble with unsupported css3 elements that break the panel's functionality, so we won't load those if a user is in no frills tgui mode since that's for similar compatability support + if(usr.client.prefs.read_player_preference(/datum/preference/toggle/tgui_fancy)) //some browsers (IE8) have trouble with unsupported css3 elements that break the panel's functionality, so we won't load those if a user is in no frills tgui mode since that's for similar compatability support panel.add_stylesheet("admin_panelscss3", 'html/admin/admin_panels_css3.css') panel.set_content(jointext(output, "")) panel.open() @@ -638,7 +638,7 @@ SELECT p.text, pv.rating, COUNT(*) panel_height = 320 var/datum/browser/panel = new(usr, "popanel", "Poll Option Panel", 370, panel_height) panel.add_stylesheet("admin_panelscss", 'html/admin/admin_panels.css') - if(usr.client.prefs.read_preference(/datum/preference/toggle/tgui_fancy)) //some browsers (IE8) have trouble with unsupported css3 elements that break the panel's functionality, so we won't load those if a user is in no frills tgui mode since that's for similar compatability support + if(usr.client.prefs.read_player_preference(/datum/preference/toggle/tgui_fancy)) //some browsers (IE8) have trouble with unsupported css3 elements that break the panel's functionality, so we won't load those if a user is in no frills tgui mode since that's for similar compatability support panel.add_stylesheet("admin_panelscss3", 'html/admin/admin_panels_css3.css') panel.set_content(jointext(output, "")) panel.open() diff --git a/code/modules/admin/sql_ban_system.dm b/code/modules/admin/sql_ban_system.dm index ad50ef9e0bbba..29489d529f42c 100644 --- a/code/modules/admin/sql_ban_system.dm +++ b/code/modules/admin/sql_ban_system.dm @@ -148,7 +148,7 @@ var/datum/browser/panel = new(usr, "banpanel", "Banning Panel", 910, panel_height) panel.add_stylesheet("admin_panelscss", 'html/admin/admin_panels.css') panel.add_stylesheet("banpanelcss", 'html/admin/banpanel.css') - var/tgui_fancy = usr.client.prefs.read_preference(/datum/preference/toggle/tgui_fancy) + var/tgui_fancy = usr.client.prefs.read_player_preference(/datum/preference/toggle/tgui_fancy) if(tgui_fancy) //some browsers (IE8) have trouble with unsupported css3 elements and DOM methods that break the panel's functionality, so we won't load those if a user is in no frills tgui mode since that's for similar compatability support panel.add_stylesheet("admin_panelscss3", 'html/admin/admin_panels_css3.css') panel.add_script("banpaneljs", 'html/admin/banpanel.js') diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index aca69c2dd5cc0..86189f0332ac5 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -15,7 +15,7 @@ mob.log_talk(msg, LOG_ASAY) msg = keywords_lookup(msg) - var/asay_color = prefs.read_preference(/datum/preference/color/asay_color) + var/asay_color = prefs.read_player_preference(/datum/preference/color/asay_color) var/custom_asay_color = (CONFIG_GET(flag/allow_admin_asaycolor) && asay_color) ? "" : "" msg = "ADMIN: [key_name(usr, 1)] [ADMIN_FLW(mob)]: [custom_asay_color][msg][custom_asay_color ? "":null]" to_chat(GLOB.admins, msg, allow_linkify = TRUE, type = MESSAGE_TYPE_ADMINCHAT) diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm index 0103ccb27bf45..aa5b0536e6c78 100644 --- a/code/modules/antagonists/changeling/changeling.dm +++ b/code/modules/antagonists/changeling/changeling.dm @@ -347,7 +347,7 @@ var/mob/living/carbon/C = owner.current //only carbons have dna now, so we have to typecaste if(isipc(C)) C.set_species(/datum/species/human) - C.fully_replace_character_name(C.real_name, C.client.prefs.read_preference(/datum/preference/name/backup_human)) + C.fully_replace_character_name(C.real_name, C.client.prefs.read_character_preference(/datum/preference/name/backup_human)) for(var/datum/data/record/E in GLOB.data_core.general) if(E.fields["name"] == C.real_name) E.fields["species"] = "\improper Human" diff --git a/code/modules/antagonists/ert/ert.dm b/code/modules/antagonists/ert/ert.dm index d73f4a8939a73..d9ebcb4cbae68 100644 --- a/code/modules/antagonists/ert/ert.dm +++ b/code/modules/antagonists/ert/ert.dm @@ -38,7 +38,7 @@ /datum/antagonist/ert/proc/update_name() var/name = pick(name_source) if (!name) - name = owner.current.client?.prefs.read_preference(/datum/preference/name/backup_human) || pick(GLOB.last_names) + name = owner.current.client?.prefs.read_character_preference(/datum/preference/name/backup_human) || pick(GLOB.last_names) owner.current.fully_replace_character_name(owner.current.real_name, "[role] [name]") /datum/antagonist/ert/deathsquad/New() diff --git a/code/modules/asset_cache/asset_list.dm b/code/modules/asset_cache/asset_list.dm index 31c5955411754..df793e8984bb1 100644 --- a/code/modules/asset_cache/asset_list.dm +++ b/code/modules/asset_cache/asset_list.dm @@ -518,6 +518,8 @@ GLOBAL_LIST_EMPTY(asset_datums) var/name /datum/asset/json/send(client) + if(!..()) + return FALSE return SSassets.transport.send_assets(client, "data/[name].json") /datum/asset/json/get_url_mappings() diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index fd0df3d89ce15..8486d93ea1ba0 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -1033,7 +1033,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( if (isliving(mob)) var/mob/living/M = mob M.update_damage_hud() - if (prefs.read_preference(/datum/preference/toggle/auto_fit_viewport)) + if (prefs.read_player_preference(/datum/preference/toggle/auto_fit_viewport)) addtimer(CALLBACK(src,.verb/fit_viewport,10)) //Delayed to avoid wingets from Login calls. /client/proc/generate_clickcatcher() diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 07797708cb6e8..469d9ab6c7685 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -9,6 +9,10 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/max_save_slots = 3 /// The current active character slot var/selected_slot = 1 + /// Cache for the current active character slot + var/datum/preferences_holder/preferences_character/character_data + /// Cache for player datumized preferences + var/datum/preferences_holder/preferences_player/player_data /// Bitflags for communications that are muted var/muted = NONE @@ -59,12 +63,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/action_buttons_screen_locs = list() - ///Someone thought we were nice! We get a little heart in OOC until we join the server past the below time (we can keep it until the end of the round otherwise) - var/hearted - ///If we have a hearted commendations, we honor it every time the player loads preferences until this time has been passed - var/hearted_until - /// If we have persistent scars enabled - var/persistent_scars = TRUE ///What outfit typepaths we've favorited in the SelectEquipment menu var/list/favorite_outfits = list() @@ -77,17 +75,14 @@ GLOBAL_LIST_EMPTY(preferences_datums) /// A list of keys that have been updated since the last save. var/list/recently_updated_keys = list() - /// A cache of preference entries to values. - /// Used to avoid expensive database query every time a preference is retrieved. - var/list/value_cache = list() - /// If set to TRUE, will update character_profiles on the next ui_data tick. var/tainted_character_profiles = FALSE /datum/preferences/Destroy(force, ...) QDEL_NULL(character_preview_view) QDEL_LIST(middleware) - value_cache = null + QDEL_NULL(character_data) + QDEL_NULL(player_data) return ..() /datum/preferences/New(client/parent) @@ -120,7 +115,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) //we couldn't load character data so just randomize the character appearance + name randomise_appearance_prefs() //let's create a random character then - rather than a fat, bald and naked man. if(parent) - apply_all_client_preferences() + //apply_all_client_preferences() parent.set_macros() if(!loaded_preferences_successfully) @@ -335,15 +330,12 @@ GLOBAL_LIST_EMPTY(preferences_datums) return preferences -/// Applies all PREFERENCE_PLAYER preferences, ignoring cached values +/// Applies all PREFERENCE_PLAYER preferences immediately /datum/preferences/proc/apply_all_client_preferences() for (var/datum/preference/preference as anything in get_preferences_in_priority_order()) if (preference.preference_type != PREFERENCE_PLAYER) continue - - // Remove the cached value - value_cache -= preference.type - preference.apply_to_client(parent, read_preference(preference.type)) + preference.apply_to_client(parent, read_player_preference(preference.type)) // This is necessary because you can open the set preferences menu before // the atoms SS is done loading. @@ -487,7 +479,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/character_preview_view) if (preference.preference_type != PREFERENCE_CHARACTER) continue - preference.apply_to_human(character, read_preference(preference.type)) + preference.apply_to_human(character, read_character_preference(preference.type)) character.dna.real_name = character.real_name @@ -496,18 +488,6 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/character_preview_view) character.update_hair() character.update_body_parts() - -/// Returns whether the parent mob should have the random hardcore settings enabled. Assumes it has a mind. -/datum/preferences/proc/should_be_random_hardcore(datum/job/job, datum/mind/mind) - if(!read_preference(/datum/preference/toggle/random_hardcore)) - return FALSE - if(job.departments & DEPT_BITFLAG_COM) //No command staff - return FALSE - for(var/datum/antagonist/antag as anything in mind.antag_datums) - if(antag.get_team()) //No team antags - return FALSE - return TRUE - /// Inverts the key_bindings list such that it can be used for key_bindings_by_key /datum/preferences/proc/get_key_bindings_by_key(list/key_bindings) var/list/output = list() diff --git a/code/modules/client/preferences/_preference.dm b/code/modules/client/preferences/_preference.dm index fb47368192d61..bc5eacf026e26 100644 --- a/code/modules/client/preferences/_preference.dm +++ b/code/modules/client/preferences/_preference.dm @@ -102,6 +102,9 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) /// will show the feature as selectable. var/relevant_species_trait = null + /// If this requires create_informed_default_value + var/informed = FALSE + /// Called on the saved input when retrieving. /// Also called by the value sent from the user through UI. Do not trust it. /// Input is the value inside the database, output is to tell other code @@ -149,58 +152,6 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) SHOULD_NOT_OVERRIDE(TRUE) return preference_type == PREFERENCE_CHARACTER && can_randomize -/// Given a ckey, return either the saved data or an acceptable default. -/// This will write to the database if a value was not found with the new value. -/datum/preference/proc/read(ckey, datum/preferences/preferences, slot) - SHOULD_NOT_OVERRIDE(TRUE) - - var/value - - if(SSdbcore.IsConnected()) - var/datum/DBQuery/Q - if(preference_type == PREFERENCE_CHARACTER) - Q = SSdbcore.NewQuery( - "SELECT slot, :ptag FROM [format_table_name("characters")] WHERE ckey=:ckey AND slot=:slot", - list("ckey" = ckey, "slot" = slot, "ptag" = db_key) - ) - else - Q = SSdbcore.NewQuery( - "SELECT CAST(preference_tag AS CHAR) AS ptag, preference_value FROM [format_table_name("preferences")] WHERE ckey=:ckey AND ptag=:ptag", - list("ckey" = ckey, "ptag" = db_key) - ) - if(!Q.warn_execute()) - qdel(Q) - return - if(Q.NextRow()) - value = Q.item[2] - qdel(Q) - - if (isnull(value)) - return null - else - return deserialize(value, preferences) - -/// Given a ckey, writes the inputted value to the database -/// Returns TRUE for a successful application. -/// Return FALSE if it is invalid. -/datum/preference/proc/write(ckey, value, slot) - SHOULD_NOT_OVERRIDE(TRUE) - - if (!is_valid(value)) - return FALSE - - if(SSdbcore.IsConnected()) - var/serialized_value = serialize(value) - var/datum/DBQuery/Q - if(preference_type == PREFERENCE_CHARACTER) - Q = SSdbcore.NewQuery("REPLACE INTO [format_table_name("characters")] (ckey, slot, :ptag) VALUES (:ckey, :slot, :pvalue)", list("ckey" = ckey, "slot" = slot, "ptag" = db_key, "pvalue" = serialized_value)) - else - Q = SSdbcore.NewQuery("INSERT INTO [format_table_name("preferences")] (ckey, preference_tag, preference_value) VALUES (:ckey, :ptag, :pvalue) ON DUPLICATE KEY UPDATE preference_value=:pvalue2", list("ckey" = ckey, "ptag" = db_key, "pvalue" = serialized_value, "pvalue2" = serialized_value)) - if(!Q.Execute(async = TRUE)) - qdel(Q) - - return TRUE - /// Apply this preference onto the given client. /// Called when the preference_type == PREFERENCE_PLAYER. /datum/preference/proc/apply_to_client(client/client, value) @@ -222,11 +173,15 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) SHOULD_CALL_PARENT(FALSE) CRASH("`apply_to_human()` was not implemented for [type]!") -/// Read a /datum/preference type and return its value. -/// This will write to the database with the default value if a value was not found. -/// Specifying a slot will query the database directly, only do this if absolutely necessary -/datum/preferences/proc/read_preference(preference_type, slot = null) - var/datum/preference/preference_entry = GLOB.preference_entries[preference_type] +/datum/preferences/proc/get_preference_holder(datum/preference/preference_entry) + RETURN_TYPE(/datum/preferences_holder) + if(preference_entry.preference_type == PREFERENCE_CHARACTER) + return character_data + return player_data + +/// Read a /datum/preference type and return its value, only using cached values and queueing any necessary writes. +/datum/preferences/proc/read_preference(preference_typepath) + var/datum/preference/preference_entry = GLOB.preference_entries[preference_typepath] if (isnull(preference_entry)) var/extra_info = "" @@ -235,46 +190,61 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) if (!isnull(Master.current_initializing_subsystem)) extra_info = "Info was attempted to be retrieved while [Master.current_initializing_subsystem] was initializing." - CRASH("Preference type `[preference_type]` is invalid! [extra_info]") + CRASH("Preference type `[preference_typepath]` is invalid! [extra_info]") + return get_preference_holder(preference_entry).read_preference(src, preference_entry) - if ((isnull(slot) || slot == selected_slot) && (preference_type in value_cache)) - return value_cache[preference_type] +/// Read a /datum/preference type and return its value, only using cached values and queueing any necessary writes. +/// Only works for player preferences. +/datum/preferences/proc/read_player_preference(preference_typepath) + var/datum/preference/preference_entry = GLOB.preference_entries[preference_typepath] + if (isnull(preference_entry)) + var/extra_info = "" - var/value = preference_entry.read(parent.ckey, src, !isnull(slot) ? slot : selected_slot) - if (isnull(value)) - value = preference_entry.create_informed_default_value(src) - if (write_preference(preference_entry, value)) - return value - else - CRASH("Couldn't write the default value for [preference_type] (received [value])") - value_cache[preference_type] = value - return value + // Current initializing subsystem is important to know because it might be a problem with + // things running pre-assets-initialization. + if (!isnull(Master.current_initializing_subsystem)) + extra_info = "Info was attempted to be retrieved while [Master.current_initializing_subsystem] was initializing." + + CRASH("Preference type `[preference_typepath]` is invalid! [extra_info]") + + if (preference_entry.preference_type == PREFERENCE_CHARACTER) + CRASH("read_player_preference called on PREFERENCE_CHARACTER type preference [preference_typepath].") + + return player_data.read_preference(src, preference_entry) + +/// Read a /datum/preference type and return its value, only using cached values and queueing any necessary writes. +/// Only works for character preferences. +/datum/preferences/proc/read_character_preference(preference_typepath) + var/datum/preference/preference_entry = GLOB.preference_entries[preference_typepath] + if (isnull(preference_entry)) + var/extra_info = "" + + // Current initializing subsystem is important to know because it might be a problem with + // things running pre-assets-initialization. + if (!isnull(Master.current_initializing_subsystem)) + extra_info = "Info was attempted to be retrieved while [Master.current_initializing_subsystem] was initializing." + + CRASH("Preference type `[preference_typepath]` is invalid! [extra_info]") + + if (preference_entry.preference_type == PREFERENCE_PLAYER) + CRASH("read_character_preference called on PREFERENCE_PLAYER type preference [preference_typepath].") + + return character_data.read_preference(src, preference_entry) /// Set a /datum/preference entry. /// Returns TRUE for a successful preference application. /// Returns FALSE if it is invalid. /datum/preferences/proc/write_preference(datum/preference/preference, preference_value) - var/new_value = preference.deserialize(preference_value, src) - var/success = preference.write(parent.ckey, new_value, selected_slot) - if (success) - value_cache[preference.type] = new_value - return success + return get_preference_holder(preference).write_preference(src, preference, preference_value) -/// Will perform an update on the preference, but not write to the savefile. +/// Will perform a write on the preference and update the relevant locations. /// This will, for instance, update the character preference view. /// Performs sanity checks. /datum/preferences/proc/update_preference(datum/preference/preference, preference_value) if (!preference.is_accessible(src)) return FALSE - var/new_value = preference.deserialize(preference_value, src) - var/success = preference.write(null, new_value, null) - - if (!success) - return FALSE - - recently_updated_keys |= preference.type - value_cache[preference.type] = new_value + write_preference(preference, preference_value) if (preference.preference_type == PREFERENCE_PLAYER) preference.apply_to_client_updated(parent, read_preference(preference.type)) @@ -310,7 +280,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) SHOULD_NOT_SLEEP(TRUE) // if (!isnull(relevant_mutant_bodypart) || !isnull(relevant_species_trait)) -// var/species_type = preferences.read_preference(/datum/preference/choiced/species) +// var/species_type = preferences.read_character_preference(/datum/preference/choiced/species) // var/datum/species/species = new species_type // TODO tgui-prefs if (!(db_key in species.get_features())) diff --git a/code/modules/client/preferences/body_type.dm b/code/modules/client/preferences/body_type.dm index 5b6709804d253..3d8f8e42b1360 100644 --- a/code/modules/client/preferences/body_type.dm +++ b/code/modules/client/preferences/body_type.dm @@ -17,5 +17,5 @@ if (!..(preferences)) return FALSE - var/gender = preferences.read_preference(/datum/preference/choiced/gender) + var/gender = preferences.read_character_preference(/datum/preference/choiced/gender) return gender != MALE && gender != FEMALE diff --git a/code/modules/client/preferences/clothing.dm b/code/modules/client/preferences/clothing.dm index ab9006daa80fa..762b4963c524b 100644 --- a/code/modules/client/preferences/clothing.dm +++ b/code/modules/client/preferences/clothing.dm @@ -139,7 +139,7 @@ if (!..(preferences)) return FALSE - var/species_type = preferences.read_preference(/datum/preference/choiced/species) + var/species_type = preferences.read_character_preference(/datum/preference/choiced/species) var/datum/species/species = new species_type return !(NO_UNDERWEAR in species.species_traits) diff --git a/code/modules/client/preferences/item_outlines.dm b/code/modules/client/preferences/item_outlines.dm index 7f13f38d3ae24..1a7a68019ecc5 100644 --- a/code/modules/client/preferences/item_outlines.dm +++ b/code/modules/client/preferences/item_outlines.dm @@ -2,3 +2,8 @@ category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "itemoutline_pref" preference_type = PREFERENCE_PLAYER + +/datum/preference/color/outline_color + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + db_key = "outline_color" + preference_type = PREFERENCE_PLAYER diff --git a/code/modules/client/preferences/middleware/random.dm b/code/modules/client/preferences/middleware/random.dm index e8efba2a54ae3..bc72ee59fc130 100644 --- a/code/modules/client/preferences/middleware/random.dm +++ b/code/modules/client/preferences/middleware/random.dm @@ -63,7 +63,7 @@ var/requested_randomization = randomise[preference.db_key] if (istype(preference, /datum/preference/name)) - requested_randomization = read_preference(/datum/preference/choiced/random_name) + requested_randomization = read_character_preference(/datum/preference/choiced/random_name) switch (requested_randomization) if (RANDOM_ENABLED) diff --git a/code/modules/client/preferences/names.dm b/code/modules/client/preferences/names.dm index bd1f3bffdd2f4..a6a78fbeb21be 100644 --- a/code/modules/client/preferences/names.dm +++ b/code/modules/client/preferences/names.dm @@ -38,14 +38,15 @@ // The `_` makes it first in ABC order. group = "_real_name" db_key = "real_name" + informed = TRUE /datum/preference/name/real_name/apply_to_human(mob/living/carbon/human/target, value) target.real_name = value target.name = value /datum/preference/name/real_name/create_informed_default_value(datum/preferences/preferences) - var/species_type = preferences.read_preference(/datum/preference/choiced/species) - var/gender = preferences.read_preference(/datum/preference/choiced/gender) + var/species_type = preferences.read_character_preference(/datum/preference/choiced/species) + var/gender = preferences.read_character_preference(/datum/preference/choiced/gender) var/datum/species/species = new species_type @@ -56,7 +57,7 @@ if (!input) return input - if (CONFIG_GET(flag/humans_need_surnames) && preferences.read_preference(/datum/preference/choiced/species) == /datum/species/human) + if (CONFIG_GET(flag/humans_need_surnames) && preferences.read_character_preference(/datum/preference/choiced/species) == /datum/species/human) var/first_space = findtext(input, " ") if(!first_space) //we need a surname input += " [pick(GLOB.last_names)]" @@ -70,9 +71,10 @@ explanation = "Backup human name" group = "backup_human" db_key = "human_name" + informed = TRUE /datum/preference/name/backup_human/create_informed_default_value(datum/preferences/preferences) - var/gender = preferences.read_preference(/datum/preference/choiced/gender) + var/gender = preferences.read_character_preference(/datum/preference/choiced/gender) return random_unique_name(gender) diff --git a/code/modules/client/preferences/pda.dm b/code/modules/client/preferences/pda.dm index da53ead9bab30..6f2842e93c4bd 100644 --- a/code/modules/client/preferences/pda.dm +++ b/code/modules/client/preferences/pda.dm @@ -15,8 +15,8 @@ /datum/preference/choiced/pda_theme/deserialize(input, datum/preferences/preferences) for(var/key in GLOB.ntos_device_themes_default) // if only there was a associative list flip proc that I could locate. Unfortunately, I could not locate one. - if(GLOB.ntos_device_themes_default[key] == input) - return key + if(GLOB.ntos_device_themes_default[key] == input || key == input) + return input return "NtOS Default" /datum/preference/choiced/pda_theme/serialize(input) @@ -26,4 +26,4 @@ return "NtOS Default" /datum/preference/choiced/pda_theme/init_possible_values() - return GLOB.ntos_device_themes_default_content + return GLOB.ntos_device_themes_default diff --git a/code/modules/client/preferences/random.dm b/code/modules/client/preferences/random.dm index 7518620715e1c..72c098056d268 100644 --- a/code/modules/client/preferences/random.dm +++ b/code/modules/client/preferences/random.dm @@ -17,24 +17,6 @@ /datum/preference/choiced/random_body/create_default_value() return RANDOM_DISABLED -/datum/preference/toggle/random_hardcore - category = PREFERENCE_CATEGORY_NON_CONTEXTUAL - db_key = "random_hardcore" - preference_type = PREFERENCE_CHARACTER - can_randomize = FALSE - default_value = FALSE - -/datum/preference/toggle/random_hardcore/apply_to_human(mob/living/carbon/human/target, value) - return - -// TODO tgui-prefs -/* -/datum/preference/toggle/random_hardcore/is_accessible(datum/preferences/preferences) - if (!..(preferences)) - return FALSE - - return preferences.parent.get_exp_living(pure_numeric = TRUE) >= PLAYTIME_HARDCORE_RANDOM -*/ /datum/preference/choiced/random_name category = PREFERENCE_CATEGORY_NON_CONTEXTUAL db_key = "name_is_always_random" diff --git a/code/modules/client/preferences/screentips.dm b/code/modules/client/preferences/screentips.dm deleted file mode 100644 index e1176b9fa3d56..0000000000000 --- a/code/modules/client/preferences/screentips.dm +++ /dev/null @@ -1,6 +0,0 @@ -/datum/preference/color/outline_color - category = PREFERENCE_CATEGORY_GAME_PREFERENCES - db_key = "outline_color" - preference_type = PREFERENCE_PLAYER - -// TODO tgui-prefs diff --git a/code/modules/client/preferences/skin_tone.dm b/code/modules/client/preferences/skin_tone.dm index ab230af45ca41..9815b04a44319 100644 --- a/code/modules/client/preferences/skin_tone.dm +++ b/code/modules/client/preferences/skin_tone.dm @@ -32,5 +32,5 @@ if (!..(preferences)) return FALSE - var/datum/species/species_type = preferences.read_preference(/datum/preference/choiced/species) + var/datum/species/species_type = preferences.read_character_preference(/datum/preference/choiced/species) return initial(species_type.use_skintones) diff --git a/code/modules/client/preferences/species_features/basic.dm b/code/modules/client/preferences/species_features/basic.dm index 21141983b79a7..935df1a91ceb2 100644 --- a/code/modules/client/preferences/species_features/basic.dm +++ b/code/modules/client/preferences/species_features/basic.dm @@ -99,3 +99,33 @@ data[SUPPLEMENTAL_FEATURE_KEY] = "hair_color" return data + +/datum/preference/color_legacy/gradient_color + db_key = "gradient_color" + preference_type = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES + relevant_species_trait = HAIR + +/datum/preference/color_legacy/gradient_color/apply_to_human(mob/living/carbon/human/target, value) + target.gradient_color = value + +/datum/preference/choiced/gradient_style + db_key = "gradient_style" + preference_type = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_FEATURES + main_feature_name = "Gradient Style" + should_generate_icons = TRUE + relevant_species_trait = HAIR + +/datum/preference/choiced/gradient_style/init_possible_values() + return generate_possible_values_for_sprite_accessories_on_head(GLOB.hair_gradients_list) + +/datum/preference/choiced/gradient_style/apply_to_human(mob/living/carbon/human/target, value) + target.gradient_style = value + +/datum/preference/choiced/gradient_style/compile_constant_data() + var/list/data = ..() + + data[SUPPLEMENTAL_FEATURE_KEY] = "gradient_color" + + return data diff --git a/code/modules/client/preferences/species_features/ethereal.dm b/code/modules/client/preferences/species_features/ethereal.dm index a9eeac657df0b..8ae45f2ef0c0d 100644 --- a/code/modules/client/preferences/species_features/ethereal.dm +++ b/code/modules/client/preferences/species_features/ethereal.dm @@ -1,4 +1,4 @@ -/datum/preference/choiced/ethereal_color +/*/datum/preference/choiced/ethereal_color db_key = "feature_ethcolor" preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES @@ -31,3 +31,4 @@ /datum/preference/choiced/ethereal_color/apply_to_human(mob/living/carbon/human/target, value) target.dna.features["ethcolor"] = GLOB.color_list_ethereal[value] +*/ diff --git a/code/modules/client/preferences/species_features/felinid.dm b/code/modules/client/preferences/species_features/felinid.dm index 7482af71c8619..3f20d0780a78a 100644 --- a/code/modules/client/preferences/species_features/felinid.dm +++ b/code/modules/client/preferences/species_features/felinid.dm @@ -1,4 +1,4 @@ -/datum/preference/choiced/tail_human +/*/datum/preference/choiced/tail_human db_key = "feature_human_tail" preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES @@ -31,3 +31,4 @@ /datum/preference/choiced/ears/create_default_value() var/datum/sprite_accessory/ears/cat/ears = /datum/sprite_accessory/ears/cat return initial(ears.name) +*/ diff --git a/code/modules/client/preferences/species_features/lizard.dm b/code/modules/client/preferences/species_features/lizard.dm index 9a34baa75992c..9662e93ae840b 100644 --- a/code/modules/client/preferences/species_features/lizard.dm +++ b/code/modules/client/preferences/species_features/lizard.dm @@ -1,4 +1,4 @@ -/proc/generate_lizard_side_shots(list/sprite_accessories, key, include_snout = TRUE) +/*/proc/generate_lizard_side_shots(list/sprite_accessories, key, include_snout = TRUE) var/list/values = list() var/icon/lizard = icon('icons/mob/human_parts_greyscale.dmi', "lizard_head_m", EAST) @@ -139,3 +139,4 @@ /datum/preference/choiced/lizard_tail/apply_to_human(mob/living/carbon/human/target, value) target.dna.features["tail_lizard"] = value +*/ diff --git a/code/modules/client/preferences/species_features/moth.dm b/code/modules/client/preferences/species_features/moth.dm index d99576cb80d08..22047cb7ed70c 100644 --- a/code/modules/client/preferences/species_features/moth.dm +++ b/code/modules/client/preferences/species_features/moth.dm @@ -1,4 +1,4 @@ -/datum/preference/choiced/moth_antennae +/*/datum/preference/choiced/moth_antennae db_key = "feature_moth_antennae" preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES @@ -100,3 +100,4 @@ /datum/preference/choiced/moth_wings/apply_to_human(mob/living/carbon/human/target, value) target.dna.features["moth_wings"] = value +*/ diff --git a/code/modules/client/preferences/species_features/mutants.dm b/code/modules/client/preferences/species_features/mutants.dm index 0a1897793aca9..ac5c333b42fb1 100644 --- a/code/modules/client/preferences/species_features/mutants.dm +++ b/code/modules/client/preferences/species_features/mutants.dm @@ -1,4 +1,4 @@ -/datum/preference/color_legacy/mutant_color +/*/datum/preference/color_legacy/mutant_color db_key = "feature_mcolor" preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES @@ -18,3 +18,4 @@ return FALSE return TRUE +*/ diff --git a/code/modules/client/preferences/underwear_color.dm b/code/modules/client/preferences/underwear_color.dm index 5c892b022e550..f0a8df5720ebe 100644 --- a/code/modules/client/preferences/underwear_color.dm +++ b/code/modules/client/preferences/underwear_color.dm @@ -10,6 +10,6 @@ if (!..(preferences)) return FALSE - var/species_type = preferences.read_preference(/datum/preference/choiced/species) + var/species_type = preferences.read_character_preference(/datum/preference/choiced/species) var/datum/species/species = new species_type return !(NO_UNDERWEAR in species.species_traits) diff --git a/code/modules/client/preferences_character.dm b/code/modules/client/preferences_character.dm index 0189fb897b840..23c86430f30d4 100644 --- a/code/modules/client/preferences_character.dm +++ b/code/modules/client/preferences_character.dm @@ -1,90 +1,145 @@ -/datum/preference_character - // Meta Vars // +/// A cache for character preferences data +/datum/preferences_holder/preferences_character /// INT: Slot number. Used for internal tracking. The slot number also correspnds to the number of slots in the characters list var/slot_number = 0 - /// BOOL: Is this slot locked, likely due to not having enough character slots available - var/slot_locked = FALSE - /// STRING: The name of the character. - var/real_name - /// BOOL: If the name should be randomized - var/be_random_name = FALSE - /// BOOL: If the body should be randomized - var/be_random_body = FALSE - /// ENUM: The gender of the character - var/gender = MALE - /// INT: How old the character is - var/age = 30 - /// STRING: What underwear type the character should use - var/underwear = "Nude" - /// STRING: The color of the underwear - var/underwear_color = "000" - /// STRING: What undershirt type the character should use - var/undershirt = "Nude" - /// STRING: What socks type the character should use - var/socks = "Nude" - /// STRING: (Plasmaman) What helmet type the character should spawn with - var/helmet_style = HELMET_DEFAULT - /// STRING: What backpack style the character should spawn with - var/backbag = DBACKPACK - /// STRING: What jumpsuit style the character should spawn with (suit/skirt) - var/jumpsuit_style = PREF_SUIT - /// STRING: What hair style the character should use - var/hair_style = "Bald" - /// STRING: What hair color the character should use - var/hair_color = "000" - /// STRING: What hair gradient color the character should use - var/gradient_color = "000" - /// STRING: What hair gradient style the character should use - var/gradient_style = "None" - /// STRING: What facial hair style the character should use - var/facial_hair_style = "Shaved" - /// STRING: What facial hair color the character should use - var/facial_hair_color = "000" - /// STRING: What skin tone the character should use - var/skin_tone = "caucasian1" - /// STRING: What eye color the character should use - var/eye_color = "000" - /// /datum/species: What species datum the character should spawn as - var/datum/species/pref_species - /// list: A relational list of features the character should spawn with, used for species specific data. - var/list/features = list( - "body_size" = "Normal", - "mcolor" = "FFF", - "ethcolor" = "9c3030", - "tail_lizard" = "Smooth", - "tail_human" = "None", - "snout" = "Round", - "horns" = "None", - "ears" = "None", - "wings" = "None", - "frills" = "None", - "spines" = "None", - "body_markings" = "None", - "legs" = "Normal Legs", - "moth_wings" = "Plain", - "moth_antennae" = "Plain", - "moth_markings" = "None", - "ipc_screen" = "Blue", - "ipc_antenna" = "None", - "ipc_chassis" = "Morpheus Cyberkinetics(Greyscale)", - "insect_type" = "Common Fly", - "apid_antenna" = "Curled", - "apid_stripes" = "Thick", - "apid_headstripes" = "Thick", - "body_model" = MALE - ) - /// list: A relational list of special name types to name values - var/list/custom_names = list() - /// STRING: What AI core display should be used - var/preferred_ai_core_display = "Blue" - /// STRING: What security department assignment is preferred - var/preferred_security_department = SEC_DEPT_RANDOM - /// list: List of all selected quirks - var/list/all_quirks = list() - var/list/job_preferences = list() - /// list: List of all selected loadout equipment - var/list/equipped_gear = list() - /// STRING: What to do when the job you select is not available - var/joblessrole = BERANDOMJOB //defaults to 1 for fewer assistants - /// STRING: Where your uplink should spawn as traitor - var/uplink_spawn_loc = UPLINK_PDA + /// List of column names to be queried + var/static/list/column_names + +/// Block varedits to column_names +/datum/preferences_holder/preferences_character/vv_edit_var(var_name, var_value) + if(var_name == NAMEOF_STATIC(src, column_names)) + return FALSE + return ..() + +/// Initialize the data cache with default values +/datum/preferences_holder/preferences_character/New(datum/preferences/prefs, slot) + slot_number = slot + if(!length(column_names)) + column_names = get_column_names() + ..(prefs) + +/datum/preferences_holder/preferences_character/proc/load_from_database(datum/preferences/prefs) + if(!query_data(prefs)) // Query direct, otherwise create informed defaults + for (var/preference_type in GLOB.preference_entries) + var/datum/preference/preference = GLOB.preference_entries[preference_type] + if (preference.preference_type != pref_type) + continue + preference_data[preference.db_key] = preference.deserialize(preference.create_informed_default_value(prefs), prefs) + +/datum/preferences_holder/preferences_character/proc/query_data(datum/preferences/prefs) + if(!SSdbcore.IsConnected()) + // TODO tgui-prefs write informed default or random value + return FALSE + var/list/values + var/datum/DBQuery/Q = SSdbcore.NewQuery( + "SELECT [db_column_list(column_names)] FROM [format_table_name("characters")] WHERE ckey=:ckey AND slot=:slot", + list("ckey" = prefs.parent.ckey, "slot" = slot_number) + ) + if(!Q.warn_execute()) + qdel(Q) + return FALSE + if(Q.NextRow()) + values = Q.item + if(!length(values)) // There is no character + return FALSE + else + return FALSE + qdel(Q) + if(length(values) != length(column_names)) + CRASH("Error querying character data: the returned value length is greater than the number of columns requested.") + for(var/index in 1 to length(values)) + var/db_key = column_names[index] + var/datum/preference/preference = GLOB.preference_entries_by_key[db_key] + if(!istype(preference)) + CRASH("Could not find preference with db_key [db_key] when querying database.") + preference_data[db_key] = preference.deserialize(values[index], prefs) + return TRUE + +/datum/preferences_holder/preferences_character/proc/write_to_database(datum/preferences/prefs) + write_data(prefs) + dirty_prefs.Cut() // clear all dirty preferences + +/datum/preferences_holder/preferences_character/proc/write_data(datum/preferences/prefs) + if(!SSdbcore.IsConnected() || IS_GUEST_KEY(prefs.parent.ckey)) + return FALSE + var/list/column_names_short = list() + var/list/new_data = list() + for(var/db_key in dirty_prefs) + if(!(db_key in preference_data)) + CRASH("Invalid db_key found in dirty preferences list: [db_key].") + var/datum/preference/preference = GLOB.preference_entries_by_key[db_key] + if(!istype(preference)) + CRASH("Could not find preference with db_key [db_key] when writing to database.") + new_data[db_key] = preference.serialize(preference_data[db_key]) + var/column_name = clean_column_name(preference) + if(length(column_name)) + column_names_short += column_name + if(!length(column_names_short)) // nothing to update + return TRUE + new_data["ckey"] = prefs.parent.ckey + new_data["slot"] = slot_number + var/datum/DBQuery/Q = SSdbcore.NewQuery( + "INSERT INTO [format_table_name("characters")] (ckey, slot, [db_column_list(column_names_short)]) VALUES (:ckey, :slot, [db_column_list(column_names_short, TRUE)]) ON DUPLICATE KEY UPDATE [db_column_values(column_names_short)]", new_data + ) + var/success = Q.warn_execute() + if(!success) + to_chat(usr, "Failed to save your character. Please inform the server operator.") + qdel(Q) + return success + +/datum/preferences_holder/preferences_character/proc/get_column_names() + var/list/result = list() + for (var/preference_type in GLOB.preference_entries) + var/datum/preference/preference = GLOB.preference_entries[preference_type] + if (preference.preference_type != PREFERENCE_CHARACTER) + continue + // IMPORTANT: use of initial evades varedits. Filter to only alphanumeric and underscores + var/column_name = clean_column_name(preference) + if(length(column_name)) + result += column_name + if(!length(result)) + CRASH("Something is very wrong, /datum/prefence_character/proc/get_column_names() returned a zero length list.") + return result + +/datum/preferences_holder/preferences_character/proc/clean_column_name(datum/preference/preference) + var/column_name = reject_bad_text(initial(preference.db_key), max_length = 64, ascii_only = TRUE, alphanumeric_only = TRUE, underscore_allowed = TRUE) + if(!length(column_name) || findtext(column_name, " ") || column_name != preference.db_key) + CRASH("Invalid or possibly modified column name: '[column_name]' for db_key '[preference.db_key]'! Something bad is going on.") + return column_name + +/// Minimized copy of english_list because I don't want someone breaking this very important function later on +/datum/preferences_holder/preferences_character/proc/db_column_list(list/input, colon = FALSE) + var/total = length(input) + switch(total) + if (0) + return "" + if (1) + return "[colon ? ":" : ""][input[1]]" + if (2) + return "[colon ? ":" : ""][input[1]], [colon ? ":" : ""][input[2]]" + else + var/output = "" + var/index = 1 + while (index < total) + output += "[colon ? ":" : ""][input[index]], " + index++ + + return "[output][colon ? ":" : ""][input[index]]" + +/datum/preferences_holder/preferences_character/proc/db_column_values(list/input) + var/total = length(input) + switch(total) + if (0) + return "" + if (1) + return "[input[1]]=VALUES([input[1]])" + if (2) + return "[input[1]]=VALUES([input[1]]), [input[2]]=VALUES([input[2]])" + else + var/output = "" + var/index = 1 + while (index < total) + output += "[input[index]]=VALUES([input[index]])," + index++ + + return "[output][input[index]]=VALUES([input[index]])" diff --git a/code/modules/client/preferences_database.dm b/code/modules/client/preferences_database.dm index cf34829336593..c91f2772af5a8 100644 --- a/code/modules/client/preferences_database.dm +++ b/code/modules/client/preferences_database.dm @@ -1,6 +1,9 @@ /datum/preferences/proc/load_preferences() + player_data = new(src) + player_data.load_from_database(src) apply_all_client_preferences() + /* //general preferences lastchangelog = savefile.get_entry("lastchangelog", lastchangelog) be_special = savefile.get_entry("be_special", be_special) @@ -42,8 +45,8 @@ return TRUE /datum/preferences/proc/save_preferences() - - for (var/preference_type in GLOB.preference_entries) + player_data.write_to_database(src) + /*for (var/preference_type in GLOB.preference_entries) var/datum/preference/preference = GLOB.preference_entries[preference_type] if (preference.preference_type != PREFERENCE_PLAYER) continue @@ -53,8 +56,8 @@ recently_updated_keys -= preference.type - if (preference_type in value_cache) - write_preference(preference, preference.serialize(value_cache[preference_type])) + if (preference_type in player_preference_cache) + write_preference(preference, preference.serialize(player_preference_cache[preference.db_key]))*/ /* savefile.set_entry("lastchangelog", lastchangelog) savefile.set_entry("be_special", be_special) @@ -69,21 +72,14 @@ return TRUE /datum/preferences/proc/load_character(slot) - SHOULD_NOT_SLEEP(TRUE) if(!slot) slot = default_slot slot = sanitize_integer(slot, 1, max_save_slots, initial(default_slot)) if(slot != default_slot) default_slot = slot - // Read everything into cache - for (var/preference_type in GLOB.preference_entries) - var/datum/preference/preference = GLOB.preference_entries[preference_type] - if (preference.preference_type != PREFERENCE_CHARACTER) - continue - - value_cache -= preference_type - read_preference(preference_type) + character_data = new(src, slot) + character_data.load_from_database(src) /* //Character randomise = save_data?["randomise"] @@ -115,9 +111,9 @@ return TRUE /datum/preferences/proc/save_character() - SHOULD_NOT_SLEEP(TRUE) + character_data.write_to_database(src) - for (var/datum/preference/preference as anything in get_preferences_in_priority_order()) + /*for (var/datum/preference/preference as anything in get_preferences_in_priority_order()) if (preference.preference_type != PREFERENCE_CHARACTER) continue @@ -126,8 +122,8 @@ recently_updated_keys -= preference.type - if (preference.type in value_cache) - write_preference(preference, preference.serialize(value_cache[preference.type])) + if (preference.type in player_preference_cache) + write_preference(preference, preference.serialize(player_preference_cache[preference.type]))*/ /* //Character save_data["randomise"] = randomise @@ -139,3 +135,49 @@ save_data["all_quirks"] = all_quirks */ return TRUE + +/datum/preferences_holder + /// A map of db_key -> value. Data type varies. + var/list/preference_data + /// A list of preference db_keys that require writing + var/list/dirty_prefs + /// Preference type to parse + var/pref_type + +/datum/preferences_holder/New(datum/preferences/prefs) + preference_data = list() + dirty_prefs = list() + // Read everything into cache + for (var/preference_type in GLOB.preference_entries) + var/datum/preference/preference = GLOB.preference_entries[preference_type] + if (preference.preference_type != pref_type || preference.informed) + continue + + // we can't use informed values here. The name will get populated manually + preference_data[preference.db_key] = preference.deserialize(preference.create_default_value(), prefs) + +/datum/preferences_holder/proc/read_preference(datum/preferences/preferences, datum/preference/preference) + SHOULD_NOT_SLEEP(TRUE) + var/value = read_raw(preferences, preference) + if (isnull(value)) + value = preference.create_informed_default_value(preferences) + if (write_preference(preferences, preference, value)) + return value + else + CRASH("Couldn't write the default value for [preference.type] (received [value])") + return value + +/datum/preferences_holder/proc/read_raw(datum/preferences/preferences, datum/preference/preference) + var/value = preference_data[preference.db_key] + if (isnull(value)) + return null + else + return preference.deserialize(value, preferences) + +/datum/preferences_holder/proc/write_preference(datum/preferences/preferences, datum/preference/preference, value) + var/new_value = preference.deserialize(value, preferences) + if (!preference.is_valid(new_value)) + return FALSE + preference_data[preference.db_key] = new_value + dirty_prefs |= preference.db_key + return TRUE diff --git a/code/modules/client/preferences_player.dm b/code/modules/client/preferences_player.dm new file mode 100644 index 0000000000000..961f63a850f01 --- /dev/null +++ b/code/modules/client/preferences_player.dm @@ -0,0 +1,59 @@ +/// A cache for player preferences data +/datum/preferences_holder/preferences_player + pref_type = PREFERENCE_PLAYER + +/datum/preferences_holder/preferences_player/proc/load_from_database(datum/preferences/prefs) + if(!query_data(prefs)) // Query direct, otherwise create informed defaults + for (var/preference_type in GLOB.preference_entries) + var/datum/preference/preference = GLOB.preference_entries[preference_type] + if (preference.preference_type != pref_type) + continue + preference_data[preference.db_key] = preference.deserialize(preference.create_informed_default_value(prefs), prefs) + +/datum/preferences_holder/preferences_player/proc/query_data(datum/preferences/prefs) + if(!SSdbcore.IsConnected()) + return FALSE + var/datum/DBQuery/Q = SSdbcore.NewQuery( + "SELECT CAST(preference_tag AS CHAR) AS ptag, preference_value FROM [format_table_name("preferences")] WHERE ckey=:ckey", + list("ckey" = prefs.parent.ckey) + ) + if(!Q.warn_execute()) + qdel(Q) + return FALSE + while(Q.NextRow()) + var/db_key = Q.item[1] + var/value = Q.item[2] + var/datum/preference/preference = GLOB.preference_entries_by_key[db_key] + if(!preference) + //CRASH("Unknown preference tag in database: [db_key] for ckey [prefs.parent.ckey]") + continue + preference_data[db_key] = preference.deserialize(value, prefs) + qdel(Q) + return TRUE + +/datum/preferences_holder/preferences_player/proc/write_to_database(datum/preferences/prefs) + write_data(prefs) + dirty_prefs.Cut() // clear all dirty preferences + +/datum/preferences_holder/preferences_player/proc/write_data(datum/preferences/prefs) + if(!SSdbcore.IsConnected() || IS_GUEST_KEY(prefs.parent.ckey)) + return FALSE + var/list/sql_inserts = list() + for(var/db_key in dirty_prefs) + if(!(db_key in preference_data)) + CRASH("Invalid db_key found in dirty preferences list: [db_key].") + var/datum/preference/preference = GLOB.preference_entries_by_key[db_key] + if(!istype(preference)) + CRASH("Could not find preference with db_key [db_key] when writing to database.") + sql_inserts += list(list( + "ckey" = prefs.parent.ckey, + "preference_tag" = db_key, + "preference_value" = preference.serialize(preference_data[db_key]) + )) + if(!length(sql_inserts)) // nothing to update + return TRUE + + var/success = SSdbcore.MassInsert(format_table_name("preferences"), sql_inserts, duplicate_key = TRUE, warn = TRUE) + if(!success) + to_chat(usr, "Failed to save your player preferences. Please inform the server operator.") + return success diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm index f91997bcaa8b4..8aa8ca1f17524 100644 --- a/code/modules/client/preferences_toggles.dm +++ b/code/modules/client/preferences_toggles.dm @@ -1,3 +1,4 @@ +// TODO tgui-prefs /client/verb/setup_character() set name = "Game Preferences" set category = "Preferences" diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm index b78c9c8ba4ec0..6cdffa726f23c 100644 --- a/code/modules/client/verbs/ooc.dm +++ b/code/modules/client/verbs/ooc.dm @@ -76,7 +76,7 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") mob.log_talk(raw_msg, LOG_OOC) var/keyname = key - var/ooccolor = prefs.read_preference(/datum/preference/color/ooc_color) + var/ooccolor = prefs.read_player_preference(/datum/preference/color/ooc_color) if(prefs.unlock_content) if(prefs.toggles & PREFTOGGLE_MEMBER_PUBLIC) keyname = "[icon2html('icons/member_content.dmi', world, "blag")][keyname]" diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 10668c2c086f4..c82561edf2cd7 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -710,10 +710,10 @@ GLOBAL_LIST_INIT(hallucination_list, list( feedback_details += "Type: [is_radio ? "Radio" : "Talk"], Source: [person.real_name], Message: [message]" // Display message - if (!is_radio && !target.client?.prefs.read_preference(/datum/preference/toggle/enable_runechat)) + if (!is_radio && !target.client?.prefs.read_player_preference(/datum/preference/toggle/enable_runechat)) var/image/speech_overlay = image('icons/mob/talk.dmi', person, "default0", layer = ABOVE_MOB_LAYER) INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(flick_overlay), speech_overlay, list(target.client), 30) - if (target.client?.prefs.read_preference(/datum/preference/toggle/enable_runechat)) + if (target.client?.prefs.read_player_preference(/datum/preference/toggle/enable_runechat)) create_chat_message(person, understood_language, list(target), chosen, spans) to_chat(target, message) qdel(src) diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm index eb87e6b563760..8558e19bad44f 100644 --- a/code/modules/jobs/job_types/_job.dm +++ b/code/modules/jobs/job_types/_job.dm @@ -200,7 +200,7 @@ if(CONFIG_GET(flag/enforce_human_authority) && (title in GLOB.command_positions)) if(H.dna.species.id != SPECIES_HUMAN) H.set_species(/datum/species/human) - H.apply_pref_name("human", preference_source) + H.apply_pref_name(/datum/preference/name/backup_human, preference_source) if(!visualsOnly) var/datum/bank_account/bank_account = new(H.real_name, src) bank_account.payday(STARTING_PAYCHECKS, TRUE) @@ -396,7 +396,7 @@ // TODO tgui-prefs /mob/living/carbon/human/apply_prefs_job(client/player_client, datum/job/job) - var/fully_randomize = /*GLOB.current_anonymous_theme ||*/ player_client.prefs.should_be_random_hardcore(job, player_client.mob.mind) || is_banned_from(player_client.ckey, "Appearance") + var/fully_randomize = is_banned_from(player_client.ckey, "Appearance") if(!player_client) return // Disconnected while checking for the appearance ban. @@ -423,10 +423,10 @@ if (require_human) set_species(/datum/species/human) if(CONFIG_GET(flag/force_random_names)) - var/species_type = player_client.prefs.read_preference(/datum/preference/choiced/species) + var/species_type = player_client.prefs.read_character_preference(/datum/preference/choiced/species) var/datum/species/species = new species_type - var/gender = player_client.prefs.read_preference(/datum/preference/choiced/gender) + var/gender = player_client.prefs.read_character_preference(/datum/preference/choiced/gender) real_name = species.random_name(gender, TRUE) dna.update_dna_identity() @@ -445,17 +445,17 @@ //if(GLOB.current_anonymous_theme) // organic_name = GLOB.current_anonymous_theme.anonymous_name(src) /*else if below */ - if(player_client.prefs.read_preference(/datum/preference/choiced/random_name) == RANDOM_ENABLED || CONFIG_GET(flag/force_random_names) || is_banned_from(player_client.ckey, "Appearance")) + if(player_client.prefs.read_character_preference(/datum/preference/choiced/random_name) == RANDOM_ENABLED || CONFIG_GET(flag/force_random_names) || is_banned_from(player_client.ckey, "Appearance")) if(!player_client) return // Disconnected while checking the appearance ban. - var/species_type = player_client.prefs.read_preference(/datum/preference/choiced/species) + var/species_type = player_client.prefs.read_character_preference(/datum/preference/choiced/species) var/datum/species/species = new species_type - organic_name = species.random_name(player_client.prefs.read_preference(/datum/preference/choiced/gender), TRUE) + organic_name = species.random_name(player_client.prefs.read_character_preference(/datum/preference/choiced/gender), TRUE) else if(!player_client) return // Disconnected while checking the appearance ban. - organic_name = player_client.prefs.read_preference(/datum/preference/name/real_name) + organic_name = player_client.prefs.read_character_preference(/datum/preference/name/real_name) mmi.name = "[initial(mmi.name)]: [organic_name]" if(mmi.brain) @@ -464,5 +464,5 @@ mmi.brainmob.real_name = organic_name //the name of the brain inside the cyborg is the robotized human's name. mmi.brainmob.name = organic_name // If this checks fails, then the name will have been handled during initialization. - //if(!GLOB.current_anonymous_theme && player_client.prefs.read_preference(/datum/preference/name/cyborg) != DEFAULT_CYBORG_NAME) + //if(!GLOB.current_anonymous_theme && player_client.prefs.read_character_preference(/datum/preference/name/cyborg) != DEFAULT_CYBORG_NAME) // apply_pref_name(/datum/preference/name/cyborg, player_client) diff --git a/code/modules/jobs/job_types/ai.dm b/code/modules/jobs/job_types/ai.dm index e6ec6f37083aa..6975cbad372be 100644 --- a/code/modules/jobs/job_types/ai.dm +++ b/code/modules/jobs/job_types/ai.dm @@ -38,7 +38,7 @@ H.forceMove(lateJoinCore.loc) qdel(lateJoinCore) var/mob/living/silicon/ai/AI = H - AI.apply_pref_name("ai", M.client) //If this runtimes oh well jobcode is fucked. + AI.apply_pref_name(/datum/preference/name/ai, M.client) //If this runtimes oh well jobcode is fucked. AI.set_core_display_icon(null, M.client) //we may have been created after our borg diff --git a/code/modules/jobs/job_types/chaplain.dm b/code/modules/jobs/job_types/chaplain.dm index b777b9dc4363c..e89069a1e1f07 100644 --- a/code/modules/jobs/job_types/chaplain.dm +++ b/code/modules/jobs/job_types/chaplain.dm @@ -44,8 +44,8 @@ H.mind?.holy_role = HOLY_ROLE_HIGHPRIEST var/client/player_client = H.client - var/new_religion = player_client?.prefs?.read_preference(/datum/preference/name/religion) || DEFAULT_RELIGION - var/new_deity = player_client?.prefs?.read_preference(/datum/preference/name/deity) || DEFAULT_DEITY + var/new_religion = player_client?.prefs?.read_character_preference(/datum/preference/name/religion) || DEFAULT_RELIGION + var/new_deity = player_client?.prefs?.read_character_preference(/datum/preference/name/deity) || DEFAULT_DEITY B.deity_name = new_deity diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm index a33ba0db4af17..a8da5c3ecb36e 100644 --- a/code/modules/jobs/job_types/security_officer.dm +++ b/code/modules/jobs/job_types/security_officer.dm @@ -46,7 +46,7 @@ GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, S // Assign department security var/department if(M?.client?.prefs) - department = M.client.prefs.read_preference(/datum/preference/choiced/security_department) + department = M.client.prefs.read_character_preference(/datum/preference/choiced/security_department) if(!LAZYLEN(GLOB.available_depts) || department == "None") return else if(department in GLOB.available_depts) diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 678e7e8eefd39..65bfc5984b51d 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -235,7 +235,7 @@ observer.client = client observer.set_ghost_appearance() if(observer.client && observer.client.prefs) - observer.real_name = observer.client.prefs.read_preference(/datum/preference/name/real_name) + observer.real_name = observer.client.prefs.read_character_preference(/datum/preference/name/real_name) observer.name = observer.real_name observer.update_icon() observer.stop_sound_channel(CHANNEL_LOBBYMUSIC) @@ -513,7 +513,7 @@ /mob/dead/new_player/proc/check_preferences() if(!client) return FALSE //Not sure how this would get run without the mob having a client, but let's just be safe. - if(client.prefs.read_preference(/datum/preference/choiced/jobless_role) != RETURNTOLOBBY) + if(client.prefs.read_player_preference(/datum/preference/choiced/jobless_role) != RETURNTOLOBBY) return TRUE // If they have antags enabled, they're potentially doing this on purpose instead of by accident. Notify admins if so. var/has_antags = FALSE diff --git a/code/modules/mob/dead/new_player/preferences_setup.dm b/code/modules/mob/dead/new_player/preferences_setup.dm index 98e21e179b00d..ce525eb4effd5 100644 --- a/code/modules/mob/dead/new_player/preferences_setup.dm +++ b/code/modules/mob/dead/new_player/preferences_setup.dm @@ -9,7 +9,7 @@ /// Randomizes the character according to preferences. /datum/preferences/proc/apply_character_randomization_prefs(antag_override = FALSE) - switch (read_preference(/datum/preference/choiced/random_body)) + switch (read_character_preference(/datum/preference/choiced/random_body)) if (RANDOM_ANTAG_ONLY) if (!antag_override) return @@ -97,7 +97,7 @@ if(preview_job) // Silicons only need a very basic preview since there is no customization for them. if (istype(preview_job,/datum/job/ai)) - return image('icons/mob/ai.dmi', icon_state = resolve_ai_icon(read_preference(/datum/preference/choiced/ai_core_display)), dir = SOUTH) + return image('icons/mob/ai.dmi', icon_state = resolve_ai_icon(read_character_preference(/datum/preference/choiced/ai_core_display)), dir = SOUTH) if (istype(preview_job,/datum/job/cyborg)) return image('icons/mob/robots.dmi', icon_state = "robot", dir = SOUTH) diff --git a/code/modules/mob/dead/observer/login.dm b/code/modules/mob/dead/observer/login.dm index 36c697b7c503e..9bb48f0c46380 100644 --- a/code/modules/mob/dead/observer/login.dm +++ b/code/modules/mob/dead/observer/login.dm @@ -1,16 +1,16 @@ /mob/dead/observer/Login() ..() - ghost_accs = client.prefs.read_preference(/datum/preference/choiced/ghost_accessories) - ghost_others = client.prefs.read_preference(/datum/preference/choiced/ghost_others) + ghost_accs = client.prefs.read_player_preference(/datum/preference/choiced/ghost_accessories) + ghost_others = client.prefs.read_player_preference(/datum/preference/choiced/ghost_others) var/preferred_form = null if(IsAdminGhost(src)) has_unlimited_silicon_privilege = 1 if(client.prefs.unlock_content) - preferred_form = client.prefs.read_preference(/datum/preference/choiced/ghost_form) - ghost_orbit = client.prefs.read_preference(/datum/preference/choiced/ghost_orbit) + preferred_form = client.prefs.read_player_preference(/datum/preference/choiced/ghost_form) + ghost_orbit = client.prefs.read_player_preference(/datum/preference/choiced/ghost_orbit) var/turf/T = get_turf(src) if (isturf(T)) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 86a8d2551c14b..751eafcbe50ee 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -202,8 +202,8 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER) . = ..() if(client) //We update our preferences in case they changed right before update_appearance was called. - ghost_accs = client.prefs.read_preference(/datum/preference/choiced/ghost_accessories) - ghost_others = client.prefs.read_preference(/datum/preference/choiced/ghost_others) + ghost_accs = client.prefs.read_player_preference(/datum/preference/choiced/ghost_accessories) + ghost_others = client.prefs.read_player_preference(/datum/preference/choiced/ghost_others) if(hair_overlay) cut_overlay(hair_overlay) @@ -398,7 +398,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp if(source) var/atom/movable/screen/alert/A = throw_alert("[REF(source)]_notify_cloning", /atom/movable/screen/alert/notify_cloning) if(A) - var/ui_style = client?.prefs?.read_preference(/datum/preference/choiced/ui_style) + var/ui_style = client?.prefs?.read_player_preference(/datum/preference/choiced/ui_style) if(ui_style) A.icon = ui_style2icon(ui_style) A.desc = message @@ -585,7 +585,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp /mob/dead/observer/update_sight() if(client) - ghost_others = client.prefs.read_preference(/datum/preference/choiced/ghost_others) //A quick update just in case this setting was changed right before calling the proc + ghost_others = client.prefs.read_player_preference(/datum/preference/choiced/ghost_others) //A quick update just in case this setting was changed right before calling the proc if (!ghostvision) see_invisible = SEE_INVISIBLE_LIVING @@ -613,7 +613,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp client.images -= GLOB.ghost_images_default if(GHOST_OTHERS_SIMPLE) client.images -= GLOB.ghost_images_simple - lastsetting = client.prefs.read_preference(/datum/preference/choiced/ghost_others) + lastsetting = client.prefs.read_player_preference(/datum/preference/choiced/ghost_others) if(!ghostvision) return if(lastsetting != GHOST_OTHERS_THEIR_SETTING) @@ -793,7 +793,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp set_ghost_appearance() if(client?.prefs) - var/real_name = client.prefs.read_preference(/datum/preference/name/real_name) + var/real_name = client.prefs.read_character_preference(/datum/preference/name/real_name) deadchat_name = real_name if(mind) mind.ghostname = real_name @@ -809,16 +809,16 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp //if(client.prefs.active_character.be_random_body) // client.prefs.active_character.randomise(gender) - var/species_type = client.prefs.read_preference(/datum/preference/choiced/species) + var/species_type = client.prefs.read_character_preference(/datum/preference/choiced/species) var/datum/species/species = new species_type if(HAIR in species.species_traits) - hair_style = client.prefs.read_preference(/datum/preference/choiced/hairstyle) - hair_color = brighten_color(client.prefs.read_preference(/datum/preference/color_legacy/hair_color)) + hair_style = client.prefs.read_character_preference(/datum/preference/choiced/hairstyle) + hair_color = brighten_color(client.prefs.read_character_preference(/datum/preference/color_legacy/hair_color)) if(FACEHAIR in species.species_traits) - facial_hair_style = client.prefs.read_preference(/datum/preference/choiced/facial_hairstyle) - facial_hair_color = brighten_color(client.prefs.read_preference(/datum/preference/color_legacy/facial_hair_color)) + facial_hair_style = client.prefs.read_character_preference(/datum/preference/choiced/facial_hairstyle) + facial_hair_color = brighten_color(client.prefs.read_character_preference(/datum/preference/color_legacy/facial_hair_color)) qdel(species) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 0cc9c2c62d9b0..0419a87ee6c2c 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -214,10 +214,10 @@ /mob/living/silicon/ai/proc/set_core_display_icon(input, client/C) if(client && !C) C = client - if(!input && !C?.prefs?.read_preference(/datum/preference/choiced/ai_core_display)) + if(!input && !C?.prefs?.read_character_preference(/datum/preference/choiced/ai_core_display)) icon_state = initial(icon_state) else - var/preferred_icon = input ? input : C.prefs.read_preference(/datum/preference/choiced/ai_core_display) + var/preferred_icon = input ? input : C.prefs.read_character_preference(/datum/preference/choiced/ai_core_display) icon_state = resolve_ai_icon(preferred_icon) /mob/living/silicon/ai/verb/pick_icon() diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index de76d2778e274..5f3b7aaf51230 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -285,7 +285,7 @@ var/changed_name = "" if(custom_name) changed_name = custom_name - if(changed_name == "" && C && C.prefs.read_preference(/datum/preference/name/cyborg) != DEFAULT_CYBORG_NAME) + if(changed_name == "" && C && C.prefs.read_character_preference(/datum/preference/name/cyborg) != DEFAULT_CYBORG_NAME) if(apply_pref_name(/datum/preference/name/cyborg, C)) return //built in camera handled in proc if(!changed_name) diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 0df18d712c649..e033865cf958c 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -35,7 +35,7 @@ create_mob_hud() if(hud_used) hud_used.show_hud(hud_used.hud_version) - hud_used.update_ui_style(ui_style2icon(client.prefs?.read_preference(/datum/preference/choiced/ui_style))) + hud_used.update_ui_style(ui_style2icon(client.prefs?.read_player_preference(/datum/preference/choiced/ui_style))) next_move = 1 diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 1f34725819b92..9247d7a72de07 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -281,20 +281,20 @@ ///Returns the client runechat visible messages preference according to the message type. /atom/proc/runechat_prefs_check(mob/target, visible_message_flags = NONE) - if(!target.client?.prefs.read_preference(/datum/preference/toggle/enable_runechat)) + if(!target.client?.prefs.read_player_preference(/datum/preference/toggle/enable_runechat)) return FALSE - if (!target.client?.prefs.read_preference(/datum/preference/toggle/enable_runechat_non_mobs)) + if (!target.client?.prefs.read_player_preference(/datum/preference/toggle/enable_runechat_non_mobs)) return FALSE // TODO tgui-prefs - //if(LAZYFIND(visible_message_flags, CHATMESSAGE_EMOTE) && !target.client.prefs.read_preference(/datum/preference/toggle/see_rc_emotes)) + //if(LAZYFIND(visible_message_flags, CHATMESSAGE_EMOTE) && !target.client.prefs.read_player_preference(/datum/preference/toggle/see_rc_emotes)) // return FALSE return TRUE /mob/runechat_prefs_check(mob/target, visible_message_flags = NONE) - if(!target.client?.prefs.read_preference(/datum/preference/toggle/enable_runechat)) + if(!target.client?.prefs.read_player_preference(/datum/preference/toggle/enable_runechat)) return FALSE // TODO tgui-prefs - /*if(LAZYFIND(visible_message_flags, CHATMESSAGE_EMOTE) && !target.client.prefs.read_preference(/datum/preference/toggle/see_rc_emotes)) + /*if(LAZYFIND(visible_message_flags, CHATMESSAGE_EMOTE) && !target.client.prefs.read_player_preference(/datum/preference/toggle/see_rc_emotes)) return FALSE*/ return TRUE diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 36cf557e2f471..fa94d9ea32b48 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -429,7 +429,7 @@ if(source) var/atom/movable/screen/alert/notify_action/A = O.throw_alert("[REF(source)]_notify_action", /atom/movable/screen/alert/notify_action) if(A) - var/ui_style = O.client?.prefs?.read_preference(/datum/preference/choiced/ui_style) + var/ui_style = O.client?.prefs?.read_player_preference(/datum/preference/choiced/ui_style) if(ui_style) A.icon = ui_style2icon(ui_style) if (header) diff --git a/code/modules/modular_computers/computers/item/tablet.dm b/code/modules/modular_computers/computers/item/tablet.dm index ed2cc06134507..ab3cdefd25052 100644 --- a/code/modules/modular_computers/computers/item/tablet.dm +++ b/code/modules/modular_computers/computers/item/tablet.dm @@ -363,13 +363,13 @@ equipped = TRUE if(!user.client.prefs) return - var/pref_theme = user.client.prefs.read_preference(/datum/preference/choiced/pda_theme) + var/pref_theme = user.client.prefs.read_player_preference(/datum/preference/choiced/pda_theme) if(!theme_locked && !ignore_theme_pref) for(var/key in allowed_themes) // i am going to scream. DM lists stop sucking please if(allowed_themes[key] == pref_theme) device_theme = pref_theme break - classic_color = user.client.prefs.read_preference(/datum/preference/color/pda_color) + classic_color = user.client.prefs.read_player_preference(/datum/preference/color/pda_color) /obj/item/modular_computer/tablet/pda/update_icon() ..() diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm index a938f512e0f10..ef5d28f7a44d1 100644 --- a/code/modules/tgui/tgui.dm +++ b/code/modules/tgui/tgui.dm @@ -57,7 +57,7 @@ /datum/tgui/New(mob/user, datum/src_object, interface, title, ui_x, ui_y) if(!user.client) // No client to show the TGUI to, so stop here return - log_tgui(user, "new [interface] fancy [user?.client?.prefs.read_preference(/datum/preference/toggle/tgui_fancy)]") + log_tgui(user, "new [interface] fancy [user?.client?.prefs.read_player_preference(/datum/preference/toggle/tgui_fancy)]") src.user = user src.src_object = src_object src.window_key = "[REF(src_object)]-main" @@ -101,7 +101,7 @@ if(!window.is_ready()) window.initialize( strict_mode = TRUE, - fancy = user.client.prefs.read_preference(/datum/preference/toggle/tgui_fancy), + fancy = user.client.prefs.read_player_preference(/datum/preference/toggle/tgui_fancy), assets = list( get_asset_datum(/datum/asset/simple/tgui), )) @@ -243,8 +243,8 @@ "window" = list( "key" = window_key, "size" = window_size, - "fancy" = user.client.prefs.read_preference(/datum/preference/toggle/tgui_fancy), - "locked" = user.client.prefs.read_preference(/datum/preference/toggle/tgui_lock), + "fancy" = user.client.prefs.read_player_preference(/datum/preference/toggle/tgui_fancy), + "locked" = user.client.prefs.read_player_preference(/datum/preference/toggle/tgui_lock), ), "client" = list( "ckey" = user.client.ckey, diff --git a/code/modules/tgui_input/alert.dm b/code/modules/tgui_input/alert.dm index e1612f5b4f862..3ae2b596a6095 100644 --- a/code/modules/tgui_input/alert.dm +++ b/code/modules/tgui_input/alert.dm @@ -20,7 +20,7 @@ else return // Client does NOT have tgui_input on: Returns regular input - if(!(user.client?.prefs?.toggles2 & PREFTOGGLE_2_TGUI_INPUT)) + if(!user.client.prefs.read_player_preference(/datum/preference/toggle/tgui_input)) switch(length(buttons)) if(1) return alert(user, message, title, buttons[1]) @@ -60,7 +60,7 @@ else return // Client does NOT have tgui_input on: Returns regular input - if(!(user.client?.prefs?.toggles2 & PREFTOGGLE_2_TGUI_INPUT)) + if(!user.client.prefs.read_player_preference(/datum/preference/toggle/tgui_input)) if(length(buttons) == 2) return alert(user, message, title, buttons[1], buttons[2]) if(length(buttons) == 3) @@ -133,8 +133,8 @@ .["autofocus"] = autofocus .["buttons"] = buttons .["message"] = message - .["large_buttons"] = !user.client?.prefs || (user.client.prefs.toggles2 & PREFTOGGLE_2_BIG_BUTTONS) - .["swapped_buttons"] = !user.client?.prefs || (user.client.prefs.toggles2 & PREFTOGGLE_2_SWITCHED_BUTTONS) + .["large_buttons"] = !user.client?.prefs || user.client.prefs.read_player_preference(/datum/preference/toggle/tgui_input_large) + .["swapped_buttons"] = !user.client?.prefs || user.client.prefs.read_player_preference(/datum/preference/toggle/tgui_input_swapped) .["title"] = title /datum/tgui_modal/ui_data(mob/user) diff --git a/code/modules/tgui_input/list.dm b/code/modules/tgui_input/list.dm index 237c719aaea29..cd2f7f1686b84 100644 --- a/code/modules/tgui_input/list.dm +++ b/code/modules/tgui_input/list.dm @@ -22,7 +22,7 @@ else return // Client does NOT have tgui_input on: Returns regular input - if(!(user.client?.prefs?.toggles2 & PREFTOGGLE_2_TGUI_INPUT)) + if(!user.client.prefs.read_player_preference(/datum/preference/toggle/tgui_input)) return input(user, message, title) as null|anything in items var/datum/tgui_list_input/input = new(user, message, title, items, default, timeout) input.ui_interact(user) @@ -56,7 +56,7 @@ else return // Client does NOT have tgui_input on: Returns regular input - if(!(user.client?.prefs?.toggles2 & PREFTOGGLE_2_TGUI_INPUT)) + if(!user.client.prefs.read_player_preference(/datum/preference/toggle/tgui_input)) return input(user, message, title) as null|anything in items var/datum/tgui_list_input/async/input = new(user, message, title, items, default, callback, timeout) input.ui_interact(user) @@ -145,8 +145,8 @@ . = list() .["init_value"] = default || items[1] .["items"] = items - .["large_buttons"] = !user.client?.prefs || (user.client.prefs.toggles2 & PREFTOGGLE_2_BIG_BUTTONS) - .["swapped_buttons"] = !user.client?.prefs || (user.client.prefs.toggles2 & PREFTOGGLE_2_SWITCHED_BUTTONS) + .["large_buttons"] = !user.client?.prefs || user.client.prefs.read_player_preference(/datum/preference/toggle/tgui_input_large) + .["swapped_buttons"] = !user.client?.prefs || user.client.prefs.read_player_preference(/datum/preference/toggle/tgui_input_swapped) .["message"] = message .["title"] = title diff --git a/code/modules/tgui_input/number.dm b/code/modules/tgui_input/number.dm index 456a578db8979..c2f0fa3fa9f03 100644 --- a/code/modules/tgui_input/number.dm +++ b/code/modules/tgui_input/number.dm @@ -25,7 +25,7 @@ else return // Client does NOT have tgui_input on: Returns regular input - if(!(user.client?.prefs?.toggles2 & PREFTOGGLE_2_TGUI_INPUT)) + if(!user.client.prefs.read_player_preference(/datum/preference/toggle/tgui_input)) var/input_number = input(user, message, title, default) as null|num return clamp(round_value ? round(input_number) : input_number, min_value, max_value) var/datum/tgui_input_number/number_input = new(user, message, title, default, max_value, min_value, timeout, round_value) @@ -61,7 +61,7 @@ else return // Client does NOT have tgui_input on: Returns regular input - if(!(user.client?.prefs?.toggles2 & PREFTOGGLE_2_TGUI_INPUT)) + if(!user.client.prefs.read_player_preference(/datum/preference/toggle/tgui_input)) var/input_number = input(user, message, title, default) as null|num return clamp(round_value ? round(input_number) : input_number, min_value, max_value) var/datum/tgui_input_number/async/number_input = new(user, message, title, default, max_value, min_value, callback, timeout, round_value) @@ -149,8 +149,8 @@ .["max_value"] = max_value .["message"] = message .["min_value"] = min_value - .["large_buttons"] = !user.client?.prefs || (user.client.prefs.toggles2 & PREFTOGGLE_2_BIG_BUTTONS) - .["swapped_buttons"] = !user.client?.prefs || (user.client.prefs.toggles2 & PREFTOGGLE_2_SWITCHED_BUTTONS) + .["large_buttons"] = !user.client?.prefs || user.client.prefs.read_player_preference(/datum/preference/toggle/tgui_input_large) + .["swapped_buttons"] = !user.client?.prefs || user.client.prefs.read_player_preference(/datum/preference/toggle/tgui_input_swapped) .["title"] = title /datum/tgui_input_number/ui_data(mob/user) diff --git a/code/modules/tgui_input/text.dm b/code/modules/tgui_input/text.dm index 75ec132061b5b..e77a63b647841 100644 --- a/code/modules/tgui_input/text.dm +++ b/code/modules/tgui_input/text.dm @@ -25,7 +25,7 @@ else return // Client does NOT have tgui_input on: Returns regular input - if(!(user.client?.prefs?.toggles2 & PREFTOGGLE_2_TGUI_INPUT)) + if(!user.client.prefs.read_player_preference(/datum/preference/toggle/tgui_input)) if(encode) if(multiline) return stripped_multiline_input(user, message, title, default, max_length) @@ -67,7 +67,7 @@ else return // Client does NOT have tgui_input on: Returns regular input - if(!(user.client?.prefs?.toggles2 & PREFTOGGLE_2_TGUI_INPUT)) + if(!user.client.prefs.read_player_preference(/datum/preference/toggle/tgui_input)) if(encode) if(multiline) return stripped_multiline_input(user, message, title, default, max_length) @@ -153,8 +153,8 @@ .["message"] = message .["multiline"] = multiline .["placeholder"] = default // Default is a reserved keyword - .["large_buttons"] = !user.client?.prefs || (user.client.prefs.toggles2 & PREFTOGGLE_2_BIG_BUTTONS) - .["swapped_buttons"] = !user.client?.prefs || (user.client.prefs.toggles2 & PREFTOGGLE_2_SWITCHED_BUTTONS) + .["large_buttons"] = !user.client?.prefs || user.client.prefs.read_player_preference(/datum/preference/toggle/tgui_input_large) + .["swapped_buttons"] = !user.client?.prefs || user.client.prefs.read_player_preference(/datum/preference/toggle/tgui_input_swapped) .["title"] = title /datum/tgui_input_text/ui_data(mob/user) diff --git a/code/modules/tooltip/tooltip.dm b/code/modules/tooltip/tooltip.dm index f9bf43d508c39..d6279e830bc70 100644 --- a/code/modules/tooltip/tooltip.dm +++ b/code/modules/tooltip/tooltip.dm @@ -108,7 +108,7 @@ Notes: /proc/openToolTip(mob/user = null, atom/movable/tip_src = null, params = null,title = "",content = "",theme = "") if(istype(user)) if(user.client && user.client.tooltips) - var/ui_style = user.client?.prefs?.read_preference(/datum/preference/choiced/ui_style) + var/ui_style = user.client?.prefs?.read_player_preference(/datum/preference/choiced/ui_style) if(!theme && ui_style) theme = lowertext(ui_style) if(!theme) diff --git a/tgui/docs/tgui-for-custom-html-popups.md b/tgui/docs/tgui-for-custom-html-popups.md index 97eaf20446e5c..2c0a73411ed3c 100644 --- a/tgui/docs/tgui-for-custom-html-popups.md +++ b/tgui/docs/tgui-for-custom-html-popups.md @@ -35,11 +35,11 @@ window.close() ## Sending assets -TGUI in /tg/station codebase has `/datum/asset`, that packs scripts and stylesheets for delivery via CDN for efficiency. TGUI internally uses this asset system to render TGUI interfaces *proper* and TGUI chat. This is a snippet from internal TGUI code: +TGUI in /tg/station codebase has `/datum/asset`, that packs scripts and stylesheets for delivery via CDN for efficiency. TGUI internally uses this asset system to render TGUI interfaces _proper_ and TGUI chat. This is a snippet from internal TGUI code: ```dm window.initialize( - fancy = user.client.prefs.read_preference( + fancy = user.client.prefs.read_player_preference( /datum/preference/toggle/tgui_fancy ), assets = list( @@ -64,8 +64,8 @@ Finally, you can use the `Byond` API object to load JS and CSS files directly vi ```html ``` @@ -91,7 +91,7 @@ window.initialize( ) ``` -If you need to inline multiple JS or CSS files, you can concatenate them for now, and separate contents of each file with an `\n` symbol. *This can be a point of improvement (add support for file lists)*. +If you need to inline multiple JS or CSS files, you can concatenate them for now, and separate contents of each file with an `\n` symbol. _This can be a point of improvement (add support for file lists)_. ## Fancy mode @@ -134,7 +134,7 @@ You can think of it in these terms: Of course we're not working with functions here, but hopefully this analogy makes the concept easier to understand. -Finally, message can contain custom properties, and how you use them is *completely up to you*. They have an important limitation - all additional properties are string-typed, and require you to use a slightly more verbose API for sending them (more about it in the next section). +Finally, message can contain custom properties, and how you use them is _completely up to you_. They have an important limitation - all additional properties are string-typed, and require you to use a slightly more verbose API for sending them (more about it in the next section). ```js Byond.sendMessage({ @@ -209,8 +209,8 @@ You can send messages with custom fields in case if you want to bypass JSON seri ```js Byond.sendMessage({ - type: "something", - ref: "[0x12345678]", + type: 'something', + ref: '[0x12345678]', }); ``` diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx index 408cc297819e5..abf9c457351ae 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx @@ -429,7 +429,7 @@ export const MainPage = (props: { ]; const randomBodyEnabled - = (data.character_preferences.non_contextual.random_body + = (data.character_preferences.non_contextual.body_is_always_random !== RandomSetting.Disabled) || randomToggleEnabled; @@ -472,7 +472,7 @@ export const MainPage = (props: { } else { // We can't use random_name/is_accessible because the // server doesn't know whether the random toggle is on. - delete nonContextualPreferences["random_name"]; + delete nonContextualPreferences['name_is_always_random']; } return ( diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts index f677e706d2822..a0cae0d3d7404 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts @@ -101,7 +101,7 @@ export type PreferencesMenuData = { features: Record; game_preferences: Record; non_contextual: { - random_body: RandomSetting, + body_is_always_random: RandomSetting, [otherKey: string]: unknown; }; secondary_features: Record; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/pda.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/pda.tsx index 823459a2a212d..e11c87313babe 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/pda.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/pda.tsx @@ -3,13 +3,13 @@ import { Feature, FeatureColorInput, FeatureDropdownInput } from "../base"; export const pda_color: Feature = { name: "PDA color", category: "GAMEPLAY", - description: "The background color of your PDA.", + description: "The background color of your PDA when using the Thinktronic Classic theme.", component: FeatureColorInput, }; -export const pda_style: Feature = { - name: "PDA style", +export const pda_theme: Feature = { + name: "PDA theme", category: "GAMEPLAY", - description: "The style of your equipped PDA. Changes font.", + description: "The theme of your equipped PDA. Changes the NtOS look in Settings.", component: FeatureDropdownInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/item_outlines.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/item_outlines.tsx index 3877da11007b9..303f5236df645 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/item_outlines.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/item_outlines.tsx @@ -1,8 +1,15 @@ -import { CheckboxInput, FeatureToggle } from "../base"; +import { CheckboxInput, FeatureToggle, FeatureColorInput } from '../base'; export const itemoutline_pref: FeatureToggle = { - name: "Item outlines", - category: "GAMEPLAY", - description: "When enabled, hovering over items will outline them.", + name: 'Item outlines', + category: 'GAMEPLAY', + description: 'When enabled, hovering over items will outline them.', component: CheckboxInput, }; + +export const outline_color: Feature = { + name: 'Item outline color', + category: 'GAMEPLAY', + description: 'The color of that hovered items will outline with.', + component: FeatureColorInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/screentips.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/screentips.tsx deleted file mode 100644 index 5d8575dd280eb..0000000000000 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/screentips.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { multiline } from "common/string"; -import { FeatureColorInput, Feature, FeatureToggle, CheckboxInput } from "../base"; - -export const screentip_color: Feature = { - name: "Screentips color", - category: "UI", - description: multiline` - The color of screen tips, the text you see when hovering over something. - `, - component: FeatureColorInput, -}; - -export const screentip_pref: FeatureToggle = { - name: "Enable screentips", - category: "UI", - description: multiline` - Enables screen tips, the text you see when hovering over something. - `, - component: CheckboxInput, -}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/randomization.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/randomization.tsx index 3d47560edf9e5..88760b9758664 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/randomization.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/randomization.tsx @@ -5,8 +5,8 @@ import { RandomizationButton } from "../../RandomizationButton"; import { useRandomToggleState } from "../../useRandomToggleState"; import { CheckboxInput, Feature, FeatureToggle } from "./base"; -export const random_body: Feature = { - name: "Random body", +export const body_is_always_random: Feature = { + name: 'Random body', component: (props, context) => { const [randomToggle, setRandomToggle] = useRandomToggleState(context); @@ -19,35 +19,30 @@ export const random_body: Feature = { />
    - { - randomToggle - ? ( - <> - - - - - - - - - ) - : ( - - - - ) - } + {randomToggle ? ( + <> + + + + + + + + ) : ( + + + + )}
    ); }, @@ -58,7 +53,7 @@ export const random_hardcore: FeatureToggle = { component: CheckboxInput, }; -export const random_name: Feature = { +export const name_is_always_random: Feature = { name: "Random name", component: (props, context) => { return ( diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx index aafa1588fde8d..66c1a426438a1 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx @@ -15,6 +15,11 @@ export const hair_color: Feature = { component: FeatureColorInput, }; +export const gradient_color: Feature = { + name: 'Gradient color', + component: FeatureColorInput, +}; + export const feature_human_ears: FeatureChoiced = { name: "Ears", component: FeatureDropdownInput, From 7cb7343226db3e42381bb095c44e49d46152337d Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 7 Jun 2023 21:10:54 -0400 Subject: [PATCH 029/269] Fixes and stuff --- code/modules/client/preferences.dm | 18 +++--- code/modules/client/preferences/tgui.dm | 58 +++++++++++++++++++ code/modules/client/preferences_character.dm | 28 +++++++++ code/modules/keybindings/setup.dm | 2 +- code/modules/tgui_input/say_modal/modal.dm | 4 +- .../CharacterPreferenceWindow.tsx | 18 +++--- .../tgui/interfaces/PreferencesMenu/data.ts | 1 + .../features/game_preferences/tgui.tsx | 7 +++ 8 files changed, 114 insertions(+), 22 deletions(-) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 469d9ab6c7685..2c9f0d462806a 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -162,6 +162,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) data["character_preferences"] = compile_character_preferences(user) data["active_slot"] = default_slot + data["max_slot"] = max_save_slots for (var/datum/preference_middleware/preference_middleware as anything in middleware) data += preference_middleware.get_ui_data(user) @@ -237,7 +238,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) if (!update_preference(requested_preference, value)) return FALSE - if (istype(requested_preference, /datum/preference/name)) + if (istype(requested_preference, /datum/preference/name/real_name)) tainted_character_profiles = TRUE return TRUE @@ -421,16 +422,15 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/character_preview_view) /datum/preferences/proc/create_character_profiles() var/list/profiles = list() - for(var/index in 1 to TRUE_MAX_SAVE_SLOTS) - var/name = read_preference(/datum/preference/name/real_name, index) - - if (isnull(name)) - profiles += null + profiles += null + var/list/slot_to_name = character_data.get_all_character_names(src) + if(!islist(slot_to_name)) + return profiles + for(var/index in 1 to TRUE_MAX_SAVE_SLOTS) + if(index > length(slot_to_name)) continue - - profiles += name - + profiles[index] = slot_to_name[index] return profiles /datum/preferences/proc/set_job_preference_level(datum/job/job, level) diff --git a/code/modules/client/preferences/tgui.dm b/code/modules/client/preferences/tgui.dm index 54ae4439e9066..f5a673a271d77 100644 --- a/code/modules/client/preferences/tgui.dm +++ b/code/modules/client/preferences/tgui.dm @@ -18,3 +18,61 @@ for (var/datum/tgui/tgui as anything in client.mob?.tgui_open_uis) // Force it to reload either way tgui.update_static_data(client.mob) + +// Determines if input boxes are in tgui or old fashioned +/datum/preference/toggle/tgui_input + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + db_key = "tgui_input" + preference_type = PREFERENCE_PLAYER + +/// Large button preference. Error text is in tooltip. +/datum/preference/toggle/tgui_input_large + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + db_key = "tgui_input_large" + preference_type = PREFERENCE_PLAYER + +/datum/preference/toggle/tgui_input_large/apply_to_client(client/client, value) + for (var/datum/tgui/tgui as anything in client.mob?.tgui_open_uis) + // Force it to reload either way + tgui.send_full_update(client.mob) + +/// Swapped button state - sets buttons to SS13 traditional SUBMIT/CANCEL +/datum/preference/toggle/tgui_input_swapped + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + db_key = "tgui_input_swapped" + preference_type = PREFERENCE_PLAYER + +/datum/preference/toggle/tgui_input_swapped/apply_to_client(client/client, value) + for (var/datum/tgui/tgui as anything in client.mob?.tgui_open_uis) + // Force it to reload either way + tgui.send_full_update(client.mob) + +/// TGUI Say vs Classic Say +/datum/preference/toggle/tgui_say + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + db_key = "tgui_say" + preference_type = PREFERENCE_PLAYER + default_value = FALSE + +/datum/preference/toggle/tgui_say/apply_to_client(client/client) + //client.tgui_say?.load() + // TODO tgui-prefs + +/// Light mode for tgui say +/datum/preference/toggle/tgui_say_light_mode + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + db_key = "tgui_say_light_mode" + preference_type = PREFERENCE_PLAYER + default_value = FALSE + +/datum/preference/toggle/tgui_say_light_mode/apply_to_client(client/client) + //client.tgui_say?.load() + +/datum/preference/toggle/tgui_say_show_prefix + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + db_key = "tgui_say_show_prefix" + preference_type = PREFERENCE_PLAYER + default_value = FALSE + +/datum/preference/toggle/tgui_say_show_prefix/apply_to_client(client/client) + //client.tgui_say?.load() diff --git a/code/modules/client/preferences_character.dm b/code/modules/client/preferences_character.dm index 23c86430f30d4..3507c7275f9b8 100644 --- a/code/modules/client/preferences_character.dm +++ b/code/modules/client/preferences_character.dm @@ -143,3 +143,31 @@ index++ return "[output][input[index]]=VALUES([input[index]])" + + +/datum/preferences_holder/preferences_character/proc/get_all_character_names(datum/preferences/prefs) + if(!SSdbcore.IsConnected()) + return list() // No names if DB is not connected + var/datum/DBQuery/Q = SSdbcore.NewQuery( + "SELECT slot,real_name FROM [format_table_name("characters")] WHERE ckey=:ckey", + list("ckey" = prefs.parent.ckey) + ) + if(!Q.warn_execute()) + qdel(Q) + CRASH("An SQL error occurred while retrieving character profile data.") + var/list/data = list() + for(var/index in 1 to TRUE_MAX_SAVE_SLOTS) + data += null + while(Q.NextRow()) + var/list/values = Q.item + if(length(values) != 2) + CRASH("Error querying character profile data: the returned value length is greater than the number of columns requested.") + if(!isnum(values[1])) + CRASH("Error querying character profile data: slot number was not a number") + if(!istext(values[2])) + CRASH("Error querying character profile data: character name was not a string") + if(values[1] > TRUE_MAX_SAVE_SLOTS) + CRASH("Slot number in database is greater than the maximum allowed slots! Please purge this character entry or increase the slot number.") + data[values[1]] = values[2] // data[1] = "John Smith" + qdel(Q) + return data diff --git a/code/modules/keybindings/setup.dm b/code/modules/keybindings/setup.dm index 510217f56b428..c29ec3534375e 100644 --- a/code/modules/keybindings/setup.dm +++ b/code/modules/keybindings/setup.dm @@ -37,7 +37,7 @@ erase_all_macros() var/list/macro_sets = SSinput.macro_sets - var/use_tgui_say = !prefs || (prefs.toggles2 & PREFTOGGLE_2_TGUI_SAY) + var/use_tgui_say = !prefs || (prefs.read_player_preference(/datum/preference/toggle/tgui_say)) var/say = use_tgui_say ? tgui_say_create_open_command(SAY_CHANNEL) : "\".winset \\\"command=\\\".start_typing say\\\";command=.init_say;saywindow.is-visible=true;saywindow.input.focus=true\\\"\"" var/me = use_tgui_say ? tgui_say_create_open_command(ME_CHANNEL) : "\".winset \\\"command=\\\".start_typing me\\\";command=.init_me;mewindow.is-visible=true;mewindow.input.focus=true\\\"\"" var/ooc = use_tgui_say ? tgui_say_create_open_command(OOC_CHANNEL) : "ooc" diff --git a/code/modules/tgui_input/say_modal/modal.dm b/code/modules/tgui_input/say_modal/modal.dm index f3aa30e84b95b..7f7e6f5e1941d 100644 --- a/code/modules/tgui_input/say_modal/modal.dm +++ b/code/modules/tgui_input/say_modal/modal.dm @@ -89,8 +89,8 @@ client.center_window("tgui_say", 231, 30) winshow(client, "tgui_say", FALSE) window.send_message("props", list( - lightMode = (client?.prefs?.toggles2 & PREFTOGGLE_2_SAY_LIGHT_THEME), - showRadioPrefix = (client?.prefs?.toggles2 & PREFTOGGLE_2_SAY_SHOW_PREFIX), + lightMode = client?.prefs?.read_player_preference(/datum/preference/toggle/tgui_say_light_mode), + showRadioPrefix = client?.prefs?.read_player_preference(/datum/preference/toggle/tgui_say_show_prefix), maxLength = max_length, )) stop_thinking() diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx index 14f622362afe4..d0da21be1d331 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx @@ -20,6 +20,7 @@ enum Page { const CharacterProfiles = (props: { activeSlot: number, + maxSlot: number, onClick: (index: number) => void, profiles: (string | null)[], }) => { @@ -31,6 +32,7 @@ const CharacterProfiles = (props: { - + ); })} - + From 75017bdd27f642c39ccfff8ede5239e4601f3a70 Mon Sep 17 00:00:00 2001 From: Mothblocks <35135081+Mothblocks@users.noreply.github.com> Date: Sun, 10 Oct 2021 04:50:38 -0700 Subject: [PATCH 033/269] Add reset keybindings button (#61942) --- .../preferences/middleware/keybindings.dm | 8 ++ .../PreferencesMenu/KeybindingsPage.tsx | 109 ++++++++++-------- .../interfaces/PreferencesMenu/TabbedMenu.tsx | 2 +- 3 files changed, 70 insertions(+), 49 deletions(-) diff --git a/code/modules/client/preferences/middleware/keybindings.dm b/code/modules/client/preferences/middleware/keybindings.dm index ff64d0df52f94..669199a079e46 100644 --- a/code/modules/client/preferences/middleware/keybindings.dm +++ b/code/modules/client/preferences/middleware/keybindings.dm @@ -3,6 +3,7 @@ /// Middleware to handle keybindings /datum/preference_middleware/keybindings action_delegations = list( + "reset_all_keybinds" = .proc/reset_all_keybinds, "reset_keybinds_to_defaults" = .proc/reset_keybinds_to_defaults, "set_keybindings" = .proc/set_keybindings, ) @@ -22,6 +23,13 @@ get_asset_datum(/datum/asset/json/keybindings) ) +/datum/preference_middleware/keybindings/proc/reset_all_keybinds(list/params, mob/user) + preferences.key_bindings = deepCopyList(GLOB.default_hotkeys) + preferences.key_bindings_by_key = preferences.get_key_bindings_by_key(preferences.key_bindings) + preferences.update_static_data(user) + + return TRUE + /datum/preference_middleware/keybindings/proc/reset_keybinds_to_defaults(list/params, mob/user) var/keybind_name = params["keybind_name"] var/datum/keybinding/keybinding = GLOB.keybindings_by_name[keybind_name] diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx index c05e89cc25785..e80bfef0f6cdb 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx @@ -374,6 +374,7 @@ export class KeybindingsPage extends Component<{}, KeybindingsPageState> { } render() { + const { act } = useBackend(this.context); const keybindings = this.state.keybindings; if (!keybindings) { @@ -394,58 +395,70 @@ export class KeybindingsPage extends Component<{}, KeybindingsPageState> { onKeyUp={this.handleKeyUp} /> - { - return [category, ( - - {sortKeybindings(Object.entries(keybindings)).map( - ([keybindingId, keybinding]) => { - const keys + + + { + return [category, ( + + {sortKeybindings(Object.entries(keybindings)).map( + ([keybindingId, keybinding]) => { + const keys = this.state.selectedKeybindings![keybindingId] || []; - const name = ( - - - - ); - - return ( - - - {name} - - {range(0, 3).map(key => ( - - - - ))} - - - + const name = ( + + - - - ); - } - )} - - )]; - } - )} - /> + ); + + return ( + + + {name} + + {range(0, 3).map(key => ( + + + + ))} + + + + + + + ); + } + )} + + )]; + } + )} + /> + + + + act("reset_all_keybinds")} + /> + + + ); } diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx index 450bf0dc3e498..267f624c19f40 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx @@ -26,7 +26,7 @@ export class TabbedMenu extends Component { {this.props.categoryEntries.map(([category]) => { return ( - + ); @@ -147,8 +146,7 @@ const KeybindingName = (props: { ? ( + position="bottom"> @@ -178,8 +176,7 @@ const ResetToDefaultButton = (props: { act("reset_keybinds_to_defaults", { keybind_name: props.keybindingId, }); - }} - > + }}> Reset to Defaults ); @@ -190,16 +187,16 @@ export class KeybindingsPage extends Component<{}, KeybindingsPageState> { keybindingOnClicks: Record void)[]> = {}; lastKeybinds?: PreferencesMenuData["keybindings"]; - state: KeybindingsPageState = { - lastKeyboardEvent: undefined, - keybindings: undefined, - selectedKeybindings: undefined, - rebindingHotkey: undefined, - }; - constructor() { super(); + this.state = { + lastKeyboardEvent: undefined, + keybindings: undefined, + selectedKeybindings: undefined, + rebindingHotkey: undefined, + }; + this.handleKeyDown = this.handleKeyDown.bind(this); this.handleKeyUp = this.handleKeyUp.bind(this); } diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx index 6546bcd737c39..21a0c6af5af14 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx @@ -34,8 +34,7 @@ const QuirkList = (props: { tabIndex="1" onClick={() => { props.onClick(quirkKey, quirk); - }} - > + }}> + }}> @@ -97,7 +95,7 @@ const QuirkList = (props: { if (quirk.failTooltip) { return ( - + {child} ); @@ -117,8 +115,7 @@ const StatDisplay: Inferno.StatelessComponent<{}> = (props) => { color="black" fontSize="1.2em" px={3} - py={0.5} - > + py={0.5}> {props.children} ); diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/ServerPreferencesFetcher.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/ServerPreferencesFetcher.tsx index d21cff506ac59..fe9c519d061c3 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/ServerPreferencesFetcher.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/ServerPreferencesFetcher.tsx @@ -11,9 +11,13 @@ export class ServerPreferencesFetcher extends Component<{ }, { serverData?: ServerData; }> { - state = { - serverData: undefined, - }; + + constructor() { + super(); + this.state = { + serverData: undefined, + }; + } componentDidMount() { this.populateServerData(); From a47c0d03cc65ea68a1df1f30ccc33a6e4737cb3d Mon Sep 17 00:00:00 2001 From: itsmeow Date: Mon, 24 Apr 2023 03:02:57 -0500 Subject: [PATCH 061/269] Proc refs --- code/modules/client/preferences/README.md | 4 ++-- code/modules/client/preferences/middleware/antags.dm | 2 +- code/modules/client/preferences/middleware/jobs.dm | 2 +- code/modules/client/preferences/middleware/keybindings.dm | 6 +++--- code/modules/client/preferences/middleware/names.dm | 2 +- code/modules/client/preferences/middleware/quirks.dm | 4 ++-- code/modules/client/preferences/middleware/random.dm | 4 ++-- code/modules/mob/living/silicon/ai/ai.dm | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/code/modules/client/preferences/README.md b/code/modules/client/preferences/README.md index 41082faf1fa1b..0c3384cedf7e9 100644 --- a/code/modules/client/preferences/README.md +++ b/code/modules/client/preferences/README.md @@ -87,7 +87,7 @@ import { CheckboxInput, FeatureToggle } from "../base"; export const enable_breathing: FeatureToggle = { name: "Enable breathing", component: CheckboxInput, -} +}; ``` ## Choiced preferences @@ -360,7 +360,7 @@ Middleware can hijack actions by specifying `action_delegations`: ```dm /datum/preference_middleware/congratulations action_delegations = list( - "congratulate_me" = .proc/congratulate_me, + "congratulate_me" = PROC_REF(congratulate_me), ) /datum/preference_middleware/congratulations/proc/congratulate_me(list/params, mob/user) diff --git a/code/modules/client/preferences/middleware/antags.dm b/code/modules/client/preferences/middleware/antags.dm index 7cb11544f05bd..ce02dd6b1083b 100644 --- a/code/modules/client/preferences/middleware/antags.dm +++ b/code/modules/client/preferences/middleware/antags.dm @@ -1,6 +1,6 @@ /datum/preference_middleware/antags action_delegations = list( - "set_antags" = .proc/set_antags, + "set_antags" = PROC_REF(set_antags), ) /datum/preference_middleware/antags/get_ui_static_data(mob/user) diff --git a/code/modules/client/preferences/middleware/jobs.dm b/code/modules/client/preferences/middleware/jobs.dm index e125af2e0f7d0..2d5a26e12ead3 100644 --- a/code/modules/client/preferences/middleware/jobs.dm +++ b/code/modules/client/preferences/middleware/jobs.dm @@ -1,6 +1,6 @@ /datum/preference_middleware/jobs action_delegations = list( - "set_job_preference" = .proc/set_job_preference, + "set_job_preference" = PROC_REF(set_job_preference), ) /datum/preference_middleware/jobs/proc/set_job_preference(list/params, mob/user) diff --git a/code/modules/client/preferences/middleware/keybindings.dm b/code/modules/client/preferences/middleware/keybindings.dm index 6e2b35d99dfd7..e9a787587eec9 100644 --- a/code/modules/client/preferences/middleware/keybindings.dm +++ b/code/modules/client/preferences/middleware/keybindings.dm @@ -4,9 +4,9 @@ /// Middleware to handle keybindings /datum/preference_middleware/keybindings action_delegations = list( - "reset_all_keybinds" = .proc/reset_all_keybinds, - "reset_keybinds_to_defaults" = .proc/reset_keybinds_to_defaults, - "set_keybindings" = .proc/set_keybindings, + "reset_all_keybinds" = PROC_REF(reset_all_keybinds), + "reset_keybinds_to_defaults" = PROC_REF(reset_keybinds_to_defaults), + "set_keybindings" = PROC_REF(set_keybindings), ) /datum/preference_middleware/keybindings/get_ui_static_data(mob/user) diff --git a/code/modules/client/preferences/middleware/names.dm b/code/modules/client/preferences/middleware/names.dm index 9cf8c577c6f78..224bf219ffe9e 100644 --- a/code/modules/client/preferences/middleware/names.dm +++ b/code/modules/client/preferences/middleware/names.dm @@ -2,7 +2,7 @@ /// they have. /datum/preference_middleware/names action_delegations = list( - "randomize_name" = .proc/randomize_name, + "randomize_name" = PROC_REF(randomize_name), ) /datum/preference_middleware/names/get_constant_data() diff --git a/code/modules/client/preferences/middleware/quirks.dm b/code/modules/client/preferences/middleware/quirks.dm index 8e6d26e6a1140..16a4587c4bac2 100644 --- a/code/modules/client/preferences/middleware/quirks.dm +++ b/code/modules/client/preferences/middleware/quirks.dm @@ -4,8 +4,8 @@ var/tainted = FALSE action_delegations = list( - "give_quirk" = .proc/give_quirk, - "remove_quirk" = .proc/remove_quirk, + "give_quirk" = PROC_REF(give_quirk), + "remove_quirk" = PROC_REF(remove_quirk), ) /datum/preference_middleware/quirks/get_ui_static_data(mob/user) diff --git a/code/modules/client/preferences/middleware/random.dm b/code/modules/client/preferences/middleware/random.dm index 1b6ebe189e1e7..616a383b1e84e 100644 --- a/code/modules/client/preferences/middleware/random.dm +++ b/code/modules/client/preferences/middleware/random.dm @@ -1,8 +1,8 @@ /// Middleware for handling randomization preferences /datum/preference_middleware/random action_delegations = list( - "randomize_character" = .proc/randomize_character, - "set_random_preference" = .proc/set_random_preference, + "randomize_character" = PROC_REF(randomize_character), + "set_random_preference" = PROC_REF(set_random_preference), ) /datum/preference_middleware/random/get_character_preferences(mob/user) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index e571ca3ca173b..1b6b78ba06f72 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -138,7 +138,7 @@ create_modularInterface() create_eye() if(client) - INVOKE_ASYNC(src, .proc/apply_pref_name, /datum/preference/name/ai, client) + INVOKE_ASYNC(src, PROC_REF(apply_pref_name), /datum/preference/name/ai, client) INVOKE_ASYNC(src, PROC_REF(set_core_display_icon)) From 40701078b78072599fdfa7c915ad96ddba3e6f40 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Mon, 24 Apr 2023 04:05:51 -0500 Subject: [PATCH 062/269] I hate typescript --- tgui/packages/tgui/components/Box.tsx | 9 +++-- tgui/packages/tgui/components/FitText.tsx | 3 +- tgui/packages/tgui/hotkeys.ts | 34 +++++++++++++++++++ .../PreferencesMenu/KeybindingsPage.tsx | 26 ++++++++------ .../preferences/features/base.tsx | 5 +-- 5 files changed, 62 insertions(+), 15 deletions(-) diff --git a/tgui/packages/tgui/components/Box.tsx b/tgui/packages/tgui/components/Box.tsx index 9a3675da1716f..16fa903dcd300 100644 --- a/tgui/packages/tgui/components/Box.tsx +++ b/tgui/packages/tgui/components/Box.tsx @@ -238,8 +238,13 @@ export const computeBoxClassName = (props: BoxProps) => { return classes([isColorClass(color) && 'color-' + color, isColorClass(backgroundColor) && 'color-bg-' + backgroundColor]); }; -export const Box = (props: BoxProps) => { - const { as = 'div', className, children, ...rest } = props; +export const Box: Inferno.SFC = (props: BoxProps) => { + const { + as = 'div', + className, + children, + ...rest + } = props; // Render props if (typeof children === 'function') { return children(computeBoxProps(props)); diff --git a/tgui/packages/tgui/components/FitText.tsx b/tgui/packages/tgui/components/FitText.tsx index 445e59119c99c..dd646efb57a41 100644 --- a/tgui/packages/tgui/components/FitText.tsx +++ b/tgui/packages/tgui/components/FitText.tsx @@ -1,4 +1,5 @@ import { Component, createRef, RefObject } from "inferno"; +import type { Inferno } from "inferno"; const DEFAULT_ACCEPTABLE_DIFFERENCE = 5; @@ -6,7 +7,7 @@ export class FitText extends Component<{ acceptableDifference?: number, maxWidth: number, maxFontSize: number, - native?: HTMLAttributes, + native?: Inferno.HTMLAttributes, }, { fontSize: number, }> { diff --git a/tgui/packages/tgui/hotkeys.ts b/tgui/packages/tgui/hotkeys.ts index 53a6688869d0c..ba60065ce397a 100644 --- a/tgui/packages/tgui/hotkeys.ts +++ b/tgui/packages/tgui/hotkeys.ts @@ -31,6 +31,9 @@ const hotKeysAcquired = [ // State of passed-through keys. const keyState: Record = {}; +// Custom listeners for key events +const keyListeners: ((key: KeyEvent) => void)[] = []; + // Is hotkey mode on? let hotkeyMode; @@ -191,6 +194,37 @@ export const setupHotKeys = () => { releaseHeldKeys(); }); globalEvents.on('key', (key: KeyEvent) => { + for (const keyListener of keyListeners) { + keyListener(key); + } handlePassthrough(key); }); }; + +/** + * Registers for any key events, such as key down or key up. + * This should be preferred over directly connecting to keydown/keyup + * as it lets tgui prevent the key from reaching BYOND. + * + * If using in a component, prefer KeyListener, which automatically handles + * stopping listening when unmounting. + * + * @param callback The function to call whenever a key event occurs + * @returns A callback to stop listening + */ +export const listenForKeyEvents = ( + callback: (key: KeyEvent) => void, +): () => void => { + keyListeners.push(callback); + + let removed = false; + + return () => { + if (removed) { + return; + } + + removed = true; + keyListeners.splice(keyListeners.indexOf(callback), 1); + }; +}; \ No newline at end of file diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx index e052ddc902ac0..1e65222f6e135 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx @@ -186,17 +186,16 @@ export class KeybindingsPage extends Component<{}, KeybindingsPageState> { cancelNextKeyUp?: number; keybindingOnClicks: Record void)[]> = {}; lastKeybinds?: PreferencesMenuData["keybindings"]; + state: KeybindingsPageState = { + lastKeyboardEvent: undefined, + keybindings: undefined, + selectedKeybindings: undefined, + rebindingHotkey: undefined, + } constructor() { super(); - this.state = { - lastKeyboardEvent: undefined, - keybindings: undefined, - selectedKeybindings: undefined, - rebindingHotkey: undefined, - }; - this.handleKeyDown = this.handleKeyDown.bind(this); this.handleKeyUp = this.handleKeyUp.bind(this); } @@ -263,7 +262,7 @@ export class KeybindingsPage extends Component<{}, KeybindingsPageState> { handleKeyDown(keyEvent: KeyEvent) { const event = keyEvent.event; - const rebindingHotkey = this.state.rebindingHotkey; + const rebindingHotkey = this.state?.rebindingHotkey; if (!rebindingHotkey) { return; @@ -292,6 +291,10 @@ export class KeybindingsPage extends Component<{}, KeybindingsPageState> { keyEvent.event.preventDefault(); } + if(this.state == null) { + return; + } + const { lastKeyboardEvent, rebindingHotkey } = this.state; if (rebindingHotkey && lastKeyboardEvent) { @@ -309,7 +312,7 @@ export class KeybindingsPage extends Component<{}, KeybindingsPageState> { if (!this.keybindingOnClicks[keybindingId][slot]) { this.keybindingOnClicks[keybindingId][slot] = () => { - if (this.state.rebindingHotkey === undefined) { + if (this.state?.rebindingHotkey === undefined) { this.setState({ lastKeyboardEvent: undefined, rebindingHotkey: [keybindingId, slot], @@ -327,6 +330,9 @@ export class KeybindingsPage extends Component<{}, KeybindingsPageState> { } getTypingHotkey(keybindingId: string, slot: number): string | undefined { + if (!this.state) { + return; + } const { lastKeyboardEvent, rebindingHotkey } = this.state; if (!rebindingHotkey) { @@ -372,7 +378,7 @@ export class KeybindingsPage extends Component<{}, KeybindingsPageState> { render() { const { act } = useBackend(this.context); - const keybindings = this.state.keybindings; + const keybindings = this.state?.keybindings; if (!keybindings) { return Loading keybindings...; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx index a582ac539ccd4..2c6ee301e3b0e 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx @@ -1,6 +1,7 @@ import { sortBy, sortStrings } from "common/collections"; import { BooleanLike, classes } from "common/react"; -import { ComponentType, createComponentVNode, InfernoNode } from "inferno"; +import { createComponentVNode } from "inferno"; +import type { Inferno, ComponentType } from "inferno"; import { VNodeFlags } from "inferno-vnode-flags"; import { sendAct, useBackend, useLocalState } from "../../../../backend"; import { Box, Button, Dropdown, NumberInput, Stack } from "../../../../components"; @@ -228,7 +229,7 @@ export const FeatureIconnedDropdownInput = ( const displayNames = Object.fromEntries( Object.entries(textNames).map(([choice, textName]) => { - let element: InfernoNode = textName; + let element: Inferno.InfernoNode = textName; if (icons && icons[choice]) { const icon = icons[choice]; From 1bfe8721955f22f76bc47d5a167d2183466ea883 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Mon, 24 Apr 2023 04:12:46 -0500 Subject: [PATCH 063/269] Disable unused import warnings for now --- tgui/.eslintrc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tgui/.eslintrc.yml b/tgui/.eslintrc.yml index 9d5b3c912b77e..753571840cf21 100644 --- a/tgui/.eslintrc.yml +++ b/tgui/.eslintrc.yml @@ -766,4 +766,4 @@ rules: react/jsx-wrap-multilines: error ## Prevents the use of unused imports. ## This could be done by enabling no-unused-vars, but we're doing this for now - unused-imports/no-unused-imports: error + #unused-imports/no-unused-imports: error From fc304900caef92aff4d6da6cdca2d9334ad61684 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Mon, 24 Apr 2023 04:17:20 -0500 Subject: [PATCH 064/269] Add stupid trailing newline --- tgui/packages/tgui/hotkeys.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tgui/packages/tgui/hotkeys.ts b/tgui/packages/tgui/hotkeys.ts index ba60065ce397a..c45e5a4a661c9 100644 --- a/tgui/packages/tgui/hotkeys.ts +++ b/tgui/packages/tgui/hotkeys.ts @@ -227,4 +227,4 @@ export const listenForKeyEvents = ( removed = true; keyListeners.splice(keyListeners.indexOf(callback), 1); }; -}; \ No newline at end of file +}; From fe9ee89adef75fa1b00a3d2b6ad79bc57cb29b2d Mon Sep 17 00:00:00 2001 From: itsmeow Date: Mon, 24 Apr 2023 04:23:12 -0500 Subject: [PATCH 065/269] Parsing fixes --- tgui/packages/tgui/components/FitText.tsx | 2 +- .../tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx | 4 ++-- tgui/packages/tgui/interfaces/ScannerGate.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tgui/packages/tgui/components/FitText.tsx b/tgui/packages/tgui/components/FitText.tsx index dd646efb57a41..237492e2254ce 100644 --- a/tgui/packages/tgui/components/FitText.tsx +++ b/tgui/packages/tgui/components/FitText.tsx @@ -14,7 +14,7 @@ export class FitText extends Component<{ ref: RefObject = createRef(); state = { fontSize: 0, - } + }; constructor() { super(); diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx index 1e65222f6e135..f3a4000e13ab1 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx @@ -191,7 +191,7 @@ export class KeybindingsPage extends Component<{}, KeybindingsPageState> { keybindings: undefined, selectedKeybindings: undefined, rebindingHotkey: undefined, - } + }; constructor() { super(); @@ -291,7 +291,7 @@ export class KeybindingsPage extends Component<{}, KeybindingsPageState> { keyEvent.event.preventDefault(); } - if(this.state == null) { + if (this.state === null) { return; } diff --git a/tgui/packages/tgui/interfaces/ScannerGate.js b/tgui/packages/tgui/interfaces/ScannerGate.js index f9e15d49c2e2d..a175638a59b08 100644 --- a/tgui/packages/tgui/interfaces/ScannerGate.js +++ b/tgui/packages/tgui/interfaces/ScannerGate.js @@ -1,5 +1,5 @@ import { useBackend } from '../backend'; -import { Box, Button, LabeledList, Section } from '../components'; +import { Box, Button, LabeledList, Section, NumberInput } from '../components'; import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox'; import { Window } from '../layouts'; From c6ad81b791f5a88c14f59b4c550ae38d15b32b28 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 26 Apr 2023 01:27:20 -0500 Subject: [PATCH 066/269] Keybindings stuff --- code/__HELPERS/global_lists.dm | 5 +---- code/_globalvars/lists/client.dm | 2 +- code/datums/keybinding/carbon.dm | 6 ------ code/datums/keybinding/living.dm | 10 +++++----- code/modules/client/preferences.dm | 19 ++++++++++++++++++- code/modules/keybindings/bindings_client.dm | 2 +- 6 files changed, 26 insertions(+), 18 deletions(-) diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index e04e1fb7da04e..60e7ebca1e9dc 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -68,10 +68,7 @@ continue var/datum/keybinding/instance = new keybinding GLOB.keybindings_by_name[instance.name] = instance - if(!LAZYLEN(instance.keys)) - continue - for(var/bound_key in instance.keys) - LAZYADD(GLOB.keybinding_list_by_key[bound_key], list(instance.name)) + LAZYADD(GLOB.keybindings_by_name_to_key[instance.name], LAZYCOPY(instance.keys)) init_subtypes(/datum/crafting_recipe, GLOB.crafting_recipes) diff --git a/code/_globalvars/lists/client.dm b/code/_globalvars/lists/client.dm index b37c2a811017f..3862f3656c836 100644 --- a/code/_globalvars/lists/client.dm +++ b/code/_globalvars/lists/client.dm @@ -1,2 +1,2 @@ -GLOBAL_LIST_EMPTY(keybinding_list_by_key) GLOBAL_LIST_EMPTY(keybindings_by_name) +GLOBAL_LIST_EMPTY(keybindings_by_name_to_key) diff --git a/code/datums/keybinding/carbon.dm b/code/datums/keybinding/carbon.dm index 853ed8acb77a8..30f38be81559e 100644 --- a/code/datums/keybinding/carbon.dm +++ b/code/datums/keybinding/carbon.dm @@ -34,8 +34,6 @@ . = ..() if(.) return - if(iscyborg(user.mob)) - return FALSE user.mob?.a_intent_change(INTENT_HELP) return TRUE @@ -52,7 +50,6 @@ . = ..() if(.) return - if (!iscarbon(user.mob)) return var/mob/living/carbon/C = user.mob C.a_intent_change(INTENT_DISARM) return TRUE @@ -70,7 +67,6 @@ . = ..() if(.) return - if (!iscarbon(user.mob)) return var/mob/living/carbon/C = user.mob C.a_intent_change(INTENT_GRAB) return TRUE @@ -88,8 +84,6 @@ . = ..() if(.) return - if(iscyborg(user.mob)) - return FALSE user.mob?.a_intent_change(INTENT_HARM) return TRUE diff --git a/code/datums/keybinding/living.dm b/code/datums/keybinding/living.dm index a295f483fc881..2b33a2466a5b2 100644 --- a/code/datums/keybinding/living.dm +++ b/code/datums/keybinding/living.dm @@ -15,7 +15,7 @@ /datum/keybinding/living/resist/down(client/user) . = ..() - if(. || !isliving(user.mob)) + if(.) return var/mob/living/L = user.mob L.resist() @@ -31,7 +31,7 @@ /datum/keybinding/living/rest/down(client/user) . = ..() - if(. || !isliving(user.mob)) + if(.) return var/mob/living/L = user.mob L.lay_down() @@ -46,7 +46,7 @@ /datum/keybinding/living/look_up/down(client/user) . = ..() - if(. || !isliving(user.mob)) + if(.) return var/mob/living/L = user.mob L.look_up(lock = TRUE) @@ -54,7 +54,7 @@ /datum/keybinding/living/look_up/up(client/user) . = ..() - if(. || !isliving(user.mob)) + if(.) return var/mob/living/L = user.mob L.look_reset() @@ -77,7 +77,7 @@ /datum/keybinding/living/look_down/up(client/user) . = ..() - if(. || !isliving(user.mob)) + if(.) return var/mob/living/L = user.mob L.look_reset() diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index de115bbbc968a..75b7314ba3104 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -31,6 +31,10 @@ GLOBAL_LIST_EMPTY(preferences_datums) /// For example, by default would have "swap_hands" -> "X" var/list/key_bindings = list() + /// Cached list of keybindings, mapping keys to actions. + /// For example, by default would have "X" -> list("swap_hands") + var/list/key_bindings_by_key = list() + var/toggles = TOGGLES_DEFAULT var/toggles2 = TOGGLES_2_DEFAULT var/db_flags @@ -494,6 +498,16 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/character_preview_view) character.update_hair() character.update_body_parts() +/// Inverts the key_bindings list such that it can be used for key_bindings_by_key +/datum/preferences/proc/get_key_bindings_by_key(list/key_bindings) + var/list/output = list() + + for (var/action in key_bindings) + for (var/key in key_bindings[action]) + LAZYADD(output[key], action) + + return output + /// Returns the default `randomise` variable ouptut /datum/preferences/proc/get_default_randomization() var/list/default_randomization = list() @@ -509,9 +523,11 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/character_preview_view) if(!istype(_key_bindings)) return key_bindings = _key_bindings + key_bindings_by_key = get_key_bindings_by_key(key_bindings) /datum/preferences/proc/set_default_key_bindings() - key_bindings = deep_copy_list(GLOB.keybinding_list_by_key) + key_bindings = deep_copy_list(GLOB.keybindings_by_name_to_key) + key_bindings_by_key = get_key_bindings_by_key(key_bindings) /datum/preferences/proc/set_keybind(keybind_name, hotkeys) if (!(keybind_name in GLOB.keybindings_by_name)) @@ -519,3 +535,4 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/character_preview_view) if(!islist(hotkeys)) return key_bindings[keybind_name] = hotkeys + key_bindings_by_key = get_key_bindings_by_key(key_bindings) diff --git a/code/modules/keybindings/bindings_client.dm b/code/modules/keybindings/bindings_client.dm index 48a909475090c..7ade5566168b4 100644 --- a/code/modules/keybindings/bindings_client.dm +++ b/code/modules/keybindings/bindings_client.dm @@ -63,7 +63,7 @@ GLOBAL_LIST_INIT(valid_keys, list( full_key = "[AltMod][CtrlMod][ShiftMod][_key]" var/list/kbs = list() - for (var/kb_name in prefs.key_bindings[full_key]) + for (var/kb_name in prefs.key_bindings_by_key[full_key]) var/datum/keybinding/kb = GLOB.keybindings_by_name[kb_name] kbs += kb // WASD-type movement keys (not the native arrow keys) are handled through the keybind system here. From de4346b8a4034aed2e3e56b1cf3245f2c8be5af9 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 26 Apr 2023 01:44:21 -0500 Subject: [PATCH 067/269] Fix keypresses and outlines --- code/datums/keybinding/mob.dm | 8 ++++---- code/modules/client/preferences/item_outlines.dm | 3 +++ code/modules/keybindings/bindings_client.dm | 10 ++++------ 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/code/datums/keybinding/mob.dm b/code/datums/keybinding/mob.dm index f3c2e1f935eb4..518d31d78d7b1 100644 --- a/code/datums/keybinding/mob.dm +++ b/code/datums/keybinding/mob.dm @@ -4,7 +4,7 @@ /datum/keybinding/mob/move_north - keys = list("W", "North") + keys = list("W") name = "move_north" full_name = "Move North" description = "" @@ -29,7 +29,7 @@ /datum/keybinding/mob/move_east - keys = list("D", "East") + keys = list("D") name = "move_east" full_name = "Move East" description = "" @@ -54,7 +54,7 @@ /datum/keybinding/mob/move_south - keys = list("S", "South") + keys = list("S") name = "move_south" full_name = "Move South" description = "" @@ -79,7 +79,7 @@ /datum/keybinding/mob/move_west - keys = list("A", "West") + keys = list("A") name = "move_west" full_name = "Move West" description = "" diff --git a/code/modules/client/preferences/item_outlines.dm b/code/modules/client/preferences/item_outlines.dm index 1a7a68019ecc5..a35900fbc2ace 100644 --- a/code/modules/client/preferences/item_outlines.dm +++ b/code/modules/client/preferences/item_outlines.dm @@ -7,3 +7,6 @@ category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "outline_color" preference_type = PREFERENCE_PLAYER + +/datum/preference/color/outline_color/create_default_value() + return COLOR_BLUE_GRAY diff --git a/code/modules/keybindings/bindings_client.dm b/code/modules/keybindings/bindings_client.dm index 7ade5566168b4..6bcf76703e21c 100644 --- a/code/modules/keybindings/bindings_client.dm +++ b/code/modules/keybindings/bindings_client.dm @@ -71,7 +71,7 @@ GLOBAL_LIST_INIT(valid_keys, list( // since these modifier keys toggle effects like "change facing" that require the movement keys to function. // Note that this doesn't prevent the user from binding CTRL-W to North: In that case *only* CTRL-W will function. if (full_key != _key) - for (var/kb_name in prefs.key_bindings[_key]) + for (var/kb_name in prefs.key_bindings_by_key[_key]) var/datum/keybinding/kb = GLOB.keybindings_by_name[kb_name] if (kb.any_modifier) kbs += kb @@ -98,7 +98,7 @@ GLOBAL_LIST_INIT(valid_keys, list( // We don't do full key for release, because for mod keys you // can hold different keys and releasing any should be handled by the key binding specifically var/list/kbs = list() - for (var/kb_name in prefs.key_bindings[_key]) + for (var/kb_name in prefs.key_bindings_by_key[_key]) var/datum/keybinding/kb = GLOB.keybindings_by_name[kb_name] kbs += kb kbs = sort_list(kbs, GLOBAL_PROC_REF(cmp_keybinding_dsc)) @@ -106,7 +106,5 @@ GLOBAL_LIST_INIT(valid_keys, list( if(kb.can_use(src) && kb.up(src)) break - if(holder) - holder.key_up(_key, src) - if(mob.focus) - mob.focus.key_up(_key, src) + holder?.key_up(_key, src) + mob.focus?.key_up(_key, src) From 58e7dbca4cd3305360146a7e2dbde41b8c46f271 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 26 Apr 2023 02:06:31 -0500 Subject: [PATCH 068/269] Fix species changing --- code/modules/client/preferences_character.dm | 3 ++- code/modules/client/preferences_database.dm | 3 ++- code/modules/client/preferences_player.dm | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/code/modules/client/preferences_character.dm b/code/modules/client/preferences_character.dm index 3507c7275f9b8..3130c20b03cd3 100644 --- a/code/modules/client/preferences_character.dm +++ b/code/modules/client/preferences_character.dm @@ -52,7 +52,8 @@ var/datum/preference/preference = GLOB.preference_entries_by_key[db_key] if(!istype(preference)) CRASH("Could not find preference with db_key [db_key] when querying database.") - preference_data[db_key] = preference.deserialize(values[index], prefs) + var/value = values[index] + preference_data[db_key] = isnull(value) ? null : preference.deserialize(value, prefs) return TRUE /datum/preferences_holder/preferences_character/proc/write_to_database(datum/preferences/prefs) diff --git a/code/modules/client/preferences_database.dm b/code/modules/client/preferences_database.dm index 48fb82a0bb0c7..50e9d84af9f15 100644 --- a/code/modules/client/preferences_database.dm +++ b/code/modules/client/preferences_database.dm @@ -168,11 +168,12 @@ return value /datum/preferences_holder/proc/read_raw(datum/preferences/preferences, datum/preference/preference) + // Data is already deserialized by the time it's in the cache. Don't deserialize it again. var/value = preference_data[preference.db_key] if (isnull(value)) return null else - return preference.deserialize(value, preferences) + return value /datum/preferences_holder/proc/write_preference(datum/preferences/preferences, datum/preference/preference, value) var/new_value = preference.deserialize(value, preferences) diff --git a/code/modules/client/preferences_player.dm b/code/modules/client/preferences_player.dm index 961f63a850f01..b9c798b1490f8 100644 --- a/code/modules/client/preferences_player.dm +++ b/code/modules/client/preferences_player.dm @@ -25,9 +25,10 @@ var/value = Q.item[2] var/datum/preference/preference = GLOB.preference_entries_by_key[db_key] if(!preference) + // TODO tgui-prefs clean out database and re-enable this //CRASH("Unknown preference tag in database: [db_key] for ckey [prefs.parent.ckey]") continue - preference_data[db_key] = preference.deserialize(value, prefs) + preference_data[db_key] = isnull(value) ? null : preference.deserialize(value, prefs) qdel(Q) return TRUE From 5c42ce7e62028d7ba57c5c8f94bd8a4acff45c96 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Thu, 27 Apr 2023 04:22:24 -0500 Subject: [PATCH 069/269] Make preferences not break the entire TGUI framework, add string pref basis, fix guests saving, fix moths --- code/modules/client/preferences/_preference.dm | 16 ++++++++++++++++ .../client/preferences/species_features/moth.dm | 16 +++++++++------- code/modules/client/preferences_database.dm | 2 ++ tgui/packages/tgui/components/Box.tsx | 10 ++++++++-- tgui/packages/tgui/components/Button.js | 3 ++- tgui/packages/tgui/components/Dropdown.js | 4 ++-- tgui/packages/tgui/components/Stack.tsx | 2 +- .../preferences/features/base.tsx | 12 +++++++++++- 8 files changed, 51 insertions(+), 14 deletions(-) diff --git a/code/modules/client/preferences/_preference.dm b/code/modules/client/preferences/_preference.dm index e60987753dbd3..53a34b4ee1875 100644 --- a/code/modules/client/preferences/_preference.dm +++ b/code/modules/client/preferences/_preference.dm @@ -511,3 +511,19 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) /datum/preference/toggle/is_valid(value) return value == TRUE || value == FALSE + +/// A simple string type preference. +/datum/preference/string + abstract_type = /datum/preference/string + + /// The default value of the string, if create_default_value is not specified + var/default_value = "" + +/datum/preference/string/create_default_value() + return default_value + +/datum/preference/string/deserialize(input, datum/preferences/preferences) + return sanitize_text(input, create_default_value()) + +/datum/preference/string/is_valid(value) + return istext(value) diff --git a/code/modules/client/preferences/species_features/moth.dm b/code/modules/client/preferences/species_features/moth.dm index 365d0c158e903..843288c63444a 100644 --- a/code/modules/client/preferences/species_features/moth.dm +++ b/code/modules/client/preferences/species_features/moth.dm @@ -1,9 +1,10 @@ -/*/datum/preference/choiced/moth_antennae +/datum/preference/choiced/moth_antennae db_key = "feature_moth_antennae" preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Antennae" should_generate_icons = TRUE + relevant_mutant_bodypart = "moth_antennae" /datum/preference/choiced/moth_antennae/init_possible_values() var/list/values = list() @@ -11,8 +12,8 @@ var/icon/moth_head = icon('icons/mob/human_parts.dmi', "moth_head_m") moth_head.Blend(icon('icons/mob/human_face.dmi', "motheyes"), ICON_OVERLAY) - for (var/antennae_name in GLOB.moth_antennae_list) - var/datum/sprite_accessory/antennae = GLOB.moth_antennae_list[antennae_name] + for (var/antennae_name in GLOB.moth_antennae_roundstart_list) + var/datum/sprite_accessory/antennae = GLOB.moth_antennae_roundstart_list[antennae_name] var/icon/icon_with_antennae = new(moth_head) icon_with_antennae.Blend(icon(antennae.icon, "m_moth_antennae_[antennae.icon_state]_FRONT"), ICON_OVERLAY) @@ -54,8 +55,8 @@ moth_body.Blend(icon('icons/mob/human_face.dmi', "motheyes"), ICON_OVERLAY) - for (var/markings_name in GLOB.moth_markings_list) - var/datum/sprite_accessory/markings = GLOB.moth_markings_list[markings_name] + for (var/markings_name in GLOB.moth_markings_roundstart_list) + var/datum/sprite_accessory/markings = GLOB.moth_markings_roundstart_list[markings_name] var/icon/icon_with_markings = new(moth_body) if (markings_name != "None") @@ -84,10 +85,11 @@ category = PREFERENCE_CATEGORY_FEATURES main_feature_name = "Moth wings" should_generate_icons = TRUE + relevant_mutant_bodypart = "moth_wings" /datum/preference/choiced/moth_wings/init_possible_values() var/list/icon/values = possible_values_for_sprite_accessory_list_for_body_part( - GLOB.moth_wings_list, + GLOB.moth_wings_roundstart_list, "moth_wings", list("BEHIND", "FRONT"), ) @@ -100,4 +102,4 @@ /datum/preference/choiced/moth_wings/apply_to_human(mob/living/carbon/human/target, value) target.dna.features["moth_wings"] = value -*/ + diff --git a/code/modules/client/preferences_database.dm b/code/modules/client/preferences_database.dm index 50e9d84af9f15..a8bac19835bcc 100644 --- a/code/modules/client/preferences_database.dm +++ b/code/modules/client/preferences_database.dm @@ -45,6 +45,8 @@ return TRUE /datum/preferences/proc/save_preferences() + if(IS_GUEST_KEY(parent.ckey)) // NO saving guests to the DB! + return player_data.write_to_database(src) /*for (var/preference_type in GLOB.preference_entries) var/datum/preference/preference = GLOB.preference_entries[preference_type] diff --git a/tgui/packages/tgui/components/Box.tsx b/tgui/packages/tgui/components/Box.tsx index 16fa903dcd300..9a661ec2d1b70 100644 --- a/tgui/packages/tgui/components/Box.tsx +++ b/tgui/packages/tgui/components/Box.tsx @@ -10,7 +10,7 @@ import { ChildFlags, VNodeFlags } from 'inferno-vnode-flags'; import { CSS_COLORS } from '../constants'; import type { Inferno, InfernoNode } from 'inferno'; -export interface BoxProps { +export type BoxProps = { [key: string]: any; as?: string; className?: string | BooleanLike; @@ -253,7 +253,13 @@ export const Box: Inferno.SFC = (props: BoxProps) => { typeof className === 'string' ? className + ' ' + computeBoxClassName(rest) : computeBoxClassName(rest); const computedProps = computeBoxProps(rest); // Render a wrapper element - return createVNode(VNodeFlags.HtmlElement, as, computedClassName, children, ChildFlags.UnknownChildren, computedProps); + return createVNode( + VNodeFlags.HtmlElement, + as, + computedClassName, + children, + ChildFlags.UnknownChildren, + computedProps); }; Box.defaultHooks = pureComponentHooks; diff --git a/tgui/packages/tgui/components/Button.js b/tgui/packages/tgui/components/Button.js index c95dcbd0d7ca6..7e5b179d2ceba 100644 --- a/tgui/packages/tgui/components/Button.js +++ b/tgui/packages/tgui/components/Button.js @@ -35,6 +35,7 @@ export const Button = (props) => { onclick, onClick, verticalAlignContent, + captureKeys, ...rest } = props; const hasContent = !!(content || children); @@ -77,7 +78,7 @@ export const Button = (props) => { ])} tabIndex={!disabled && '0'} onKeyDown={e => { - if (props.captureKeys === false) { + if (captureKeys === false) { return; } diff --git a/tgui/packages/tgui/components/Dropdown.js b/tgui/packages/tgui/components/Dropdown.js index 6379c8b660512..d0514ea34dd36 100644 --- a/tgui/packages/tgui/components/Dropdown.js +++ b/tgui/packages/tgui/components/Dropdown.js @@ -131,8 +131,8 @@ export class Dropdown extends Component { } this.setOpen(!this.state.open); - if (props.onOpen) { - props.onOpen(event); + if (onOpen) { + onOpen(event); } }}> {icon && ( diff --git a/tgui/packages/tgui/components/Stack.tsx b/tgui/packages/tgui/components/Stack.tsx index 6111ce26db540..e746f1564d004 100644 --- a/tgui/packages/tgui/components/Stack.tsx +++ b/tgui/packages/tgui/components/Stack.tsx @@ -47,7 +47,7 @@ const StackItem = (props: StackItemProps) => { computeFlexItemClassName(rest), ])} ref={innerRef} - {...computeBoxProps(computeFlexItemProps(rest))} + {...computeFlexItemProps(rest)} /> ); }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx index 2c6ee301e3b0e..6953df1efdcd5 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx @@ -4,7 +4,7 @@ import { createComponentVNode } from "inferno"; import type { Inferno, ComponentType } from "inferno"; import { VNodeFlags } from "inferno-vnode-flags"; import { sendAct, useBackend, useLocalState } from "../../../../backend"; -import { Box, Button, Dropdown, NumberInput, Stack } from "../../../../components"; +import { Box, Button, Dropdown, Input, NumberInput, Stack } from "../../../../components"; import { createSetPreference, PreferencesMenuData } from "../../data"; import { ServerPreferencesFetcher } from "../../ServerPreferencesFetcher"; @@ -90,6 +90,16 @@ export const FeatureColorInput = (props: FeatureValueProps) => { export type FeatureToggle = Feature; +export const TextInput = (props: FeatureValueProps) => { + return ( + props.handleSetValue(newValue)} + width="100%" + /> + ); +}; + export const CheckboxInput = ( props: FeatureValueProps ) => { From 18bd67885dbd003fcfcfd19a062f4f6f6208078d Mon Sep 17 00:00:00 2001 From: GoldenAlpharex <58045821+GoldenAlpharex@users.noreply.github.com> Date: Tue, 30 Nov 2021 23:54:40 -0500 Subject: [PATCH 070/269] Numpad keys will now be recognised by the keybinds menu (#63128) Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com> Co-authored-by: Aleksej Komarov --- .../tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx index f3a4000e13ab1..1ec91c6822ebb 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx @@ -49,6 +49,13 @@ const KEY_CODE_TO_BYOND: Record = { "UP": "North", }; +/** + * So, as it turns out, KeyboardEvent seems to be broken with IE 11, the + * DOM_KEY_LOCATION_X codes are all undefined. See this to see why it's 3: + * https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/location + */ +const DOM_KEY_LOCATION_NUMPAD = 3; + const sortKeybindings = sortBy( ([_, keybinding]: [string, Keybinding]) => { return keybinding.name; @@ -74,7 +81,7 @@ const formatKeyboardEvent = (event: KeyboardEvent): string => { text += "Shift"; } - if (event.location === KeyboardEvent.DOM_KEY_LOCATION_NUMPAD) { + if (event.location === DOM_KEY_LOCATION_NUMPAD) { text += "Numpad"; } From 72ef6a2491656639dfd3a5a791b2f3a0f7c4991e Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 7 Jun 2023 20:41:51 -0400 Subject: [PATCH 071/269] tg/63200 --- code/__DEFINES/jobs.dm | 2 + code/modules/client/preferences/README.md | 24 - .../client/preferences/middleware/jobs.dm | 35 ++ code/modules/jobs/job_types/_job.dm | 6 + .../interfaces/PreferencesMenu/JobsPage.tsx | 451 ++++++++---------- .../tgui/interfaces/PreferencesMenu/data.ts | 73 +-- .../interfaces/PreferencesMenu/jobs/base.ts | 7 - .../PreferencesMenu/jobs/departments.ts | 40 -- .../PreferencesMenu/jobs/jobs/ai.ts | 10 - .../PreferencesMenu/jobs/jobs/assistant.ts | 11 - .../jobs/jobs/atmospheric_technician.ts | 11 - .../PreferencesMenu/jobs/jobs/bartender.ts | 10 - .../PreferencesMenu/jobs/jobs/botanist.ts | 10 - .../PreferencesMenu/jobs/jobs/captain.ts | 12 - .../jobs/jobs/cargo_technician.ts | 12 - .../PreferencesMenu/jobs/jobs/chaplain.ts | 11 - .../PreferencesMenu/jobs/jobs/chemist.ts | 11 - .../jobs/jobs/chief_engineer.ts | 11 - .../jobs/jobs/chief_medical_officer.ts | 11 - .../PreferencesMenu/jobs/jobs/clown.ts | 11 - .../PreferencesMenu/jobs/jobs/cook.ts | 10 - .../PreferencesMenu/jobs/jobs/curator.ts | 11 - .../PreferencesMenu/jobs/jobs/cyborg.ts | 10 - .../PreferencesMenu/jobs/jobs/detective.ts | 11 - .../PreferencesMenu/jobs/jobs/geneticist.ts | 11 - .../jobs/jobs/head_of_personnel.ts | 11 - .../jobs/jobs/head_of_security.ts | 12 - .../PreferencesMenu/jobs/jobs/janitor.ts | 11 - .../PreferencesMenu/jobs/jobs/lawyer.ts | 11 - .../jobs/jobs/medical_doctor.ts | 11 - .../PreferencesMenu/jobs/jobs/mime.ts | 10 - .../PreferencesMenu/jobs/jobs/paramedic.ts | 11 - .../PreferencesMenu/jobs/jobs/prisoner.ts | 10 - .../PreferencesMenu/jobs/jobs/psychologist.ts | 11 - .../jobs/jobs/quartermaster.ts | 11 - .../jobs/jobs/research_director.ts | 12 - .../PreferencesMenu/jobs/jobs/roboticist.ts | 10 - .../PreferencesMenu/jobs/jobs/scientist.ts | 10 - .../jobs/jobs/security_officer.ts | 11 - .../PreferencesMenu/jobs/jobs/shaft_miner.ts | 11 - .../jobs/jobs/station_engineer.ts | 11 - .../PreferencesMenu/jobs/jobs/virologist.ts | 11 - .../PreferencesMenu/jobs/jobs/warden.ts | 12 - .../styles/interfaces/PreferencesMenu.scss | 3 +- 44 files changed, 275 insertions(+), 747 deletions(-) delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/base.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/departments.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/ai.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/assistant.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/atmospheric_technician.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/bartender.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/botanist.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/captain.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/cargo_technician.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chaplain.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chemist.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chief_engineer.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/chief_medical_officer.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/clown.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/cook.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/curator.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/cyborg.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/detective.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/geneticist.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/head_of_personnel.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/head_of_security.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/janitor.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/lawyer.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/medical_doctor.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/mime.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/paramedic.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/prisoner.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/psychologist.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/quartermaster.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/research_director.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/roboticist.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/scientist.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/security_officer.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/shaft_miner.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/station_engineer.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/virologist.ts delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/warden.ts diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm index 35051cddd5efc..dc96a87e04b42 100644 --- a/code/__DEFINES/jobs.dm +++ b/code/__DEFINES/jobs.dm @@ -110,6 +110,8 @@ #define DEPT_BITFLAG_SEC (1<<7) #define DEPT_BITFLAG_VIP (1<<8) #define DEPT_BITFLAG_SILICON (1<<9) +#define DEPT_BITFLAG_CAPTAIN (1<<10) +#define DEPT_BITFLAG_ASSISTANT (1<<11) // should check the ones in `\_DEFINES\economy.dm` // It's true that bitflags shouldn't be separated in two DEFINES if these are same, but just in case the system can be devided, it's remained separated. diff --git a/code/modules/client/preferences/README.md b/code/modules/client/preferences/README.md index 0c3384cedf7e9..9403dcc90ce85 100644 --- a/code/modules/client/preferences/README.md +++ b/code/modules/client/preferences/README.md @@ -373,30 +373,6 @@ Middleware can inject its own data at several points, such as providing new UI a --- -## Jobs - -Every job must have an associated `.ts` file so that the UI knows where to place it. - -Create a file in `tgui/packages/tgui/interfaces/PreferencesMenu/jobs/jobs/`. - -This will specify the description and department of the job. Here is the details for the medical doctor: - -```ts -import { Job } from "../base"; -import { Service } from "../departments"; - -const Cook: Job = { - name: "Cook", - // If you need more room, use `multiline` - description: "Serve food, cook meat, keep the crew fed.", - department: Service, -}; - -export default Cook; -``` - ---- - ## Antagonists In order to make an antagonist selectable, you must do a few things: diff --git a/code/modules/client/preferences/middleware/jobs.dm b/code/modules/client/preferences/middleware/jobs.dm index 2d5a26e12ead3..c8b8e20889ad6 100644 --- a/code/modules/client/preferences/middleware/jobs.dm +++ b/code/modules/client/preferences/middleware/jobs.dm @@ -25,6 +25,41 @@ return TRUE +/datum/preference_middleware/jobs/get_constant_data() + var/list/data = list() + + var/list/departments = list() + var/list/jobs = list() + + for (var/datum/job/job as anything in SSjob.occupations) + /*var/department_flag = job.department_for_prefs + + if (isnull(department_flag)) + stack_trace("[job] does not have a department set, yet is a joinable occupation!") + continue*/ + + if (isnull(job.description)) + stack_trace("[job] does not have a description set, yet is a joinable occupation!") + continue + + var/department_name = "testing department" // TODO tgui-prefs department logic + /*if (isnull(departments[department_name])) + var/datum/job/department_head_type = initial(department_type.department_head) + + departments[department_name] = list( + "head" = department_head_type && initial(department_head_type.title), + )*/ + + jobs[job.title] = list( + "description" = job.description, + "department" = department_name, + ) + + data["departments"] = departments + data["jobs"] = jobs + + return data + /datum/preference_middleware/jobs/get_ui_data(mob/user) var/list/data = list() diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm index 87c406faed3de..74c5cdb3324fd 100644 --- a/code/modules/jobs/job_types/_job.dm +++ b/code/modules/jobs/job_types/_job.dm @@ -2,6 +2,10 @@ ///The name of the job , used for preferences, bans and more. Make sure you know what you're doing before changing this. var/title = "NOPE" + /// The description of the job, used for preferences menu. + /// Keep it short and useful. Avoid in-jokes, these are for new players. + var/description = "Someone didn't fill this out... please report this bug!" // TODO tgui-prefs change this to null + ///Job access. The use of minimal_access or access is determined by a config setting: config.jobs_have_minimal_access var/list/minimal_access = list() //Useful for servers which prefer to only have access given to the places a job absolutely needs (Larger server population) var/list/access = list() //Useful for servers which either have fewer players, so each person needs to fill more than one role, or servers which like to give more access, so players can't hide forever in their super secure departments (I'm looking at you, chemistry!) @@ -77,6 +81,8 @@ ///Bitfield of departments this job belongs with var/departments = NONE + /// Same as the departments bitflag, but only one is allowed. Used in the preferences menu. + var/department_for_prefs = null ///Is this job affected by weird spawns like the ones from station traits var/random_spawns_possible = TRUE /// Should this job be allowed to be picked for the bureaucratic error event? diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx index 46f6fa2bf6324..3ada1c5016f40 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx @@ -1,68 +1,27 @@ -import { binaryInsertWith } from "common/collections"; -import { classes } from "common/react"; -import { InfernoNode } from "inferno"; -import { useBackend } from "../../backend"; -import { Box, Button, Dropdown, Stack, Tooltip } from "../../components"; -import { createSetPreference, JoblessRole, JobPriority, PreferencesMenuData } from "./data"; -import { Job } from "./jobs/base"; -import * as Departments from "./jobs/departments"; - -const requireJob = require.context("./jobs/jobs", false, /.ts$/); -const jobsByDepartment = new Map(); - -const binaryInsertJob = binaryInsertWith((job: Job) => { - return job.name; -}); - -const PRIORITY_BUTTON_SIZE = "18px"; - -for (const jobKey of requireJob.keys()) { - const job = requireJob<{ - default?: Job, - }>(jobKey).default; - - if (!job) { - continue; - } - - - let departmentInfo = jobsByDepartment.get(job.department); - if (departmentInfo === undefined) { - departmentInfo = { - jobs: [], - head: undefined, - }; - - jobsByDepartment.set(job.department, departmentInfo); - } - - if (job.department.head === job.name) { - departmentInfo.head = job; - } else { - departmentInfo.jobs = binaryInsertJob(departmentInfo.jobs, job); - } -} - -const PriorityButton = (props: { - name: string, - color: string, - modifier?: string, - enabled: boolean, - onClick: () => void, -}) => { +import { sortBy } from 'common/collections'; +import { classes } from 'common/react'; +import { InfernoNode, Inferno } from 'inferno'; +import { useBackend } from '../../backend'; +import { Box, Button, Dropdown, Stack, Tooltip } from '../../components'; +import { createSetPreference, Job, JoblessRole, JobPriority, PreferencesMenuData } from './data'; +import { ServerPreferencesFetcher } from './ServerPreferencesFetcher'; + +const sortJobs = (entries: [string, Job][], head?: string) => + sortBy<[string, Job]>( + ([key, _]) => (key === head ? -1 : 1), + ([key, _]) => key + )(entries); + +const PRIORITY_BUTTON_SIZE = '18px'; + +const PriorityButton = (props: { name: string; color: string; modifier?: string; enabled: boolean; onClick: () => void }) => { const className = `PreferencesMenu__Jobs__departments__priority`; return ( - - - - )}> +
    + + + + + }> - {props.antagonists.map(antagonist => { - const isBanned = data.antag_bans - && data.antag_bans.indexOf(antagonist.key) !== -1; + {props.antagonists.map((antagonist) => { + const isBanned = data.antag_bans && data.antag_bans.indexOf(antagonist.key) !== -1; - const daysLeft - = (data.antag_days_left && data.antag_days_left[antagonist.key]) - || 0; + const daysLeft = (data.antag_days_left && data.antag_days_left[antagonist.key]) || 0; return ( 0) - ? "banned" - : predictedState.has(antagonist.key) ? "on" : "off" + isBanned || daysLeft > 0 ? 'banned' : predictedState.has(antagonist.key) ? 'on' : 'off' }`, ])} - key={antagonist.key} - > + key={antagonist.key}> - + {antagonist.name} - { - return ( -
    - {text} - { - index !== antagonist.description.length - 1 - && - } -
    - ); - }) - } position="bottom"> + { + return ( +
    + {text} + {index !== antagonist.description.length - 1 && } +
    + ); + }) + } + position="bottom"> { if (isBanned) { return; @@ -156,17 +142,10 @@ const AntagSelection = (props: { } else { enableAntags([antagonist.key]); } - }} - > - - - {isBanned && ( - - )} + }}> + + + {isBanned && } {daysLeft > 0 && ( @@ -188,20 +167,11 @@ const AntagSelection = (props: { export const AntagsPage = () => { return ( - - - - - + + + + + ); }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx index 6fb237fc54e63..52b280ae8a337 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx @@ -1,15 +1,15 @@ -import { exhaustiveCheck } from "common/exhaustive"; -import { useBackend, useLocalState } from "../../backend"; -import { Button, Stack } from "../../components"; -import { Window } from "../../layouts"; -import { PreferencesMenuData } from "./data"; -import { PageButton } from "./PageButton"; -import { AntagsPage } from "./AntagsPage"; -import { JobsPage } from "./JobsPage"; -import { MainPage } from "./MainPage"; -import { SpeciesPage } from "./SpeciesPage"; -import { QuirksPage } from "./QuirksPage"; -import { LoadoutPage } from "./LoadoutPage"; +import { exhaustiveCheck } from 'common/exhaustive'; +import { useBackend, useLocalState } from '../../backend'; +import { Button, Stack } from '../../components'; +import { Window } from '../../layouts'; +import { PreferencesMenuData } from './data'; +import { PageButton } from './PageButton'; +import { AntagsPage } from './AntagsPage'; +import { JobsPage } from './JobsPage'; +import { MainPage } from './MainPage'; +import { SpeciesPage } from './SpeciesPage'; +import { QuirksPage } from './QuirksPage'; +import { LoadoutPage } from './LoadoutPage'; enum Page { Antags, @@ -17,14 +17,14 @@ enum Page { Jobs, Species, Quirks, - Loadout + Loadout, } const CharacterProfiles = (props: { - activeSlot: number, - maxSlot: number, - onClick: (index: number) => void, - profiles: (string | null)[], + activeSlot: number; + maxSlot: number; + onClick: (index: number) => void; + profiles: (string | null)[]; }) => { const { profiles } = props; @@ -37,7 +37,9 @@ const CharacterProfiles = (props: { disabled={slot >= props.maxSlot} onClick={() => { props.onClick(slot); - }} fluid>{profile ?? "New Character"} + }} + fluid> + {profile ?? 'New Character'}
    ))} @@ -48,7 +50,7 @@ const CharacterProfiles = (props: { export const CharacterPreferenceWindow = (props, context) => { const { act, data } = useBackend(context); - const [currentPage, setCurrentPage] = useLocalState(context, "currentPage", Page.Main); + const [currentPage, setCurrentPage] = useLocalState(context, 'currentPage', Page.Main); let pageContents; @@ -60,15 +62,11 @@ export const CharacterPreferenceWindow = (props, context) => { pageContents = ; break; case Page.Main: - pageContents = ( setCurrentPage(Page.Species)} - />); + pageContents = setCurrentPage(Page.Species)} />; break; case Page.Species: - pageContents = ( setCurrentPage(Page.Main)} - />); + pageContents = setCurrentPage(Page.Main)} />; break; case Page.Quirks: @@ -82,11 +80,7 @@ export const CharacterPreferenceWindow = (props, context) => { } return ( - + @@ -94,7 +88,7 @@ export const CharacterPreferenceWindow = (props, context) => { activeSlot={data.active_slot - 1} maxSlot={data.max_slot} onClick={(slot) => { - act("change_slot", { + act('change_slot', { slot: slot + 1, }); }} @@ -102,11 +96,7 @@ export const CharacterPreferenceWindow = (props, context) => { /> - {!data.content_unlocked && ( - - Buy BYOND premium for more slots! - - )} + {!data.content_unlocked && Buy BYOND premium for more slots!} @@ -123,42 +113,29 @@ export const CharacterPreferenceWindow = (props, context) => {
    - + {/* Fun fact: This isn't "Jobs" so that it intentionally catches your eyes, because it's really important! */} - Occupations - + Loadout - + Antagonists - + Quirks @@ -167,9 +144,7 @@ export const CharacterPreferenceWindow = (props, context) => { - - {pageContents} - + {pageContents}
    diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreview.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreview.tsx index ab27d2fe2f54a..71622d4937eea 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreview.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreview.tsx @@ -1,15 +1,14 @@ -import { ByondUi } from "../../components"; +import { ByondUi } from '../../components'; -export const CharacterPreview = (props: { - height: string, - id: string, -}) => { - return (); +export const CharacterPreview = (props: { height: string; id: string }) => { + return ( + + ); }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferenceWindow.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferenceWindow.tsx index a54f869687c06..df0a09ff8e861 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferenceWindow.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferenceWindow.tsx @@ -1,23 +1,25 @@ -import { Stack } from "../../components"; -import { Window } from "../../layouts"; -import { KeybindingsPage } from "./KeybindingsPage"; -import { GamePreferencesPage } from "./GamePreferencesPage"; -import { PageButton } from "./PageButton"; -import { useBackend, useLocalState } from "../../backend"; -import { GamePreferencesSelectedPage, PreferencesMenuData } from "./data"; -import { exhaustiveCheck } from "common/exhaustive"; +import { Stack } from '../../components'; +import { Window } from '../../layouts'; +import { KeybindingsPage } from './KeybindingsPage'; +import { GamePreferencesPage } from './GamePreferencesPage'; +import { PageButton } from './PageButton'; +import { useBackend, useLocalState } from '../../backend'; +import { GamePreferencesSelectedPage, PreferencesMenuData } from './data'; +import { exhaustiveCheck } from 'common/exhaustive'; -export const GamePreferenceWindow = (props: { - startingPage?: GamePreferencesSelectedPage, -}, context) => { +export const GamePreferenceWindow = ( + props: { + startingPage?: GamePreferencesSelectedPage; + }, + context +) => { const { act, data } = useBackend(context); - const [currentPage, setCurrentPage] - = useLocalState( - context, - "currentPage", - props.startingPage ?? GamePreferencesSelectedPage.Settings, - ); + const [currentPage, setCurrentPage] = useLocalState( + context, + 'currentPage', + props.startingPage ?? GamePreferencesSelectedPage.Settings + ); let pageContents; @@ -32,34 +34,20 @@ export const GamePreferenceWindow = (props: { exhaustiveCheck(currentPage); } - return ( - + - + Settings - + Keybindings diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx index 49376980e2b25..a29fa45410300 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx @@ -1,20 +1,18 @@ -import { binaryInsertWith, sortBy } from "common/collections"; -import type { InfernoNode } from "inferno"; -import { useBackend } from "../../backend"; -import { Box, Flex, Tooltip } from "../../components"; -import { PreferencesMenuData } from "./data"; -import features from "./preferences/features"; -import { FeatureValueInput } from "./preferences/features/base"; -import { TabbedMenu } from "./TabbedMenu"; +import { binaryInsertWith, sortBy } from 'common/collections'; +import type { InfernoNode } from 'inferno'; +import { useBackend } from '../../backend'; +import { Box, Flex, Tooltip } from '../../components'; +import { PreferencesMenuData } from './data'; +import features from './preferences/features'; +import { FeatureValueInput } from './preferences/features/base'; +import { TabbedMenu } from './TabbedMenu'; type PreferenceChild = { - name: string, - children: InfernoNode, + name: string; + children: InfernoNode; }; -const binaryInsertPreference = binaryInsertWith( - (child) => child.name, -); +const binaryInsertPreference = binaryInsertWith((child) => child.name); const sortByName = sortBy<[string, PreferenceChild[]]>(([name]) => name); @@ -23,18 +21,18 @@ export const GamePreferencesPage = (props, context) => { const gamePreferences: Record = {}; - for (const [featureId, value] of Object.entries( - data.character_preferences.game_preferences - )) { + for (const [featureId, value] of Object.entries(data.character_preferences.game_preferences)) { const feature = features[featureId]; let nameInner: InfernoNode = feature?.name || featureId; if (feature?.description) { nameInner = ( - + {nameInner} ); @@ -59,12 +57,7 @@ export const GamePreferencesPage = (props, context) => { {name} - {feature && || ( + {(feature && ) || ( ...is not filled out properly!!! @@ -78,18 +71,16 @@ export const GamePreferencesPage = (props, context) => { children: child, }; - const category = feature?.category || "ERROR"; + const category = feature?.category || 'ERROR'; - gamePreferences[category] - = binaryInsertPreference(gamePreferences[category] || [], entry); + gamePreferences[category] = binaryInsertPreference(gamePreferences[category] || [], entry); } - const gamePreferenceEntries: [string, InfernoNode][] = sortByName( - Object.entries(gamePreferences) - ).map( + const gamePreferenceEntries: [string, InfernoNode][] = sortByName(Object.entries(gamePreferences)).map( ([category, preferences]) => { - return [category, preferences.map(entry => entry.children)]; - }); + return [category, preferences.map((entry) => entry.children)]; + } + ); return ( >; type KeybindingsPageState = { keybindings?: Keybindings; lastKeyboardEvent?: KeyboardEvent; - selectedKeybindings?: PreferencesMenuData["keybindings"]; + selectedKeybindings?: PreferencesMenuData['keybindings']; /** * The current hotkey that the user is rebinding. @@ -29,24 +29,21 @@ type KeybindingsPageState = { }; const isStandardKey = (event: KeyboardEvent): boolean => { - return event.key !== "Alt" - && event.key !== "Control" - && event.key !== "Shift" - && event.key !== "Esc"; + return event.key !== 'Alt' && event.key !== 'Control' && event.key !== 'Shift' && event.key !== 'Esc'; }; const KEY_CODE_TO_BYOND: Record = { - "DEL": "Delete", - "DOWN": "South", - "END": "Southwest", - "HOME": "Northwest", - "INSERT": "Insert", - "LEFT": "West", - "PAGEDOWN": "Southeast", - "PAGEUP": "Northeast", - "RIGHT": "East", - "SPACEBAR": "Space", - "UP": "North", + 'DEL': 'Delete', + 'DOWN': 'South', + 'END': 'Southwest', + 'HOME': 'Northwest', + 'INSERT': 'Insert', + 'LEFT': 'West', + 'PAGEDOWN': 'Southeast', + 'PAGEUP': 'Northeast', + 'RIGHT': 'East', + 'SPACEBAR': 'Space', + 'UP': 'North', }; /** @@ -56,33 +53,31 @@ const KEY_CODE_TO_BYOND: Record = { */ const DOM_KEY_LOCATION_NUMPAD = 3; -const sortKeybindings = sortBy( - ([_, keybinding]: [string, Keybinding]) => { - return keybinding.name; - }); +const sortKeybindings = sortBy(([_, keybinding]: [string, Keybinding]) => { + return keybinding.name; +}); -const sortKeybindingsByCategory = sortBy( - ([category, _]: [string, Record]) => { - return category; - }); +const sortKeybindingsByCategory = sortBy(([category, _]: [string, Record]) => { + return category; +}); const formatKeyboardEvent = (event: KeyboardEvent): string => { - let text = ""; + let text = ''; if (event.altKey) { - text += "Alt"; + text += 'Alt'; } if (event.ctrlKey) { - text += "Ctrl"; + text += 'Ctrl'; } if (event.shiftKey) { - text += "Shift"; + text += 'Shift'; } if (event.location === DOM_KEY_LOCATION_NUMPAD) { - text += "Numpad"; + text += 'Numpad'; } if (isStandardKey(event)) { @@ -98,27 +93,23 @@ const moveToBottom = (entries: [string, unknown][], findCategory: string) => { entries.splice( entries.findIndex(([category, _]) => { return category === findCategory; - }), 1 + }), + 1 )[0] ); }; class KeybindingButton extends Component<{ - currentHotkey?: string, - onClick?: () => void, - typingHotkey?: string, + currentHotkey?: string; + onClick?: () => void; + typingHotkey?: string; }> { shouldComponentUpdate(nextProps) { - return this.props.typingHotkey !== nextProps.typingHotkey - || this.props.currentHotkey !== nextProps.currentHotkey; + return this.props.typingHotkey !== nextProps.typingHotkey || this.props.currentHotkey !== nextProps.currentHotkey; } render() { - const { - currentHotkey, - onClick, - typingHotkey, - } = this.props; + const { currentHotkey, onClick, typingHotkey } = this.props; const child = ( ); if (typingHotkey && onClick) { return ( // onClick will cancel it - - {child} - + {child} ); } else { return child; @@ -144,24 +133,22 @@ class KeybindingButton extends Component<{ } } -const KeybindingName = (props: { - keybinding: Keybinding, -}) => { +const KeybindingName = (props: { keybinding: Keybinding }) => { const { keybinding } = props; - return keybinding.description - ? ( - - + - {keybinding.name} - - - ) - : {keybinding.name}; + {keybinding.name} + + + ) : ( + {keybinding.name} + ); }; KeybindingName.defaultHooks = { @@ -170,9 +157,12 @@ KeybindingName.defaultHooks = { }, }; -const ResetToDefaultButton = (props: { - keybindingId: string, -}, context) => { +const ResetToDefaultButton = ( + props: { + keybindingId: string; + }, + context +) => { const { act } = useBackend(context); return ( @@ -180,7 +170,7 @@ const ResetToDefaultButton = (props: { fluid textAlign="center" onClick={() => { - act("reset_keybinds_to_defaults", { + act('reset_keybinds_to_defaults', { keybind_name: props.keybindingId, }); }}> @@ -192,7 +182,7 @@ const ResetToDefaultButton = (props: { export class KeybindingsPage extends Component<{}, KeybindingsPageState> { cancelNextKeyUp?: number; keybindingOnClicks: Record void)[]> = {}; - lastKeybinds?: PreferencesMenuData["keybindings"]; + lastKeybinds?: PreferencesMenuData['keybindings']; state: KeybindingsPageState = { lastKeyboardEvent: undefined, keybindings: undefined, @@ -241,10 +231,7 @@ export class KeybindingsPage extends Component<{}, KeybindingsPageState> { if (selectedKeybindings[keybindName]) { if (value) { - selectedKeybindings[keybindName][Math.min( - selectedKeybindings[keybindName].length, - slot - )] = value; + selectedKeybindings[keybindName][Math.min(selectedKeybindings[keybindName].length, slot)] = value; } else { selectedKeybindings[keybindName].splice(slot, 1); } @@ -254,9 +241,9 @@ export class KeybindingsPage extends Component<{}, KeybindingsPageState> { selectedKeybindings[keybindName] = [value]; } - act("set_keybindings", { - "keybind_name": keybindName, - "hotkeys": selectedKeybindings[keybindName], + act('set_keybindings', { + 'keybind_name': keybindName, + 'hotkeys': selectedKeybindings[keybindName], }); return { @@ -282,7 +269,7 @@ export class KeybindingsPage extends Component<{}, KeybindingsPageState> { if (isStandardKey(event)) { this.setRebindingHotkey(formatKeyboardEvent(event)); return; - } else if (event.key === "Esc") { + } else if (event.key === 'Esc') { this.setRebindingHotkey(undefined); return; } @@ -309,10 +296,7 @@ export class KeybindingsPage extends Component<{}, KeybindingsPageState> { } } - getKeybindingOnClick( - keybindingId: string, - slot: number, - ): () => void { + getKeybindingOnClick(keybindingId: string, slot: number): () => void { if (!this.keybindingOnClicks[keybindingId]) { this.keybindingOnClicks[keybindingId] = []; } @@ -346,21 +330,19 @@ export class KeybindingsPage extends Component<{}, KeybindingsPageState> { return undefined; } - if (rebindingHotkey[0] !== keybindingId - || rebindingHotkey[1] !== slot - ) { + if (rebindingHotkey[0] !== keybindingId || rebindingHotkey[1] !== slot) { return undefined; } if (lastKeyboardEvent === undefined) { - return "..."; + return '...'; } return formatKeyboardEvent(lastKeyboardEvent); } async populateKeybindings() { - const keybindingsResponse = await fetchRetry(resolveAsset("keybindings.json")); + const keybindingsResponse = await fetchRetry(resolveAsset('keybindings.json')); const keybindingsData: Keybindings = await keybindingsResponse.json(); this.setState({ @@ -375,10 +357,9 @@ export class KeybindingsPage extends Component<{}, KeybindingsPageState> { this.setState({ selectedKeybindings: Object.fromEntries( - Object.entries(data.keybindings) - .map(([keybind, hotkeys]) => { - return [keybind, hotkeys.filter(value => value !== "Unbound")]; - }) + Object.entries(data.keybindings).map(([keybind, hotkeys]) => { + return [keybind, hotkeys.filter((value) => value !== 'Unbound')]; + }) ), }); } @@ -391,83 +372,62 @@ export class KeybindingsPage extends Component<{}, KeybindingsPageState> { return Loading keybindings...; } - const keybindingEntries = sortKeybindingsByCategory( - Object.entries(keybindings) - ); + const keybindingEntries = sortKeybindingsByCategory(Object.entries(keybindings)); - moveToBottom(keybindingEntries, "EMOTE"); - moveToBottom(keybindingEntries, "ADMIN"); + moveToBottom(keybindingEntries, 'EMOTE'); + moveToBottom(keybindingEntries, 'ADMIN'); return ( <> - + { - return [category, ( - - {sortKeybindings(Object.entries(keybindings)).map( - ([keybindingId, keybinding]) => { - const keys - = this.state.selectedKeybindings![keybindingId] - || []; - - const name = ( - - + categoryEntries={keybindingEntries.map(([category, keybindings]) => { + return [ + category, + + {sortKeybindings(Object.entries(keybindings)).map(([keybindingId, keybinding]) => { + const keys = this.state.selectedKeybindings![keybindingId] || []; + + const name = ( + + + + ); + + return ( + + + {name} + + {range(0, 3).map((key) => ( + + + + ))} + + + - ); - - return ( - - - {name} - - {range(0, 3).map(key => ( - - - - ))} - - - - - - - ); - } - )} - - )]; - } - )} + + + ); + })} + , + ]; + })} /> - act("reset_all_keybinds")} - /> + act('reset_all_keybinds')} /> - ); diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx index dfb73318aa944..1f48239ebc064 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx @@ -11,11 +11,7 @@ export const LoadoutPage = (props, context) => { if (!data) { return Loading loadout data...; } - const { - categories = [], - purchased_gear = [], - equipped_gear = [], - } = data.loadout; + const { categories = [], purchased_gear = [], equipped_gear = [] } = data.loadout; return ( diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx index abf9c457351ae..cda5c8fc211b9 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx @@ -1,16 +1,16 @@ -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 { CharacterPreview } from "./CharacterPreview"; -import { RandomizationButton } from "./RandomizationButton"; -import { ServerPreferencesFetcher } from "./ServerPreferencesFetcher"; -import { MultiNameInput, NameInput } from "./names"; -import { Gender, GENDERS } from "./preferences/gender"; -import features from "./preferences/features"; -import { FeatureChoicedServerData, FeatureValueInput } from "./preferences/features/base"; -import { filterMap, sortBy } from "common/collections"; -import { useRandomToggleState } from "./useRandomToggleState"; +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 { CharacterPreview } from './CharacterPreview'; +import { RandomizationButton } from './RandomizationButton'; +import { ServerPreferencesFetcher } from './ServerPreferencesFetcher'; +import { MultiNameInput, NameInput } from './names'; +import { Gender, GENDERS } from './preferences/gender'; +import features from './preferences/features'; +import { FeatureChoicedServerData, FeatureValueInput } from './preferences/features/base'; +import { filterMap, sortBy } from 'common/collections'; +import { useRandomToggleState } from './useRandomToggleState'; const CLOTHING_CELL_SIZE = 48; const CLOTHING_SIDEBAR_ROWS = 9; @@ -20,80 +20,60 @@ const CLOTHING_SELECTION_WIDTH = 5.4; const CLOTHING_SELECTION_MULTIPLIER = 5.2; const CharacterControls = (props: { - handleRotate: () => void, - handleOpenSpecies: () => void, - gender: Gender, - setGender: (gender: Gender) => void, - showGender: boolean, + handleRotate: () => void; + handleOpenSpecies: () => void; + gender: Gender; + setGender: (gender: Gender) => void; + showGender: boolean; }) => { return ( - @@ -140,9 +118,8 @@ const ChoicedSelection = (props: { key={index} basis={`${CLOTHING_SELECTION_CELL_SIZE}px`} style={{ - padding: "5px", - }} - > + padding: '5px', + }}>
    ); @@ -168,20 +144,24 @@ const ChoicedSelection = (props: { ); }; -const GenderButton = (props: { - handleSetGender: (gender: Gender) => void, - gender: Gender, -}, context) => { - const [genderMenuOpen, setGenderMenuOpen] = useLocalState(context, "genderMenuOpen", false); +const GenderButton = ( + props: { + handleSetGender: (gender: Gender) => void; + gender: Gender; + }, + context +) => { + const [genderMenuOpen, setGenderMenuOpen] = useLocalState(context, 'genderMenuOpen', false); return ( - - {[Gender.Male, Gender.Female, Gender.Other].map(gender => { + {[Gender.Male, Gender.Female, Gender.Other].map((gender) => { return ( ); }; -const createSetRandomization = ( - act: typeof sendAct, - preference: string, -) => (newSetting: RandomSetting) => { - act("set_random_preference", { +const createSetRandomization = (act: typeof sendAct, preference: string) => (newSetting: RandomSetting) => { + act('set_random_preference', { preference, value: newSetting, }); }; -const sortPreferences = sortBy<[string, unknown]>( - ([featureId, _]) => { - const feature = features[featureId]; - return feature?.name; - }); +const sortPreferences = sortBy<[string, unknown]>(([featureId, _]) => { + const feature = features[featureId]; + return feature?.name; +}); const PreferenceList = (props: { - act: typeof sendAct, - preferences: Record, - randomizations: Record, + act: typeof sendAct; + preferences: Record; + randomizations: Record; }) => { return ( - + - { - sortPreferences(Object.entries(props.preferences)) - .map(([featureId, value]) => { - const feature = features[featureId]; - const randomSetting = props.randomizations[featureId]; + {sortPreferences(Object.entries(props.preferences)).map(([featureId, value]) => { + const feature = features[featureId]; + const randomSetting = props.randomizations[featureId]; + + if (feature === undefined) { + return ( + + Feature {featureId} is not recognized. + + ); + } - if (feature === undefined) { - return ( - - Feature {featureId} is not recognized. + return ( + + + {randomSetting && ( + + - ); - } + )} - return ( - - - {randomSetting && ( - - - - )} - - - - - - - ); - }) - } + + + + + + ); + })} ); }; -export const MainPage = (props: { - openSpecies: () => void, -}, context) => { +export const MainPage = ( + props: { + openSpecies: () => void; + }, + context +) => { const { act, data } = useBackend(context); - const [currentClothingMenu, setCurrentClothingMenu] - = useLocalState(context, "currentClothingMenu", null); - const [multiNameInputOpen, setMultiNameInputOpen] - = useLocalState(context, "multiNameInputOpen", false); + const [currentClothingMenu, setCurrentClothingMenu] = useLocalState(context, 'currentClothingMenu', null); + const [multiNameInputOpen, setMultiNameInputOpen] = useLocalState(context, 'multiNameInputOpen', false); const [randomToggleEnabled] = useRandomToggleState(context); return ( - { - const currentSpeciesData = serverData && serverData.species[ - data - .character_preferences - .misc - .species - ]; - - const contextualPreferences - = data.character_preferences.secondary_features || []; - - const mainFeatures = [ - ...Object.entries(data.character_preferences.clothing), - ...Object.entries(data.character_preferences.features) - .filter(([featureName]) => { + { + const currentSpeciesData = serverData && serverData.species[data.character_preferences.misc.species]; + + const contextualPreferences = data.character_preferences.secondary_features || []; + + const mainFeatures = [ + ...Object.entries(data.character_preferences.clothing), + ...Object.entries(data.character_preferences.features).filter(([featureName]) => { if (!currentSpeciesData) { return false; } - return currentSpeciesData - .enabled_features - .indexOf(featureName) !== -1; + return currentSpeciesData.enabled_features.indexOf(featureName) !== -1; }), - ]; - - const randomBodyEnabled - = (data.character_preferences.non_contextual.body_is_always_random - !== RandomSetting.Disabled) - || randomToggleEnabled; - - const getRandomization = ( - preferences: Record - ): Record => { - if (!serverData) { - return {}; - } + ]; - return Object.fromEntries(filterMap( - Object.keys(preferences), preferenceKey => { - if (serverData.random.randomizable.indexOf(preferenceKey) === -1) { - return undefined; - } + const randomBodyEnabled = + data.character_preferences.non_contextual.body_is_always_random !== RandomSetting.Disabled || randomToggleEnabled; - if (!randomBodyEnabled) { - return undefined; - } + const getRandomization = (preferences: Record): Record => { + if (!serverData) { + return {}; + } - return [ - preferenceKey, - data.character_preferences.randomization[preferenceKey] - || RandomSetting.Disabled, - ]; - })); - }; + return Object.fromEntries( + filterMap(Object.keys(preferences), (preferenceKey) => { + if (serverData.random.randomizable.indexOf(preferenceKey) === -1) { + return undefined; + } - const randomizationOfMainFeatures - = getRandomization(Object.fromEntries(mainFeatures)); + if (!randomBodyEnabled) { + return undefined; + } + + return [preferenceKey, data.character_preferences.randomization[preferenceKey] || RandomSetting.Disabled]; + }) + ); + }; - const nonContextualPreferences - = { + const randomizationOfMainFeatures = getRandomization(Object.fromEntries(mainFeatures)); + + const nonContextualPreferences = { ...data.character_preferences.non_contextual, }; - if (randomBodyEnabled) { - nonContextualPreferences["random_species"] - = data.character_preferences.randomization["species"]; - } else { - // We can't use random_name/is_accessible because the - // server doesn't know whether the random toggle is on. - delete nonContextualPreferences['name_is_always_random']; - } - - return ( - <> - {multiNameInputOpen && setMultiNameInputOpen(false)} - handleRandomizeName={preference => act("randomize_name", { - preference, - })} - handleUpdateName={(nameType, value) => act("set_preference", { - preference: nameType, - value, - })} - names={data.character_preferences.names} - />} - - - - - - { - act("rotate"); - }} - setGender={createSetPreference(act, "gender")} - showGender={ - currentSpeciesData ? !!currentSpeciesData.sexes : true - } - /> - + if (randomBodyEnabled) { + nonContextualPreferences['random_species'] = data.character_preferences.randomization['species']; + } else { + // We can't use random_name/is_accessible because the + // server doesn't know whether the random toggle is on. + delete nonContextualPreferences['name_is_always_random']; + } - - - + return ( + <> + {multiNameInputOpen && ( + setMultiNameInputOpen(false)} + handleRandomizeName={(preference) => + act('randomize_name', { + preference, + }) + } + handleUpdateName={(nameType, value) => + act('set_preference', { + preference: nameType, + value, + }) + } + names={data.character_preferences.names} + /> + )} - - { - setMultiNameInputOpen(true); - }} - /> - + + + + + { + act('rotate'); + }} + setGender={createSetPreference(act, 'gender')} + showGender={currentSpeciesData ? !!currentSpeciesData.sexes : true} + /> + - - + + + - - - {mainFeatures - .map(([clothingKey, clothing]) => { - const catalog = ( - serverData - && serverData[clothingKey] as - FeatureChoicedServerData & { - name: string, - } - ); + + { + setMultiNameInputOpen(true); + }} + /> + + + - return catalog && ( - - { - setCurrentClothingMenu(null); - }} - handleOpen={() => { - setCurrentClothingMenu(clothingKey); - }} - handleSelect={createSetPreference(act, clothingKey)} - randomization={ - randomizationOfMainFeatures[clothingKey] - } - setRandomization={createSetRandomization( - act, - clothingKey, - )} - /> - + + + {mainFeatures.map(([clothingKey, clothing]) => { + const catalog = + serverData && + (serverData[clothingKey] as FeatureChoicedServerData & { + name: string; + }); + + return ( + catalog && ( + + { + setCurrentClothingMenu(null); + }} + handleOpen={() => { + setCurrentClothingMenu(clothingKey); + }} + handleSelect={createSetPreference(act, clothingKey)} + randomization={randomizationOfMainFeatures[clothingKey]} + setRandomization={createSetRandomization(act, clothingKey)} + /> + + ) ); })} - - + + - - - + + + - - - - - - ); - }} /> + + + + + + ); + }} + /> ); }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/PageButton.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/PageButton.tsx index 9189898231915..19e3f0ce58a41 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/PageButton.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/PageButton.tsx @@ -1,29 +1,20 @@ -import type { InfernoNode } from "inferno"; -import { Button } from "../../components"; +import type { InfernoNode } from 'inferno'; +import { Button } from '../../components'; export const PageButton =

    (props: { - currentPage: P, - page: P, - otherActivePages?: P[], + currentPage: P; + page: P; + otherActivePages?: P[]; - setPage: (page: P) => void, + setPage: (page: P) => void; - children?: InfernoNode, + children?: InfernoNode; }) => { - const pageIsActive = props.currentPage === props.page - || ( - props.otherActivePages - && props.otherActivePages.indexOf(props.currentPage) !== -1 - ); + const pageIsActive = + props.currentPage === props.page || (props.otherActivePages && props.otherActivePages.indexOf(props.currentPage) !== -1); return ( - ); diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx index 21a0c6af5af14..0f37e8bbd7b21 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx @@ -1,30 +1,33 @@ -import type { Inferno } from "inferno"; -import { Box, Icon, Stack, Tooltip } from "../../components"; -import { PreferencesMenuData, Quirk } from "./data"; -import { useBackend, useLocalState } from "../../backend"; -import { ServerPreferencesFetcher } from "./ServerPreferencesFetcher"; +import type { Inferno } from 'inferno'; +import { Box, Icon, Stack, Tooltip } from '../../components'; +import { PreferencesMenuData, Quirk } from './data'; +import { useBackend, useLocalState } from '../../backend'; +import { ServerPreferencesFetcher } from './ServerPreferencesFetcher'; const getValueClass = (value: number): string => { if (value > 0) { - return "positive"; + return 'positive'; } else if (value < 0) { - return "negative"; + return 'negative'; } else { - return "neutral"; + return 'neutral'; } }; const QuirkList = (props: { - quirks: [string, Quirk & { - failTooltip?: string; - }][], - onClick: (quirkName: string, quirk: Quirk) => void, + quirks: [ + string, + Quirk & { + failTooltip?: string; + } + ][]; + onClick: (quirkName: string, quirk: Quirk) => void; }) => { return ( // Stack is not used here for a variety of IE flex bugs {props.quirks.map(([quirkKey, quirk]) => { - const className = "PreferencesMenu__Quirks__QuirkList__quirk"; + const className = 'PreferencesMenu__Quirks__QuirkList__quirk'; const child = ( - - + + - + - + // Fixes an IE bug for text overflowing in Flex boxes + 'min-width': '0%', + }}> - + {quirk.name} @@ -81,10 +87,13 @@ const QuirkList = (props: { - + {quirk.description} @@ -109,13 +118,7 @@ const QuirkList = (props: { const StatDisplay: Inferno.StatelessComponent<{}> = (props) => { return ( - + {props.children} ); @@ -127,26 +130,22 @@ export const QuirksPage = (props, context) => { const [selectedQuirks, setSelectedQuirks] = useLocalState( context, `selectedQuirks_${data.active_slot}`, - data.selected_quirks, + data.selected_quirks ); return ( { + render={(data) => { if (!data) { return Loading quirks...; } - const { - max_positive_quirks: maxPositiveQuirks, - quirk_blacklist: quirkBlacklist, - quirk_info: quirkInfo, - } = data.quirks; + const { max_positive_quirks: maxPositiveQuirks, quirk_blacklist: quirkBlacklist, quirk_info: quirkInfo } = data.quirks; const quirks = Object.entries(quirkInfo); quirks.sort(([_, quirkA], [__, quirkB]) => { if (quirkA.value === quirkB.value) { - return (quirkA.name > quirkB.name) ? 1 : -1; + return quirkA.name > quirkB.name ? 1 : -1; } else { return quirkA.value - quirkB.value; } @@ -171,17 +170,15 @@ export const QuirksPage = (props, context) => { const getReasonToNotAdd = (quirkName: string) => { const quirk = quirkInfo[quirkName]; - if ( - quirk.value > 0 - ) { + if (quirk.value > 0) { if (positiveQuirks >= maxPositiveQuirks) { return "You can't have any more positive quirks!"; } else if (balance + quirk.value > 0) { - return "You need a negative quirk to balance this out!"; + return 'You need a negative quirk to balance this out!'; } } - const selectedQuirkNames = selectedQuirks.map(quirkKey => { + const selectedQuirkNames = selectedQuirks.map((quirkKey) => { return quirkInfo[quirkKey].name; }); @@ -191,10 +188,7 @@ export const QuirksPage = (props, context) => { } for (const incompatibleQuirk of blacklist) { - if ( - incompatibleQuirk !== quirk.name - && selectedQuirkNames.indexOf(incompatibleQuirk) !== -1 - ) { + if (incompatibleQuirk !== quirk.name && selectedQuirkNames.indexOf(incompatibleQuirk) !== -1) { return `This is incompatible with ${incompatibleQuirk}!`; } } @@ -207,7 +201,7 @@ export const QuirksPage = (props, context) => { const quirk = quirkInfo[quirkName]; if (balance - quirk.value > 0) { - return "You need to remove a positive quirk first!"; + return 'You need to remove a positive quirk first!'; } return undefined; @@ -218,9 +212,7 @@ export const QuirksPage = (props, context) => { - - Positive Quirks - + Positive Quirks @@ -244,43 +236,38 @@ export const QuirksPage = (props, context) => { setSelectedQuirks(selectedQuirks.concat(quirkName)); - act("give_quirk", { quirk: quirk.name }); + act('give_quirk', { quirk: quirk.name }); }} - quirks={quirks.filter(([quirkName, _]) => { - return selectedQuirks - .indexOf(quirkName) === -1; - }).map(([quirkName, quirk]) => { - return [quirkName, { - ...quirk, - failTooltip: getReasonToNotAdd(quirkName), - }]; - })} + quirks={quirks + .filter(([quirkName, _]) => { + return selectedQuirks.indexOf(quirkName) === -1; + }) + .map(([quirkName, quirk]) => { + return [ + quirkName, + { + ...quirk, + failTooltip: getReasonToNotAdd(quirkName), + }, + ]; + })} /> - + - - Quirk Balance - + Quirk Balance - - {balance} - + {balance} @@ -296,22 +283,23 @@ export const QuirksPage = (props, context) => { return; } - setSelectedQuirks( - selectedQuirks - .filter(otherQuirk => quirkName !== otherQuirk), - ); + setSelectedQuirks(selectedQuirks.filter((otherQuirk) => quirkName !== otherQuirk)); - act("remove_quirk", { quirk: quirk.name }); + act('remove_quirk', { quirk: quirk.name }); }} - quirks={quirks.filter(([quirkName, _]) => { - return selectedQuirks - .indexOf(quirkName) !== -1; - }).map(([quirkName, quirk]) => { - return [quirkName, { - ...quirk, - failTooltip: getReasonToNotRemove(quirkName), - }]; - })} + quirks={quirks + .filter(([quirkName, _]) => { + return selectedQuirks.indexOf(quirkName) !== -1; + }) + .map(([quirkName, quirk]) => { + return [ + quirkName, + { + ...quirk, + failTooltip: getReasonToNotRemove(quirkName), + }, + ]; + })} /> diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/RandomizationButton.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/RandomizationButton.tsx index ea9a4a64a604a..ea33f7437f050 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/RandomizationButton.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/RandomizationButton.tsx @@ -1,29 +1,25 @@ -import { Dropdown, Icon } from "../../components"; -import { RandomSetting } from "./data"; -import { exhaustiveCheck } from "common/exhaustive"; +import { Dropdown, Icon } from '../../components'; +import { RandomSetting } from './data'; +import { exhaustiveCheck } from 'common/exhaustive'; export const RandomizationButton = (props: { - dropdownProps?: Record, - setValue: (newValue: RandomSetting) => void, - value: RandomSetting, + dropdownProps?: Record; + setValue: (newValue: RandomSetting) => void; + value: RandomSetting; }) => { - const { - dropdownProps = {}, - setValue, - value, - } = props; + const { dropdownProps = {}, setValue, value } = props; let color; switch (value) { case RandomSetting.AntagOnly: - color = "orange"; + color = 'orange'; break; case RandomSetting.Disabled: - color = "red"; + color = 'red'; break; case RandomSetting.Enabled: - color = "green"; + color = 'green'; break; default: exhaustiveCheck(value); @@ -34,25 +30,20 @@ export const RandomizationButton = (props: { backgroundColor={color} {...dropdownProps} clipSelectedText={false} - displayText={( - - )} + displayText={} options={[ { - displayText: "Do not randomize", + displayText: 'Do not randomize', value: RandomSetting.Disabled, }, { - displayText: "Always randomize", + displayText: 'Always randomize', value: RandomSetting.Enabled, }, { - displayText: "Randomize when antagonist", + displayText: 'Randomize when antagonist', value: RandomSetting.AntagOnly, }, ]} diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/ServerPreferencesFetcher.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/ServerPreferencesFetcher.tsx index a25647d3d6e2a..c0427b6e3668b 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/ServerPreferencesFetcher.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/ServerPreferencesFetcher.tsx @@ -1,18 +1,20 @@ -import { Component } from "inferno"; -import type { InfernoNode } from "inferno"; -import { resolveAsset } from "../../assets"; -import { fetchRetry } from "../../http"; -import { ServerData } from "./data"; +import { Component } from 'inferno'; +import type { InfernoNode } from 'inferno'; +import { resolveAsset } from '../../assets'; +import { fetchRetry } from '../../http'; +import { ServerData } from './data'; // Cache response so it's only sent once let fetchServerData: Promise | undefined; -export class ServerPreferencesFetcher extends Component<{ - render: (serverData: ServerData | undefined) => InfernoNode, -}, { - serverData?: ServerData; -}> { - +export class ServerPreferencesFetcher extends Component< + { + render: (serverData: ServerData | undefined) => InfernoNode; + }, + { + serverData?: ServerData; + } +> { constructor() { super(); this.state = { @@ -26,8 +28,7 @@ export class ServerPreferencesFetcher extends Component<{ async populateServerData() { if (!fetchServerData) { - fetchServerData = fetchRetry(resolveAsset("preferences.json")) - .then(response => response.json()); + fetchServerData = fetchRetry(resolveAsset('preferences.json')).then((response) => response.json()); } const preferencesData: ServerData = await fetchServerData; @@ -38,6 +39,8 @@ export class ServerPreferencesFetcher extends Component<{ } render() { - return this.state !== null && this.state.serverData !== null ? this.props.render(this.state.serverData) : "Error: Unable to fetch preferences data."; + return this.state !== null && this.state.serverData !== null + ? this.props.render(this.state.serverData) + : 'Error: Unable to fetch preferences data.'; } } diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/SpeciesPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/SpeciesPage.tsx index fdee08ea310bc..4fff117c3c98e 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/SpeciesPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/SpeciesPage.tsx @@ -1,61 +1,52 @@ -import { classes } from "common/react"; -import { useBackend } from "../../backend"; -import { BlockQuote, Box, Button, Divider, Icon, Section, Stack, Tooltip } from "../../components"; -import { CharacterPreview } from "./CharacterPreview"; -import { createSetPreference, Food, PreferencesMenuData, ServerData, ServerSpeciesData } from "./data"; -import { Feature, Species, fallbackSpecies } from "./preferences/species/base"; -import { ServerPreferencesFetcher } from "./ServerPreferencesFetcher"; +import { classes } from 'common/react'; +import { useBackend } from '../../backend'; +import { BlockQuote, Box, Button, Divider, Icon, Section, Stack, Tooltip } from '../../components'; +import { CharacterPreview } from './CharacterPreview'; +import { createSetPreference, Food, PreferencesMenuData, ServerData, ServerSpeciesData } from './data'; +import { Feature, Species, fallbackSpecies } from './preferences/species/base'; +import { ServerPreferencesFetcher } from './ServerPreferencesFetcher'; -const requireSpecies = require.context("./preferences/species"); +const requireSpecies = require.context('./preferences/species'); const FOOD_ICONS = { - [Food.Cloth]: "tshirt", - [Food.Dairy]: "cheese", - [Food.Fried]: "bacon", - [Food.Fruit]: "apple-alt", - [Food.Grain]: "bread-slice", - [Food.Gross]: "trash", - [Food.Junkfood]: "pizza-slice", - [Food.Meat]: "hamburger", - [Food.Raw]: "drumstick-bite", - [Food.Sugar]: "candy-cane", - [Food.Toxic]: "biohazard", - [Food.Vegetables]: "carrot", + [Food.Cloth]: 'tshirt', + [Food.Dairy]: 'cheese', + [Food.Fried]: 'bacon', + [Food.Fruit]: 'apple-alt', + [Food.Grain]: 'bread-slice', + [Food.Gross]: 'trash', + [Food.Junkfood]: 'pizza-slice', + [Food.Meat]: 'hamburger', + [Food.Raw]: 'drumstick-bite', + [Food.Sugar]: 'candy-cane', + [Food.Toxic]: 'biohazard', + [Food.Vegetables]: 'carrot', }; const FOOD_NAMES: Record = { - [Food.Cloth]: "Clothing", - [Food.Dairy]: "Dairy", - [Food.Fried]: "Fried food", - [Food.Fruit]: "Fruit", - [Food.Grain]: "Grain", - [Food.Gross]: "Gross food", - [Food.Junkfood]: "Junk food", - [Food.Meat]: "Meat", - [Food.Raw]: "Raw", - [Food.Sugar]: "Sugar", - [Food.Toxic]: "Toxic food", - [Food.Vegetables]: "Vegetables", + [Food.Cloth]: 'Clothing', + [Food.Dairy]: 'Dairy', + [Food.Fried]: 'Fried food', + [Food.Fruit]: 'Fruit', + [Food.Grain]: 'Grain', + [Food.Gross]: 'Gross food', + [Food.Junkfood]: 'Junk food', + [Food.Meat]: 'Meat', + [Food.Raw]: 'Raw', + [Food.Sugar]: 'Sugar', + [Food.Toxic]: 'Toxic food', + [Food.Vegetables]: 'Vegetables', }; -const IGNORE_UNLESS_LIKED: Set = new Set([ - Food.Cloth, - Food.Gross, - Food.Toxic, -]); +const IGNORE_UNLESS_LIKED: Set = new Set([Food.Cloth, Food.Gross, Food.Toxic]); -const notIn = function (set: Set) { +const notIn = function (set: Set) { return (value: T) => { return !set.has(value); }; }; -const FoodList = (props: { - food: Food[], - icon: string, - name: string, - className: string, -}) => { +const FoodList = (props: { food: Food[]; icon: string; name: string; className: string }) => { if (props.food.length === 0) { return null; } @@ -65,50 +56,38 @@ const FoodList = (props: { position="bottom-end" content={ - {props.name} + {props.name} {props.food .reduce((names, food) => { const foodName = FOOD_NAMES[food]; return foodName ? names.concat(foodName) : names; - }, []).join(", ")} + }, []) + .join(', ')} }> - {props.food.map(food => { - return FOOD_ICONS[food] - && ( + {props.food.map((food) => { + return ( + FOOD_ICONS[food] && ( - + - ); + ) + ); })} ); }; -const Diet = (props: { - likedFood: Food[], - dislikedFood: Food[], - toxicFood: Food[], -}) => { +const Diet = (props: { likedFood: Food[]; dislikedFood: Food[]; toxicFood: Food[] }) => { return ( - + @@ -132,20 +111,19 @@ const Diet = (props: { ); }; -const SpeciesFeature = (props: { - className: string, - feature: Feature, -}) => { +const SpeciesFeature = (props: { className: string; feature: Feature }) => { const { className, feature } = props; return ( - - {feature.name} - - {feature.description} - - }> + + {feature.name} + + {feature.description} + + }> @@ -163,21 +141,17 @@ const SpeciesFeature = (props: { ); }; -const SpeciesFeatures = (props: { - features: Species["features"], -}) => { +const SpeciesFeatures = (props: { features: Species['features'] }) => { const { good, neutral, bad } = props.features; return ( - {good.map(feature => { + {good.map((feature) => { return ( - + ); })} @@ -185,24 +159,20 @@ const SpeciesFeatures = (props: { - {neutral.map(feature => { + {neutral.map((feature) => { return ( - + ); })} - {bad.map(feature => { + {bad.map((feature) => { return ( - + ); })} @@ -211,29 +181,30 @@ const SpeciesFeatures = (props: { ); }; -const SpeciesPageInner = (props: { - handleClose: () => void, - species: ServerData["species"], -}, context) => { +const SpeciesPageInner = ( + props: { + handleClose: () => void; + species: ServerData['species']; + }, + context +) => { const { act, data } = useBackend(context); - const setSpecies = createSetPreference(act, "species"); + const setSpecies = createSetPreference(act, 'species'); - let species: [string, Species & ServerSpeciesData][] - = Object.entries(props.species) - .map(([species, serverData]) => { - return [ - species, - { - ...serverData, - ...(requireSpecies.keys().indexOf(`./${species}`) === -1 - ? fallbackSpecies - : requireSpecies(`./${species}`).default) as Species, - }, - ]; - }); + let species: [string, Species & ServerSpeciesData][] = Object.entries(props.species).map(([species, serverData]) => { + return [ + species, + { + ...serverData, + ...((requireSpecies.keys().indexOf(`./${species}`) === -1 + ? fallbackSpecies + : requireSpecies(`./${species}`).default) as Species), + }, + ]; + }); // Humans are always the top of the list - const humanIndex = species.findIndex(([species]) => species === "human"); + const humanIndex = species.findIndex(([species]) => species === 'human'); const swapWith = species[0]; species[0] = species[humanIndex]; species[humanIndex] = swapWith; @@ -261,23 +232,14 @@ const SpeciesPageInner = (props: { ); })} @@ -289,18 +251,19 @@ const SpeciesPageInner = (props: { -

    ) - }> -
    - {currentSpecies.description} -
    +
    + ) + }> +
    {currentSpecies.description}
    @@ -309,10 +272,7 @@ const SpeciesPageInner = (props: { - + @@ -324,8 +284,12 @@ const SpeciesPageInner = (props: { {lore.map((text, index) => ( {text} - {index !== lore.length - 1 - && (<>

    )} + {index !== lore.length - 1 && ( + <> +
    +
    + + )}
    ))} @@ -340,17 +304,12 @@ const SpeciesPageInner = (props: { ); }; -export const SpeciesPage = (props: { - closeSpecies: () => void, -}) => { +export const SpeciesPage = (props: { closeSpecies: () => void }) => { return ( { + render={(serverData) => { if (serverData) { - return (); + return ; } else { return Loading species...; } diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx index 299636bebfb91..4dba7157f97aa 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx @@ -1,11 +1,11 @@ -import { Component, createRef, RefObject } from "inferno"; +import { Component, createRef, RefObject } from 'inferno'; import type { InfernoNode } from 'inferno'; -import { Button, Section, Stack } from "../../components"; -import { FlexProps } from "../../components/Flex"; +import { Button, Section, Stack } from '../../components'; +import { FlexProps } from '../../components/Flex'; type TabbedMenuProps = { - categoryEntries: [string, InfernoNode][], - contentProps?: FlexProps, + categoryEntries: [string, InfernoNode][]; + contentProps?: FlexProps; }; export class TabbedMenu extends Component { @@ -33,8 +33,7 @@ export class TabbedMenu extends Component { fontSize="1.2em" fluid onClick={() => { - const offsetTop = this.categoryRefs[category] - .current?.offsetTop; + const offsetTop = this.categoryRefs[category].current?.offsetTop; if (offsetTop === undefined) { return; @@ -47,8 +46,7 @@ export class TabbedMenu extends Component { } currentSection.scrollTop = offsetTop; - }} - > + }}> {category} @@ -67,26 +65,17 @@ export class TabbedMenu extends Component { // Otherwise, TypeScript complains about invalid prop className: undefined, - }} - > + }}> - {this.props.categoryEntries.map( - ([category, children]) => { - return ( - -
    - {children} -
    -
    - ); - } - )} + {this.props.categoryEntries.map(([category, children]) => { + return ( + +
    + {children} +
    +
    + ); + })}
    diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/abductor.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/abductor.ts index 9f0abe6da87af..58db32a372d45 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/abductor.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/abductor.ts @@ -1,9 +1,9 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; const Abductor: Antagonist = { - key: "abductor", - name: "Abductor", + key: 'abductor', + name: 'Abductor', description: [ multiline` Abductors are technologically advanced alien society set on cataloging diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/blob.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/blob.ts index 1d2de66ab4816..c562e4e5ee555 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/blob.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/blob.ts @@ -1,5 +1,5 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; export const BLOB_MECHANICAL_DESCRIPTION = multiline` The blob infests the station and destroys everything in its path, including @@ -9,11 +9,9 @@ export const BLOB_MECHANICAL_DESCRIPTION = multiline` `; const Blob: Antagonist = { - key: "blob", - name: "Blob", - description: [ - BLOB_MECHANICAL_DESCRIPTION, - ], + key: 'blob', + name: 'Blob', + description: [BLOB_MECHANICAL_DESCRIPTION], category: Category.Midround, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/blobinfection.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/blobinfection.ts index 45a8a03dc8445..3cddceb3c3dde 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/blobinfection.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/blobinfection.ts @@ -1,10 +1,10 @@ -import { Antagonist, Category } from "../base"; -import { BLOB_MECHANICAL_DESCRIPTION } from "./blob"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { BLOB_MECHANICAL_DESCRIPTION } from './blob'; +import { multiline } from 'common/string'; const BlobInfection: Antagonist = { - key: "blobinfection", - name: "Blob Infection", + key: 'blobinfection', + name: 'Blob Infection', description: [ multiline` At any point in the middle of the shift, be strucken with an infection diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/bloodbrother.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/bloodbrother.ts index 3a05cf1ec00ef..8d4d45f582f46 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/bloodbrother.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/bloodbrother.ts @@ -1,9 +1,9 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; const BloodBrother: Antagonist = { - key: "bloodbrother", - name: "Blood Brother", + key: 'bloodbrother', + name: 'Blood Brother', description: [ multiline` Team up with other crew members as blood brothers to combine the strengths diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/changeling.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/changeling.ts index 114e6413850e8..3368ff791760c 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/changeling.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/changeling.ts @@ -1,9 +1,9 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; const Changeling: Antagonist = { - key: "changeling", - name: "Changeling", + key: 'changeling', + name: 'Changeling', description: [ multiline` A highly intelligent alien predator that is capable of altering their diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/cultist.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/cultist.ts index ea27413786a9e..a3bb817214280 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/cultist.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/cultist.ts @@ -1,9 +1,9 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; const Cultist: Antagonist = { - key: "cultist", - name: "Cultist", + key: 'cultist', + name: 'Cultist', description: [ multiline` The Geometer of Blood, Nar-Sie, has sent a number of her followers to diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/familyheadaspirant.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/familyheadaspirant.ts index 6ed2e8dda5756..f648fa1b723a9 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/familyheadaspirant.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/familyheadaspirant.ts @@ -1,10 +1,10 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; -import { GANGSTER_MECHANICAL_DESCRIPTION } from "./gangster"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; +import { GANGSTER_MECHANICAL_DESCRIPTION } from './gangster'; const FamilyHeadAspirant: Antagonist = { - key: "familyheadaspirant", - name: "Family Head Aspirant", + key: 'familyheadaspirant', + name: 'Family Head Aspirant', description: [ multiline` A form of family leader that can activate at any point in the middle diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/gangster.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/gangster.ts index 38df184b26aad..84fb130c06df7 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/gangster.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/gangster.ts @@ -1,18 +1,15 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; -export const GANGSTER_MECHANICAL_DESCRIPTION - = multiline` +export const GANGSTER_MECHANICAL_DESCRIPTION = multiline` Convince people to join your family, wear your uniform, tag turf for the family, and accomplish your family's goals. `; const Gangster: Antagonist = { - key: "gangster", - name: "Family Leader", - description: [ - GANGSTER_MECHANICAL_DESCRIPTION, - ], + key: 'gangster', + name: 'Family Leader', + description: [GANGSTER_MECHANICAL_DESCRIPTION], category: Category.Roundstart, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/headrevolutionary.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/headrevolutionary.ts index ea670d854dfae..fd2081fe49e20 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/headrevolutionary.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/headrevolutionary.ts @@ -1,19 +1,15 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; -export const REVOLUTIONARY_MECHANICAL_DESCRIPTION - = multiline` +export const REVOLUTIONARY_MECHANICAL_DESCRIPTION = multiline` Armed with a flash, convert as many people to the revolution as you can. Kill or exile all heads of staff on the station. `; const HeadRevolutionary: Antagonist = { - key: "headrevolutionary", - name: "Head Revolutionary", - description: [ - "VIVA LA REVOLUTION!", - REVOLUTIONARY_MECHANICAL_DESCRIPTION, - ], + key: 'headrevolutionary', + name: 'Head Revolutionary', + description: ['VIVA LA REVOLUTION!', REVOLUTIONARY_MECHANICAL_DESCRIPTION], category: Category.Roundstart, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/heretic.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/heretic.ts index 611ad714eda84..b8daffc0b64db 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/heretic.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/heretic.ts @@ -1,15 +1,14 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; -export const HERETIC_MECHANICAL_DESCRIPTION - = multiline` +export const HERETIC_MECHANICAL_DESCRIPTION = multiline` Find hidden influences and sacrifice crew members to gain magical powers and ascend as one of several paths. `; const Heretic: Antagonist = { - key: "heretic", - name: "Heretic", + key: 'heretic', + name: 'Heretic', description: [ multiline` Forgotten, devoured, gutted. Humanity has forgotten the eldritch forces diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/hereticsmuggler.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/hereticsmuggler.ts index 8667e0b199911..be5273740c1ab 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/hereticsmuggler.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/hereticsmuggler.ts @@ -1,13 +1,10 @@ -import { Antagonist, Category } from "../base"; -import { HERETIC_MECHANICAL_DESCRIPTION } from "./heretic"; +import { Antagonist, Category } from '../base'; +import { HERETIC_MECHANICAL_DESCRIPTION } from './heretic'; const HereticSmuggler: Antagonist = { - key: "hereticsmuggler", - name: "Heretic Smuggler", - description: [ - "A form of heretic that can activate when joining an ongoing shift.", - HERETIC_MECHANICAL_DESCRIPTION, - ], + key: 'hereticsmuggler', + name: 'Heretic Smuggler', + description: ['A form of heretic that can activate when joining an ongoing shift.', HERETIC_MECHANICAL_DESCRIPTION], category: Category.Latejoin, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/loneoperative.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/loneoperative.ts index b0de7f13b3e11..c74b2ca01b426 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/loneoperative.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/loneoperative.ts @@ -1,10 +1,10 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; -import { OPERATIVE_MECHANICAL_DESCRIPTION } from "./operative"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; +import { OPERATIVE_MECHANICAL_DESCRIPTION } from './operative'; const LoneOperative: Antagonist = { - key: "loneoperative", - name: "Lone Operative", + key: 'loneoperative', + name: 'Lone Operative', description: [ multiline` A solo nuclear operative that has a higher chance of spawning the longer diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/malfai.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/malfai.ts index ed6b5d7f92300..1aa9e0034bbde 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/malfai.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/malfai.ts @@ -1,19 +1,16 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; -export const MALF_AI_MECHANICAL_DESCRIPTION - = multiline` +export const MALF_AI_MECHANICAL_DESCRIPTION = multiline` With a law zero to complete your objectives at all costs, combine your omnipotence and malfunction modules to wreak havoc across the station. Go delta to destroy the station and all those who opposed you. `; const MalfAI: Antagonist = { - key: "malfai", - name: "Malfunctioning AI", - description: [ - MALF_AI_MECHANICAL_DESCRIPTION, - ], + key: 'malfai', + name: 'Malfunctioning AI', + description: [MALF_AI_MECHANICAL_DESCRIPTION], category: Category.Roundstart, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/malfaimidround.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/malfaimidround.ts index 1334e48379b00..d686a78af498d 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/malfaimidround.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/malfaimidround.ts @@ -1,10 +1,10 @@ -import { multiline } from "common/string"; -import { Antagonist, Category } from "../base"; -import { MALF_AI_MECHANICAL_DESCRIPTION } from "./malfai"; +import { multiline } from 'common/string'; +import { Antagonist, Category } from '../base'; +import { MALF_AI_MECHANICAL_DESCRIPTION } from './malfai'; const MalfAIMidround: Antagonist = { - key: "malfaimidround", - name: "Value Drifted AI", + key: 'malfaimidround', + name: 'Value Drifted AI', description: [ multiline` A form of malfunctioning AI that is given to existing AIs in the middle diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/monkey.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/monkey.ts index 224c1d45516ad..35098fa1f51ee 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/monkey.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/monkey.ts @@ -1,9 +1,9 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; const Monkey: Antagonist = { - key: "monkey", - name: "Monkey", + key: 'monkey', + name: 'Monkey', description: [ multiline` When the round starts, become infected with Jungle Fever, a disease which diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/obsessed.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/obsessed.ts index 4caf77f29d6e9..fcb18419a81d0 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/obsessed.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/obsessed.ts @@ -1,9 +1,9 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; const Obsessed: Antagonist = { - key: "obsessed", - name: "Obsessed", + key: 'obsessed', + name: 'Obsessed', description: [ multiline` You're obsessed with someone! Your obsession may begin to notice their diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/operative.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/operative.ts index 582c74f003fbf..a8b14e88fce80 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/operative.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/operative.ts @@ -1,5 +1,5 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; export const OPERATIVE_MECHANICAL_DESCRIPTION = multiline` Retrieve the nuclear authentication disk, use it to activate the nuclear @@ -7,8 +7,8 @@ export const OPERATIVE_MECHANICAL_DESCRIPTION = multiline` `; const Operative: Antagonist = { - key: "operative", - name: "Nuclear Operative", + key: 'operative', + name: 'Nuclear Operative', description: [ multiline` Congratulations, agent. You have been chosen to join the Syndicate diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/operativemidround.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/operativemidround.ts index ae23adc8151de..cd4e0e948588c 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/operativemidround.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/operativemidround.ts @@ -1,10 +1,10 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; -import { OPERATIVE_MECHANICAL_DESCRIPTION } from "./operative"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; +import { OPERATIVE_MECHANICAL_DESCRIPTION } from './operative'; const OperativeMidround: Antagonist = { - key: "operativemidround", - name: "Nuclear Assailant", + key: 'operativemidround', + name: 'Nuclear Assailant', description: [ multiline` A form of nuclear operative that is offered to ghosts in the middle diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/provocateur.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/provocateur.ts index a55ab96435d33..b8b847da11de7 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/provocateur.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/provocateur.ts @@ -1,10 +1,10 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; -import { REVOLUTIONARY_MECHANICAL_DESCRIPTION } from "./headrevolutionary"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; +import { REVOLUTIONARY_MECHANICAL_DESCRIPTION } from './headrevolutionary'; const Provocateur: Antagonist = { - key: "provocateur", - name: "Provocateur", + key: 'provocateur', + name: 'Provocateur', description: [ multiline` A form of head revolutionary that can activate when joining an ongoing diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/revenant.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/revenant.ts index f351dac602a73..a23b4e178c87c 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/revenant.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/revenant.ts @@ -1,9 +1,9 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; const Revenant: Antagonist = { - key: "revenant", - name: "Revenant", + key: 'revenant', + name: 'Revenant', description: [ multiline` Become the mysterious revenant. Break windows, overload lights, and eat diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/sentientdisease.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/sentientdisease.ts index 4691fda264e4a..fabe1d34e5f57 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/sentientdisease.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/sentientdisease.ts @@ -1,9 +1,9 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; const SentientDisease: Antagonist = { - key: "sentientdisease", - name: "Sentient Disease", + key: 'sentientdisease', + name: 'Sentient Disease', description: [ multiline` Mutate and spread yourself and infect as much of the crew as possible diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/spacedragon.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/spacedragon.ts index f4bebfcf50e65..e3e8940dda91f 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/spacedragon.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/spacedragon.ts @@ -1,9 +1,9 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; const SpaceDragon: Antagonist = { - key: "spacedragon", - name: "Space Dragon", + key: 'spacedragon', + name: 'Space Dragon', description: [ multiline` Become a ferocious space dragon. Breathe fire, summon an army of space diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/spaceninja.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/spaceninja.ts index 971fe81606195..18ce064b37384 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/spaceninja.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/spaceninja.ts @@ -1,9 +1,9 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; const SpaceNinja: Antagonist = { - key: "spaceninja", - name: "Space Ninja", + key: 'spaceninja', + name: 'Space Ninja', description: [ multiline` The Spider Clan practice a sort of augmentation of human flesh in order to diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/swarmer.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/swarmer.ts index 9b6696e261b58..d4e5354cd25a9 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/swarmer.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/swarmer.ts @@ -1,9 +1,9 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; const Swarmer: Antagonist = { - key: "swarmer", - name: "Swarmer", + key: 'swarmer', + name: 'Swarmer', description: [ multiline` A swarmer is a small robot that replicates itself autonomously with diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/syndicateinfiltrator.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/syndicateinfiltrator.ts index 2bcafe666f8e9..97d98b50648ba 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/syndicateinfiltrator.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/syndicateinfiltrator.ts @@ -1,13 +1,10 @@ -import { Antagonist, Category } from "../base"; -import { TRAITOR_MECHANICAL_DESCRIPTION } from "./traitor"; +import { Antagonist, Category } from '../base'; +import { TRAITOR_MECHANICAL_DESCRIPTION } from './traitor'; const SyndicateInfiltrator: Antagonist = { - key: "syndicateinfiltrator", - name: "Syndicate Infiltrator", - description: [ - "A form of traitor that can activate when joining an ongoing shift.", - TRAITOR_MECHANICAL_DESCRIPTION, - ], + key: 'syndicateinfiltrator', + name: 'Syndicate Infiltrator', + description: ['A form of traitor that can activate when joining an ongoing shift.', TRAITOR_MECHANICAL_DESCRIPTION], category: Category.Latejoin, priority: -1, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/syndicatesleeperagent.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/syndicatesleeperagent.ts index bc3fb9d44f14e..a692736c408e5 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/syndicatesleeperagent.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/syndicatesleeperagent.ts @@ -1,10 +1,10 @@ -import { Antagonist, Category } from "../base"; -import { TRAITOR_MECHANICAL_DESCRIPTION } from "./traitor"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { TRAITOR_MECHANICAL_DESCRIPTION } from './traitor'; +import { multiline } from 'common/string'; const SyndicateSleeperAgent: Antagonist = { - key: "syndicatesleeperagent", - name: "Syndicate Sleeper Agent", + key: 'syndicatesleeperagent', + name: 'Syndicate Sleeper Agent', description: [ multiline` A form of traitor that can activate at any point in the middle diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/traitor.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/traitor.ts index 9f11609f29ffb..df5127acf51fe 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/traitor.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/traitor.ts @@ -1,15 +1,14 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; -export const TRAITOR_MECHANICAL_DESCRIPTION - = multiline` +export const TRAITOR_MECHANICAL_DESCRIPTION = multiline` Start with a set of sinister objectives and an uplink to purchase items to get the job done. `; const Traitor: Antagonist = { - key: "traitor", - name: "Traitor", + key: 'traitor', + name: 'Traitor', description: [ multiline` An unpaid debt. A score to be settled. Maybe you were just in the wrong diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/wizard.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/wizard.ts index 0455235cb5865..09a88945670b0 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/wizard.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/wizard.ts @@ -1,19 +1,15 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; -export const WIZARD_MECHANICAL_DESCRIPTION - = multiline` +export const WIZARD_MECHANICAL_DESCRIPTION = multiline` Choose between a variety of powerful spells in order to cause chaos among Space Station 13. `; const Wizard: Antagonist = { - key: "wizard", - name: "Wizard", - description: [ - `"GREETINGS. WE'RE THE WIZARDS OF THE WIZARD'S FEDERATION."`, - WIZARD_MECHANICAL_DESCRIPTION, - ], + key: 'wizard', + name: 'Wizard', + description: [`"GREETINGS. WE'RE THE WIZARDS OF THE WIZARD'S FEDERATION."`, WIZARD_MECHANICAL_DESCRIPTION], category: Category.Roundstart, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/wizardmidround.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/wizardmidround.ts index 3a1f88e8f7acb..b91ea16ccb632 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/wizardmidround.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/wizardmidround.ts @@ -1,13 +1,10 @@ -import { Antagonist, Category } from "../base"; -import { WIZARD_MECHANICAL_DESCRIPTION } from "./wizard"; +import { Antagonist, Category } from '../base'; +import { WIZARD_MECHANICAL_DESCRIPTION } from './wizard'; const WizardMidround: Antagonist = { - key: "wizardmidround", - name: "Wizard (Midround)", - description: [ - "A form of wizard that is offered to ghosts in the middle of the shift.", - WIZARD_MECHANICAL_DESCRIPTION, - ], + key: 'wizardmidround', + name: 'Wizard (Midround)', + description: ['A form of wizard that is offered to ghosts in the middle of the shift.', WIZARD_MECHANICAL_DESCRIPTION], category: Category.Midround, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/xenomorph.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/xenomorph.ts index 82e75ce7d8e5e..6bea64450e93a 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/xenomorph.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/xenomorph.ts @@ -1,9 +1,9 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; +import { Antagonist, Category } from '../base'; +import { multiline } from 'common/string'; const Xenomorph: Antagonist = { - key: "xenomorph", - name: "Xenomorph", + key: 'xenomorph', + name: 'Xenomorph', description: [ multiline` Become the extraterrestrial xenomorph. Start as a larva, and progress diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts index 4d381032fe58d..f8f1e68d6b098 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts @@ -105,13 +105,12 @@ export enum GamePreferencesSelectedPage { Keybindings, } -export const createSetPreference = - (act: typeof sendAct, preference: string) => (value: unknown) => { - act('set_preference', { - preference, - value, - }); - }; +export const createSetPreference = (act: typeof sendAct, preference: string) => (value: unknown) => { + act('set_preference', { + preference, + value, + }); +}; export enum Window { Character = 0, diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/index.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/index.tsx index f7b3f87c8a0ad..197a940d3a140 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/index.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/index.tsx @@ -1,8 +1,8 @@ -import { exhaustiveCheck } from "common/exhaustive"; -import { useBackend } from "../../backend"; -import { GamePreferencesSelectedPage, PreferencesMenuData, Window } from "./data"; -import { CharacterPreferenceWindow } from "./CharacterPreferenceWindow"; -import { GamePreferenceWindow } from "./GamePreferenceWindow"; +import { exhaustiveCheck } from 'common/exhaustive'; +import { useBackend } from '../../backend'; +import { GamePreferencesSelectedPage, PreferencesMenuData, Window } from './data'; +import { CharacterPreferenceWindow } from './CharacterPreferenceWindow'; +import { GamePreferenceWindow } from './GamePreferenceWindow'; export const PreferencesMenu = (props, context) => { const { data } = useBackend(context); @@ -15,9 +15,7 @@ export const PreferencesMenu = (props, context) => { case Window.Game: return ; case Window.Keybindings: - return (); + return ; default: exhaustiveCheck(window); } diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx index 8f798394491fa..a08386d805223 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx @@ -1,77 +1,68 @@ -import { binaryInsertWith, sortBy } from "common/collections"; -import { useLocalState } from "../../backend"; -import { Box, Button, FitText, Icon, Input, LabeledList, Modal, Section, Stack, TrackOutsideClicks } from "../../components"; -import { Name } from "./data"; -import { ServerPreferencesFetcher } from "./ServerPreferencesFetcher"; +import { binaryInsertWith, sortBy } from 'common/collections'; +import { useLocalState } from '../../backend'; +import { Box, Button, FitText, Icon, Input, LabeledList, Modal, Section, Stack, TrackOutsideClicks } from '../../components'; +import { Name } from './data'; +import { ServerPreferencesFetcher } from './ServerPreferencesFetcher'; type NameWithKey = { - key: string, - name: Name, + key: string; + name: Name; }; const binaryInsertName = binaryInsertWith(({ key }) => key); -const sortNameWithKeyEntries = sortBy<[string, NameWithKey[]]>( - ([key]) => key, -); +const sortNameWithKeyEntries = sortBy<[string, NameWithKey[]]>(([key]) => key); -export const MultiNameInput = (props: { - handleClose: () => void, - handleRandomizeName: (nameType: string) => void, - handleUpdateName: (nameType: string, value: string) => void, - names: Record, -}, context) => { - const [currentlyEditingName, setCurrentlyEditingName] - = useLocalState(context, "currentlyEditingName", null); +export const MultiNameInput = ( + props: { + handleClose: () => void; + handleRandomizeName: (nameType: string) => void; + handleUpdateName: (nameType: string, value: string) => void; + names: Record; + }, + context +) => { + const [currentlyEditingName, setCurrentlyEditingName] = useLocalState(context, 'currentlyEditingName', null); return ( - { - if (!data) { - return null; - } + { + if (!data) { + return null; + } - const namesIntoGroups: Record = {}; + const namesIntoGroups: Record = {}; - for (const [key, name] of Object.entries(data.names.types)) { - namesIntoGroups[name.group] = binaryInsertName( - namesIntoGroups[name.group] || [], - { + for (const [key, name] of Object.entries(data.names.types)) { + namesIntoGroups[name.group] = binaryInsertName(namesIntoGroups[name.group] || [], { key, name, - } - ); - } - - return ( - - -
    - Close - - )} - title="Alternate names" - > - - {sortNameWithKeyEntries(Object.entries(namesIntoGroups)) - .map(([_, names], index, collection) => ( + }); + } + + return ( + + +
    + Close + + } + title="Alternate names"> + + {sortNameWithKeyEntries(Object.entries(namesIntoGroups)).map(([_, names], index, collection) => ( <> {names.map(({ key, name }) => { let content; if (currentlyEditingName === key) { const updateName = (event, value) => { - props.handleUpdateName( - key, - value, - ); + props.handleUpdateName(key, value); setCurrentlyEditingName(null); }; @@ -95,8 +86,7 @@ export const MultiNameInput = (props: { setCurrentlyEditingName(key); event.cancelBubble = true; event.stopPropagation(); - }} - > + }}> {props.names[key]} @@ -105,14 +95,9 @@ export const MultiNameInput = (props: { } return ( - + - - {content} - + {content} {!!name.can_randomize && ( @@ -131,26 +116,28 @@ export const MultiNameInput = (props: { ); })} - {(index !== collection.length - 1) - && } + {index !== collection.length - 1 && } ))} - -
    -
    -
    - ); - }} /> +
    +
    +
    +
    + ); + }} + /> ); }; -export const NameInput = (props: { - handleUpdateName: (name: string) => void, - name: string, - openMultiNameInput: () => void, -}, context) => { - const [lastNameBeforeEdit, setLastNameBeforeEdit] - = useLocalState(context, "lastNameBeforeEdit", null); +export const NameInput = ( + props: { + handleUpdateName: (name: string) => void; + name: string; + openMultiNameInput: () => void; + }, + context +) => { + const [lastNameBeforeEdit, setLastNameBeforeEdit] = useLocalState(context, 'lastNameBeforeEdit', null); const editing = lastNameBeforeEdit === props.name; const updateName = (e, value) => { @@ -159,19 +146,27 @@ export const NameInput = (props: { }; return ( - - - ) : null} /> + + data ? ( + + + + ) : null + } + /> ); }; - - diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx index 8b0bcd64a129e..cfe217495f0d9 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx @@ -1,26 +1,18 @@ -import { sortBy, sortStrings } from "common/collections"; -import { BooleanLike, classes } from "common/react"; -import { createComponentVNode } from "inferno"; -import type { InfernoNode, ComponentType } from "inferno"; -import { VNodeFlags } from "inferno-vnode-flags"; -import { sendAct, useBackend, useLocalState } from "../../../../backend"; -import { Box, Button, Dropdown, Input, NumberInput, Stack } from "../../../../components"; -import { createSetPreference, PreferencesMenuData } from "../../data"; -import { ServerPreferencesFetcher } from "../../ServerPreferencesFetcher"; +import { sortBy, sortStrings } from 'common/collections'; +import { BooleanLike, classes } from 'common/react'; +import { createComponentVNode } from 'inferno'; +import type { InfernoNode, ComponentType } from 'inferno'; +import { VNodeFlags } from 'inferno-vnode-flags'; +import { sendAct, useBackend, useLocalState } from '../../../../backend'; +import { Box, Button, Dropdown, Input, NumberInput, Stack } from '../../../../components'; +import { createSetPreference, PreferencesMenuData } from '../../data'; +import { ServerPreferencesFetcher } from '../../ServerPreferencesFetcher'; export const sortChoices = sortBy<[string, InfernoNode]>(([name]) => name); -export type Feature< - TReceiving, - TSending = TReceiving, - TServerData = unknown, -> = { +export type Feature = { name: string; - component: FeatureValue< - TReceiving, - TSending, - TServerData - >; + component: FeatureValue; category?: string; description?: string; }; @@ -31,58 +23,46 @@ export type Feature< * TSending = The type you will be sending * TServerData = The data the server sends through preferences.json */ -type FeatureValue< - TReceiving, - TSending = TReceiving, - TServerData = unknown, -> - = ComponentType>; - -export type FeatureValueProps< - TReceiving, - TSending = TReceiving, - TServerData = undefined, -> = { - act: typeof sendAct, - featureId: string, - handleSetValue: (newValue: TSending) => void, - serverData: TServerData | undefined, - shrink?: boolean, - value: TReceiving, +type FeatureValue = ComponentType< + FeatureValueProps +>; + +export type FeatureValueProps = { + act: typeof sendAct; + featureId: string; + handleSetValue: (newValue: TSending) => void; + serverData: TServerData | undefined; + shrink?: boolean; + value: TReceiving; }; export const FeatureColorInput = (props: FeatureValueProps) => { return ( - ); @@ -91,57 +71,52 @@ export const FeatureColorInput = (props: FeatureValueProps) => { export type FeatureToggle = Feature; export const TextInput = (props: FeatureValueProps) => { + return props.handleSetValue(newValue)} width="100%" />; +}; + +export const CheckboxInput = (props: FeatureValueProps) => { return ( - props.handleSetValue(newValue)} - width="100%" + { + props.handleSetValue(!props.value); + }} /> ); }; -export const CheckboxInput = ( - props: FeatureValueProps -) => { - return ( { - props.handleSetValue(!props.value); - }} - />); -}; - -export const CheckboxInputInverse = ( - props: FeatureValueProps -) => { - return ( { - props.handleSetValue(!props.value); - }} - />); +export const CheckboxInputInverse = (props: FeatureValueProps) => { + return ( + { + props.handleSetValue(!props.value); + }} + /> + ); }; export const createDropdownInput = ( // Map of value to display texts choices: Record, - dropdownProps?: Record, + dropdownProps?: Record ): FeatureValue => { return (props: FeatureValueProps) => { - return ( { + return ( + { return { displayText: label, value: dataValue, }; })} - {...dropdownProps} - />); + {...dropdownProps} + /> + ); }; }; @@ -153,77 +128,72 @@ export type FeatureChoicedServerData = { export type FeatureChoiced = Feature; -const capitalizeFirstLetter = (text: string) => ( - text.toString().charAt(0).toUpperCase() + text.toString().slice(1) -); +const capitalizeFirstLetter = (text: string) => + text + .toString() + .charAt(0) + .toUpperCase() + text.toString().slice(1); export const StandardizedDropdown = (props: { - choices: string[], - disabled?: boolean, - displayNames: Record, - onSetValue: (newValue: string) => void, - value: string, + choices: string[]; + disabled?: boolean; + displayNames: Record; + onSetValue: (newValue: string) => void; + value: string; }) => { - const { - choices, - disabled, - displayNames, - onSetValue, - value, - } = props; - - return ( { - return { - displayText: displayNames[choice], - value: choice, - }; - }) - } - />); + const { choices, disabled, displayNames, onSetValue, value } = props; + + return ( + { + return { + displayText: displayNames[choice], + value: choice, + }; + })} + /> + ); }; export const FeatureDropdownInput = ( props: FeatureValueProps & { - disabled?: boolean, - }, + disabled?: boolean; + } ) => { const serverData = props.serverData; if (!serverData) { return null; } - const displayNames = serverData.display_names - || Object.fromEntries( - serverData.choices.map(choice => [choice, capitalizeFirstLetter(choice)]) - ); + const displayNames = + serverData.display_names || Object.fromEntries(serverData.choices.map((choice) => [choice, capitalizeFirstLetter(choice)])); - return (); + return ( + + ); }; -export type FeatureWithIcons = Feature< - { value: T }, - T, - FeatureChoicedServerData ->; +export type FeatureWithIcons = Feature<{ value: T }, T, FeatureChoicedServerData>; export const FeatureIconnedDropdownInput = ( - props: FeatureValueProps<{ - value: string, - }, string, FeatureChoicedServerData>, + props: FeatureValueProps< + { + value: string; + }, + string, + FeatureChoicedServerData + > ) => { const serverData = props.serverData; if (!serverData) { @@ -232,10 +202,8 @@ export const FeatureIconnedDropdownInput = ( const icons = serverData.icons; - const textNames = serverData.display_names - || Object.fromEntries( - serverData.choices.map(choice => [choice, capitalizeFirstLetter(choice)]) - ); + const textNames = + serverData.display_names || Object.fromEntries(serverData.choices.map((choice) => [choice, capitalizeFirstLetter(choice)])); const displayNames = Object.fromEntries( Object.entries(textNames).map(([choice, textName]) => { @@ -246,17 +214,15 @@ export const FeatureIconnedDropdownInput = ( element = ( - + - - {element} - + {element} ); } @@ -265,49 +231,53 @@ export const FeatureIconnedDropdownInput = ( }) ); - return (); + return ( + + ); }; - export type FeatureNumericData = { - minimum: number, - maximum: number, - step: number, -} + minimum: number; + maximum: number; + step: number; +}; export type FeatureNumeric = Feature; -export const FeatureNumberInput = ( - props: FeatureValueProps -) => { +export const FeatureNumberInput = (props: FeatureValueProps) => { if (!props.serverData) { return Loading...; } - return ( { - props.handleSetValue(value); - }} - minValue={props.serverData.minimum} - maxValue={props.serverData.maximum} - step={props.serverData.step} - value={props.value} - />); + return ( + { + props.handleSetValue(value); + }} + minValue={props.serverData.minimum} + maxValue={props.serverData.maximum} + step={props.serverData.step} + value={props.value} + /> + ); }; -export const FeatureValueInput = (props: { - feature: Feature, - featureId: string, - shrink?: boolean, - value: unknown, +export const FeatureValueInput = ( + props: { + feature: Feature; + featureId: string; + shrink?: boolean; + value: unknown; - act: typeof sendAct, -}, context) => { + act: typeof sendAct; + }, + context +) => { const { data } = useBackend(context); const feature = props.feature; @@ -315,7 +285,7 @@ export const FeatureValueInput = (props: { const [predictedValue, setPredictedValue] = useLocalState( context, `${props.featureId}_predictedValue_${data.active_slot}`, - props.value, + props.value ); const changeValue = (newValue: unknown) => { @@ -325,19 +295,16 @@ export const FeatureValueInput = (props: { return ( { - return createComponentVNode( - VNodeFlags.ComponentUnknown, - feature.component, - { - act: props.act, - featureId: props.featureId, - serverData: serverData && serverData[props.featureId], - shrink: props.shrink, - - handleSetValue: changeValue, - value: predictedValue, - }); + render={(serverData) => { + return createComponentVNode(VNodeFlags.ComponentUnknown, feature.component, { + act: props.act, + featureId: props.featureId, + serverData: serverData && serverData[props.featureId], + shrink: props.shrink, + + handleSetValue: changeValue, + value: predictedValue, + }); }} /> ); diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/age.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/age.tsx index 41889a1fc685b..3b0dc68158f28 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/age.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/age.tsx @@ -1,6 +1,6 @@ -import { Feature, FeatureNumberInput } from "../base"; +import { Feature, FeatureNumberInput } from '../base'; export const age: Feature = { - name: "Age", + name: 'Age', component: FeatureNumberInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/ai_core_display.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/ai_core_display.tsx index b67e62dbe8d50..6b3e10bb10cc0 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/ai_core_display.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/ai_core_display.tsx @@ -1,6 +1,6 @@ -import { FeatureIconnedDropdownInput, FeatureWithIcons } from "../base"; +import { FeatureIconnedDropdownInput, FeatureWithIcons } from '../base'; export const preferred_ai_core_display: FeatureWithIcons = { - name: "AI core display", + name: 'AI core display', component: FeatureIconnedDropdownInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/body_type.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/body_type.tsx index 9ae25fa1486f7..de9d6523e2c42 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/body_type.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/body_type.tsx @@ -1,6 +1,6 @@ -import { FeatureChoiced, FeatureDropdownInput } from "../base"; +import { FeatureChoiced, FeatureDropdownInput } from '../base'; export const body_type: FeatureChoiced = { - name: "Body type", + name: 'Body type', component: FeatureDropdownInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/pda.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/pda.tsx index e11c87313babe..537bc672da16d 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/pda.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/pda.tsx @@ -1,15 +1,15 @@ -import { Feature, FeatureColorInput, FeatureDropdownInput } from "../base"; +import { Feature, FeatureColorInput, FeatureDropdownInput } from '../base'; export const pda_color: Feature = { - name: "PDA color", - category: "GAMEPLAY", - description: "The background color of your PDA when using the Thinktronic Classic theme.", + name: 'PDA color', + category: 'GAMEPLAY', + description: 'The background color of your PDA when using the Thinktronic Classic theme.', component: FeatureColorInput, }; export const pda_theme: Feature = { - name: "PDA theme", - category: "GAMEPLAY", - description: "The theme of your equipped PDA. Changes the NtOS look in Settings.", + name: 'PDA theme', + category: 'GAMEPLAY', + description: 'The theme of your equipped PDA. Changes the NtOS look in Settings.', component: FeatureDropdownInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/phobia.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/phobia.tsx index 04d91daea06d9..dd14349277fec 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/phobia.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/phobia.tsx @@ -1,6 +1,6 @@ -import { FeatureChoiced, FeatureDropdownInput } from "../base"; +import { FeatureChoiced, FeatureDropdownInput } from '../base'; export const phobia: FeatureChoiced = { - name: "Phobia", + name: 'Phobia', component: FeatureDropdownInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/security_department.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/security_department.tsx index 118f2cf467859..e9f380bd67562 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/security_department.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/security_department.tsx @@ -1,6 +1,6 @@ -import { FeatureChoiced, FeatureDropdownInput } from "../base"; +import { FeatureChoiced, FeatureDropdownInput } from '../base'; export const prefered_security_department: FeatureChoiced = { - name: "Security department", + name: 'Security department', component: FeatureDropdownInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skin_tone.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skin_tone.tsx index 5bd5096367c02..3f6cd06344f67 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skin_tone.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skin_tone.tsx @@ -1,28 +1,23 @@ -import { sortBy } from "common/collections"; -import { Box, Stack } from "../../../../../components"; -import { Feature, FeatureChoicedServerData, FeatureValueProps, StandardizedDropdown } from "../base"; +import { sortBy } from 'common/collections'; +import { Box, Stack } from '../../../../../components'; +import { Feature, FeatureChoicedServerData, FeatureValueProps, StandardizedDropdown } from '../base'; type HexValue = { - lightness: number, - value: string, + lightness: number; + value: string; }; type SkinToneServerData = FeatureChoicedServerData & { - display_names: NonNullable, - to_hex: Record, + display_names: NonNullable; + to_hex: Record; }; -const sortHexValues - = sortBy<[string, HexValue]>(([_, hexValue]) => -hexValue.lightness); +const sortHexValues = sortBy<[string, HexValue]>(([_, hexValue]) => -hexValue.lightness); export const skin_tone: Feature = { - name: "Skin tone", + name: 'Skin tone', component: (props: FeatureValueProps) => { - const { - handleSetValue, - serverData, - value, - } = props; + const { handleSetValue, serverData, value } = props; if (!serverData) { return null; @@ -30,30 +25,29 @@ export const skin_tone: Feature = { return ( key)} + choices={sortHexValues(Object.entries(serverData.to_hex)).map(([key]) => key)} displayNames={Object.fromEntries( - Object.entries(serverData.display_names) - .map(([key, displayName]) => { - const hexColor = serverData.to_hex[key]; - - return [key, ( - - - { + const hexColor = serverData.to_hex[key]; + + return [ + key, + + + - - - - {displayName} - - - )]; - }) + 'box-sizing': 'content-box', + 'height': '11px', + 'width': '11px', + }} + /> + + + {displayName} + , + ]; + }) )} onSetValue={handleSetValue} value={value} diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/uplink_loc.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/uplink_loc.tsx index 459892597089c..06c125c33fc30 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/uplink_loc.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/uplink_loc.tsx @@ -1,6 +1,6 @@ -import { FeatureChoiced, FeatureDropdownInput } from "../base"; +import { FeatureChoiced, FeatureDropdownInput } from '../base'; export const uplink_loc: FeatureChoiced = { - name: "Uplink Spawn Location", + name: 'Uplink Spawn Location', component: FeatureDropdownInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ambient_occlusion.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ambient_occlusion.tsx index 4884c07377fb6..8886a5da9db95 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ambient_occlusion.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ambient_occlusion.tsx @@ -1,8 +1,8 @@ -import { CheckboxInput, FeatureToggle } from "../base"; +import { CheckboxInput, FeatureToggle } from '../base'; export const ambientocclusion: FeatureToggle = { - name: "Enable ambient occlusion", - category: "GAMEPLAY", - description: "Enable ambient occlusion, light shadows around characters.", + name: 'Enable ambient occlusion', + category: 'GAMEPLAY', + description: 'Enable ambient occlusion, light shadows around characters.', component: CheckboxInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/auto_fit_viewport.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/auto_fit_viewport.tsx index 5339de43d15ae..31609990f18c6 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/auto_fit_viewport.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/auto_fit_viewport.tsx @@ -1,7 +1,7 @@ -import { CheckboxInput, FeatureToggle } from "../base"; +import { CheckboxInput, FeatureToggle } from '../base'; export const auto_fit_viewport: FeatureToggle = { - name: "Auto fit viewport", - category: "UI", + name: 'Auto fit viewport', + category: 'UI', component: CheckboxInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/broadcast_login_logout.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/broadcast_login_logout.tsx index a997b265f64a2..f8252412fc994 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/broadcast_login_logout.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/broadcast_login_logout.tsx @@ -1,9 +1,9 @@ -import { multiline } from "common/string"; -import { CheckboxInput, FeatureToggle } from "../base"; +import { multiline } from 'common/string'; +import { CheckboxInput, FeatureToggle } from '../base'; export const broadcast_login_logout: FeatureToggle = { - name: "Broadcast login/logout", - category: "GAMEPLAY", + name: 'Broadcast login/logout', + category: 'GAMEPLAY', description: multiline` When enabled, disconnecting and reconnecting will announce to deadchat. `, diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/buttons_locked.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/buttons_locked.tsx index 67849342094d0..d82bc89306ea2 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/buttons_locked.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/buttons_locked.tsx @@ -1,8 +1,8 @@ -import { CheckboxInput, FeatureToggle } from "../base"; +import { CheckboxInput, FeatureToggle } from '../base'; export const buttons_locked: FeatureToggle = { - name: "Lock action buttons", - category: "GAMEPLAY", - description: "When enabled, action buttons will be locked in place.", + name: 'Lock action buttons', + category: 'GAMEPLAY', + description: 'When enabled, action buttons will be locked in place.', component: CheckboxInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/darkened_flash.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/darkened_flash.tsx index 706b178770599..0703c7ef04c08 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/darkened_flash.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/darkened_flash.tsx @@ -1,9 +1,9 @@ -import { multiline } from "common/string"; -import { CheckboxInput, FeatureToggle } from "../base"; +import { multiline } from 'common/string'; +import { CheckboxInput, FeatureToggle } from '../base'; export const darkened_flash: FeatureToggle = { - name: "Enable darkened flashes", - category: "GAMEPLAY", + name: 'Enable darkened flashes', + category: 'GAMEPLAY', description: multiline` When toggled, being flashed will show a dark screen rather than a bright one. diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/fps.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/fps.tsx index 55c091a9714fa..c812f0e3616fb 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/fps.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/fps.tsx @@ -1,13 +1,11 @@ -import { Dropdown, NumberInput, Stack } from "../../../../../components"; -import { Feature, FeatureNumericData, FeatureValueProps } from "../base"; +import { Dropdown, NumberInput, Stack } from '../../../../../components'; +import { Feature, FeatureNumericData, FeatureValueProps } from '../base'; type FpsServerData = FeatureNumericData & { recommended_fps: number; -} +}; -const FpsInput = ( - props: FeatureValueProps -) => { +const FpsInput = (props: FeatureValueProps) => { const { handleSetValue, serverData } = props; let recommened = `Recommended`; @@ -19,7 +17,7 @@ const FpsInput = ( { if (value === recommened) { handleSetValue(-1); @@ -28,29 +26,28 @@ const FpsInput = ( } }} width="100%" - options={[ - recommened, - "Custom", - ]} + options={[recommened, 'Custom']} /> - {serverData && props.value !== -1 && ( { - props.handleSetValue(value); - }} - minValue={1} - maxValue={serverData.maximum} - value={props.value} - />)} + {serverData && props.value !== -1 && ( + { + props.handleSetValue(value); + }} + minValue={1} + maxValue={serverData.maximum} + value={props.value} + /> + )} ); }; export const clientfps: Feature = { - name: "FPS", - category: "GAMEPLAY", + name: 'FPS', + category: 'GAMEPLAY', component: FpsInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx index 508ac2ce59c44..aa7b150367840 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx @@ -1,29 +1,25 @@ -import { multiline } from "common/string"; -import { CheckboxInput, FeatureChoiced, FeatureChoicedServerData, FeatureDropdownInput, FeatureToggle, FeatureValueProps } from "../base"; -import { Box, Dropdown, Flex } from "../../../../../components"; -import { classes } from "common/react"; +import { multiline } from 'common/string'; +import { CheckboxInput, FeatureChoiced, FeatureChoicedServerData, FeatureDropdownInput, FeatureToggle, FeatureValueProps } from '../base'; +import { Box, Dropdown, Flex } from '../../../../../components'; +import { classes } from 'common/react'; import type { InfernoNode } from 'inferno'; -import { binaryInsertWith } from "common/collections"; -import { useBackend } from "../../../../../backend"; -import { PreferencesMenuData } from "../../../data"; +import { binaryInsertWith } from 'common/collections'; +import { useBackend } from '../../../../../backend'; +import { PreferencesMenuData } from '../../../data'; export const ghost_accs: FeatureChoiced = { - name: "Ghost accessories", - category: "GHOST", - description: "Determines what adjustments your ghost will have.", + name: 'Ghost accessories', + category: 'GHOST', + description: 'Determines what adjustments your ghost will have.', component: FeatureDropdownInput, }; -const insertGhostForm - = binaryInsertWith<{ - displayText: InfernoNode, - value: string, - }>(({ value }) => value); +const insertGhostForm = binaryInsertWith<{ + displayText: InfernoNode; + value: string; +}>(({ value }) => value); -const GhostFormInput = ( - props: FeatureValueProps, - context, -) => { +const GhostFormInput = (props: FeatureValueProps, context) => { const { data } = useBackend(context); const serverData = props.serverData; @@ -33,32 +29,23 @@ const GhostFormInput = ( const displayNames = serverData.display_names; if (!displayNames) { - return ( - - No display names for ghost_form! - - ); + return No display names for ghost_form!; } const displayTexts = {}; let options: { - displayText: InfernoNode, - value: string, + displayText: InfernoNode; + value: string; }[] = []; for (const [name, displayName] of Object.entries(displayNames)) { const displayText = ( - + - - {displayName} - + {displayName} ); @@ -70,60 +57,56 @@ const GhostFormInput = ( }; // Put the default ghost on top - if (name === "ghost") { + if (name === 'ghost') { options.unshift(optionEntry); } else { options = insertGhostForm(options, optionEntry); } } - return (); + return ( + + ); }; export const ghost_form: FeatureChoiced = { - name: "Ghosts form", - category: "GHOST", - description: "The appearance of your ghost. Requires BYOND membership.", + name: 'Ghosts form', + category: 'GHOST', + description: 'The appearance of your ghost. Requires BYOND membership.', component: GhostFormInput, }; export const ghost_hud: FeatureToggle = { - name: "Ghost HUD", - category: "GHOST", - description: "Enable HUD buttons for ghosts.", + name: 'Ghost HUD', + category: 'GHOST', + description: 'Enable HUD buttons for ghosts.', component: CheckboxInput, }; export const ghost_orbit: FeatureChoiced = { - name: "Ghost orbit", - category: "GHOST", + name: 'Ghost orbit', + category: 'GHOST', description: multiline` The shape in which your ghost will orbit. Requires BYOND membership. `, - component: ( - props: FeatureValueProps, - context, - ) => { + component: (props: FeatureValueProps, context) => { const { data } = useBackend(context); - return (); + return ; }, }; export const ghost_others: FeatureChoiced = { - name: "Ghosts of others", - category: "GHOST", + name: 'Ghosts of others', + category: 'GHOST', description: multiline` Do you want the ghosts of others to show up as their own setting, as their default sprites, or always as the default white ghost? @@ -132,8 +115,8 @@ export const ghost_others: FeatureChoiced = { }; export const inquisitive_ghost: FeatureToggle = { - name: "Ghost inquisitiveness", - category: "GHOST", - description: "Clicking on something as a ghost will examine it.", + name: 'Ghost inquisitiveness', + category: 'GHOST', + description: 'Clicking on something as a ghost will examine it.', component: CheckboxInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/hotkeys.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/hotkeys.tsx index 73cc3c9de16e9..04ed6c04558f7 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/hotkeys.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/hotkeys.tsx @@ -1,8 +1,8 @@ -import { CheckboxInputInverse, FeatureToggle } from "../base"; +import { CheckboxInputInverse, FeatureToggle } from '../base'; export const hotkeys: FeatureToggle = { - name: "Classic hotkeys", - category: "GAMEPLAY", - description: "When enabled, will revert to the legacy hotkeys, using the input bar rather than popups.", + name: 'Classic hotkeys', + category: 'GAMEPLAY', + description: 'When enabled, will revert to the legacy hotkeys, using the input bar rather than popups.', component: CheckboxInputInverse, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ooc.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ooc.tsx index e4f5c5466f0e3..3e0421de71241 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ooc.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ooc.tsx @@ -1,8 +1,8 @@ -import { FeatureColorInput, Feature } from "../base"; +import { FeatureColorInput, Feature } from '../base'; export const ooccolor: Feature = { - name: "OOC color", - category: "CHAT", - description: "The color of your OOC messages.", + name: 'OOC color', + category: 'CHAT', + description: 'The color of your OOC messages.', component: FeatureColorInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/parallax.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/parallax.tsx index 1fbb2230ff202..a7b143a6d3d0d 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/parallax.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/parallax.tsx @@ -1,7 +1,7 @@ -import { Feature, FeatureDropdownInput } from "../base"; +import { Feature, FeatureDropdownInput } from '../base'; export const parallax: Feature = { - name: "Parallax (fancy space)", - category: "GAMEPLAY", + name: 'Parallax (fancy space)', + category: 'GAMEPLAY', component: FeatureDropdownInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/pixel_size.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/pixel_size.tsx index bb07453e340f1..4afde6a8bb1b8 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/pixel_size.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/pixel_size.tsx @@ -1,13 +1,13 @@ -import { createDropdownInput, Feature } from "../base"; +import { createDropdownInput, Feature } from '../base'; export const pixel_size: Feature = { - name: "Pixel Scaling", - category: "UI", + name: 'Pixel Scaling', + category: 'UI', component: createDropdownInput({ - 0: "Stretch to fit", - 1: "Pixel Perfect 1x", - 1.5: "Pixel Perfect 1.5x", - 2: "Pixel Perfect 2x", - 3: "Pixel Perfect 3x", + 0: 'Stretch to fit', + 1: 'Pixel Perfect 1x', + 1.5: 'Pixel Perfect 1.5x', + 2: 'Pixel Perfect 2x', + 3: 'Pixel Perfect 3x', }), }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/preferred_map.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/preferred_map.tsx index 3acfad00445a3..8d3c8b1742699 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/preferred_map.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/preferred_map.tsx @@ -1,9 +1,9 @@ -import { multiline } from "common/string"; -import { Feature, FeatureDropdownInput } from "../base"; +import { multiline } from 'common/string'; +import { Feature, FeatureDropdownInput } from '../base'; export const preferred_map: Feature = { - name: "Preferred map", - category: "GAMEPLAY", + name: 'Preferred map', + category: 'GAMEPLAY', description: multiline` During map rotation, prefer this map be chosen. This does not affect the map vote, only random rotation when a vote diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/runechat.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/runechat.tsx index 82f6fce7306fa..355142231ae28 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/runechat.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/runechat.tsx @@ -1,30 +1,30 @@ -import { Feature, CheckboxInput, FeatureDropdownInput, FeatureNumberInput, FeatureNumeric, FeatureToggle } from "../base"; +import { Feature, CheckboxInput, FeatureDropdownInput, FeatureNumberInput, FeatureNumeric, FeatureToggle } from '../base'; export const chat_on_map: FeatureToggle = { - name: "Enable Runechat", - category: "RUNECHAT", - description: "Chat messages will show above heads.", + name: 'Enable Runechat', + category: 'RUNECHAT', + description: 'Chat messages will show above heads.', component: CheckboxInput, }; export const see_chat_non_mob: FeatureToggle = { - name: "Enable Runechat on objects", - category: "RUNECHAT", - description: "Chat messages will show above objects when they speak.", + name: 'Enable Runechat on objects', + category: 'RUNECHAT', + description: 'Chat messages will show above objects when they speak.', component: CheckboxInput, }; export const see_rc_emotes: FeatureToggle = { - name: "Enable Runechat emotes", - category: "RUNECHAT", - description: "Emotes will show above heads.", + name: 'Enable Runechat emotes', + category: 'RUNECHAT', + description: 'Emotes will show above heads.', component: CheckboxInput, }; export const max_chat_length: FeatureNumeric = { - name: "Max chat length", - category: "RUNECHAT", - description: "The maximum length a Runechat message will show as.", + name: 'Max chat length', + category: 'RUNECHAT', + description: 'The maximum length a Runechat message will show as.', component: FeatureNumberInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/scaling_method.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/scaling_method.tsx index 114cc80ef5e8e..68a4c9c87fa30 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/scaling_method.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/scaling_method.tsx @@ -1,11 +1,11 @@ -import { createDropdownInput, Feature } from "../base"; +import { createDropdownInput, Feature } from '../base'; export const scaling_method: Feature = { - name: "Scaling method", - category: "UI", + name: 'Scaling method', + category: 'UI', component: createDropdownInput({ - blur: "Bilinear", - distort: "Nearest Neighbor", - normal: "Point Sampling", + blur: 'Bilinear', + distort: 'Nearest Neighbor', + normal: 'Point Sampling', }), }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tgui.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tgui.tsx index 573594dc43839..940b15eda4e29 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tgui.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tgui.tsx @@ -17,8 +17,7 @@ export const tgui_lock: FeatureToggle = { export const tgui_say_show_prefix: FeatureToggle = { name: 'Say: Show Prefix', category: 'UI', - description: - 'If radio prefixes should remain in the chatbox after being typed.', + description: 'If radio prefixes should remain in the chatbox after being typed.', component: CheckboxInput, }; @@ -39,8 +38,7 @@ export const tgui_input_large: FeatureToggle = { export const tgui_input_swapped: FeatureToggle = { name: 'Input: Swap Submit/Cancel buttons', category: 'UI', - description: - 'Switches the location of the Submit and Cancel buttons. On means Submit will be on the left.', + description: 'Switches the location of the Submit and Cancel buttons. On means Submit will be on the left.', component: CheckboxInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tooltips.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tooltips.tsx index d3147a01a1f20..62bfe8b4f5d30 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tooltips.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tooltips.tsx @@ -1,9 +1,9 @@ -import { multiline } from "common/string"; -import { CheckboxInput, Feature, FeatureNumberInput, FeatureToggle } from "../base"; +import { multiline } from 'common/string'; +import { CheckboxInput, Feature, FeatureNumberInput, FeatureToggle } from '../base'; export const enable_tips: FeatureToggle = { - name: "Enable tooltips", - category: "TOOLTIPS", + name: 'Enable tooltips', + category: 'TOOLTIPS', description: multiline` Do you want to see tooltips when hovering over items? `, @@ -11,8 +11,8 @@ export const enable_tips: FeatureToggle = { }; export const tip_delay: Feature = { - name: "Tooltip delay (in milliseconds)", - category: "TOOLTIPS", + name: 'Tooltip delay (in milliseconds)', + category: 'TOOLTIPS', description: multiline` How long should it take to see a tooltip when hovering over items? `, diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/window_flashing.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/window_flashing.tsx index 58f32c9476885..c4b7b6c2768d8 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/window_flashing.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/window_flashing.tsx @@ -1,9 +1,9 @@ -import { multiline } from "common/string"; -import { CheckboxInput, FeatureToggle } from "../base"; +import { multiline } from 'common/string'; +import { CheckboxInput, FeatureToggle } from '../base'; export const windowflashing: FeatureToggle = { - name: "Enable window flashing", - category: "UI", + name: 'Enable window flashing', + category: 'UI', description: multiline` When toggled, some important events will make your game icon flash on your task tray. diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/index.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/index.ts index d487a282e58cb..d8fa77f496bd5 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/index.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/index.ts @@ -3,15 +3,15 @@ // extremely small, and so it's easier for everyone to just combine them // together. // This still helps to prevent the server from needing to send client UI data -import { Feature } from "./base"; +import { Feature } from './base'; // while also preventing downstreams from needing to mutate existing files. const features: Record> = {}; -const requireFeature = require.context("./", true, /.tsx$/); +const requireFeature = require.context('./', true, /.tsx$/); for (const key of requireFeature.keys()) { - if (key === "index" || key === "base") { + if (key === 'index' || key === 'base') { continue; } diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/randomization.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/randomization.tsx index 88760b9758664..c2c4a0a937618 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/randomization.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/randomization.tsx @@ -1,9 +1,9 @@ -import { useBackend } from "../../../../backend"; -import { Button, Stack } from "../../../../components"; -import { PreferencesMenuData, RandomSetting } from "../../data"; -import { RandomizationButton } from "../../RandomizationButton"; -import { useRandomToggleState } from "../../useRandomToggleState"; -import { CheckboxInput, Feature, FeatureToggle } from "./base"; +import { useBackend } from '../../../../backend'; +import { Button, Stack } from '../../../../components'; +import { PreferencesMenuData, RandomSetting } from '../../data'; +import { RandomizationButton } from '../../RandomizationButton'; +import { useRandomToggleState } from '../../useRandomToggleState'; +import { CheckboxInput, Feature, FeatureToggle } from './base'; export const body_is_always_random: Feature = { name: 'Random body', @@ -13,10 +13,7 @@ export const body_is_always_random: Feature = { return ( - props.handleSetValue(newValue)} - value={props.value} - /> + props.handleSetValue(newValue)} value={props.value} /> {randomToggle ? ( @@ -49,35 +46,32 @@ export const body_is_always_random: Feature = { }; export const random_hardcore: FeatureToggle = { - name: "Hardcore random", + name: 'Hardcore random', component: CheckboxInput, }; export const name_is_always_random: Feature = { - name: "Random name", + name: 'Random name', component: (props, context) => { - return ( - props.handleSetValue(value)} - value={props.value} - /> - ); + return props.handleSetValue(value)} value={props.value} />; }, }; export const random_species: Feature = { - name: "Random species", + name: 'Random species', component: (props, context) => { const { act, data } = useBackend(context); - const species = data.character_preferences.randomization["species"]; + const species = data.character_preferences.randomization['species']; return ( act("set_random_preference", { - preference: "species", - value: newValue, - })} + setValue={(newValue) => + act('set_random_preference', { + preference: 'species', + value: newValue, + }) + } value={species || RandomSetting.Disabled} /> ); diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/gender.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/gender.ts index 787a3c900cb04..d757c47aa4874 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/gender.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/gender.ts @@ -1,22 +1,22 @@ export enum Gender { - Male = "male", - Female = "female", - Other = "plural", + Male = 'male', + Female = 'female', + Other = 'plural', } export const GENDERS = { [Gender.Male]: { - icon: "male", - text: "Male", + icon: 'male', + text: 'Male', }, [Gender.Female]: { - icon: "female", - text: "Female", + icon: 'female', + text: 'Female', }, [Gender.Other]: { - icon: "tg-non-binary", - text: "Other", + icon: 'tg-non-binary', + text: 'Other', }, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/base.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/base.ts index 7f99548c5b500..8a0de3a90f81a 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/base.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/base.ts @@ -1,9 +1,9 @@ export type Species = { description: string; features: { - good: Feature[], - neutral: Feature[], - bad: Feature[], + good: Feature[]; + neutral: Feature[]; + bad: Feature[]; }; lore?: string[]; }; @@ -15,7 +15,7 @@ export type Feature = { }; export const fallbackSpecies: Species = { - description: "No description! File a bug report!", + description: 'No description! File a bug report!', features: { good: [], neutral: [], @@ -25,9 +25,8 @@ export const fallbackSpecies: Species = { export const createLanguagePerk = (language: string): Feature => { return { - icon: "comment", - name: "Native Speaker", - description: - `Alongside Galactic Common, gain the ability to speak ${language}.`, + icon: 'comment', + name: 'Native Speaker', + description: `Alongside Galactic Common, gain the ability to speak ${language}.`, }; }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/ethereal.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/ethereal.ts index 2ff0f9cc89176..f5bbc71799fdd 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/ethereal.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/ethereal.ts @@ -1,63 +1,80 @@ -import { createLanguagePerk, Species } from "./base"; +import { createLanguagePerk, Species } from './base'; const Ethereal: Species = { - description: "Coming from the planet of Sprout, the theocratic ethereals are \ + description: + 'Coming from the planet of Sprout, the theocratic ethereals are \ separated socially by caste, and espouse a dogma of aiding the weak and \ - downtrodden.", + downtrodden.', features: { - good: [{ - icon: "bolt", - name: "Shockingly Tasty", - description: "Ethereals can feed on electricity from APCs, and do not \ - otherwise need to eat.", - }, { - icon: "lightbulb", - name: "Disco Ball", - description: "Ethereals passively generate their own light.", - }, { - icon: "shield-alt", - name: "Shock Resistance", - description: "Ethereals are less affected by shocks.", - }, { - icon: "temperature-high", - name: "Heat Resistance", - description: "Ethereals have much better tolerance for high \ - temperatures.", - }, createLanguagePerk("Voltaic")], - neutral: [{ - icon: "tint", - name: "Liquid Electricity", - description: "Ethereals have liquid electricity instead of blood. \ + good: [ + { + icon: 'bolt', + name: 'Shockingly Tasty', + description: 'Ethereals can feed on electricity from APCs, and do not \ + otherwise need to eat.', + }, + { + icon: 'lightbulb', + name: 'Disco Ball', + description: 'Ethereals passively generate their own light.', + }, + { + icon: 'shield-alt', + name: 'Shock Resistance', + description: 'Ethereals are less affected by shocks.', + }, + { + icon: 'temperature-high', + name: 'Heat Resistance', + description: 'Ethereals have much better tolerance for high \ + temperatures.', + }, + createLanguagePerk('Voltaic'), + ], + neutral: [ + { + icon: 'tint', + name: 'Liquid Electricity', + description: + 'Ethereals have liquid electricity instead of blood. \ Great for them, horrid for anyone else. Can make receiving medical \ - treatment harder.", - }, { - icon: "fire", - name: "Flaming Punch", - description: "Ethereals deal burn damage when punching instead of \ - brute damage.", - }, { - icon: "gem", - name: "Crystal Core", - description: "The hearts of ethereals will protect them in a cystal when \ - they die, reviving them with a permanent brain trauma.", - }], - bad: [{ - icon: "biohazard", - name: "Starving Artist", - description: "Ethereals take toxin damage while starving.", - }, { - icon: "fist-raised", - name: "Brutal Weakness", - description: "Ethereals are weak to brute damage.", - }, { - icon: "temperature-low", - name: "Cold Weakness", - description: "Ethereals have much lower tolerance for cold \ - temperatures.", - }], + treatment harder.', + }, + { + icon: 'fire', + name: 'Flaming Punch', + description: 'Ethereals deal burn damage when punching instead of \ + brute damage.', + }, + { + icon: 'gem', + name: 'Crystal Core', + description: + 'The hearts of ethereals will protect them in a cystal when \ + they die, reviving them with a permanent brain trauma.', + }, + ], + bad: [ + { + icon: 'biohazard', + name: 'Starving Artist', + description: 'Ethereals take toxin damage while starving.', + }, + { + icon: 'fist-raised', + name: 'Brutal Weakness', + description: 'Ethereals are weak to brute damage.', + }, + { + icon: 'temperature-low', + name: 'Cold Weakness', + description: 'Ethereals have much lower tolerance for cold \ + temperatures.', + }, + ], }, lore: [ - "Ethereals are a species native to the planet Sprout. When they were originally discovered, they were at a medieval level of technological progression, but due to their natural acclimation with electricity, they felt easy among the large NanoTrasen installations.", + 'Ethereals are a species native to the planet Sprout. When they were originally discovered, they were at a medieval level of technological progression, but due to their natural acclimation with electricity, they felt easy among the large NanoTrasen installations.', ], }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/felinid.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/felinid.ts index 4ab3ff566bd69..f372eab15dee2 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/felinid.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/felinid.ts @@ -1,27 +1,33 @@ -import { createLanguagePerk, Species } from "./base"; +import { createLanguagePerk, Species } from './base'; const Felinid: Species = { - description: "Felinids are one of the many types of bespoke genetic \ + description: + "Felinids are one of the many types of bespoke genetic \ modifications to come of humanity's mastery of genetic science, and are \ also one of the most common. Meow?", features: { - good: [{ - icon: "grin-tongue", - name: "Grooming", - description: "Felinids can lick wounds to reduce bleeding.", - }, createLanguagePerk("Nekomimetic")], + good: [ + { + icon: 'grin-tongue', + name: 'Grooming', + description: 'Felinids can lick wounds to reduce bleeding.', + }, + createLanguagePerk('Nekomimetic'), + ], neutral: [], - bad: [{ - icon: "assistive-listening-systems", - name: "Sensitive Hearing", - description: "Felinids are more sensitive to loud sounds, such as \ - flashbangs.", - }], + bad: [ + { + icon: 'assistive-listening-systems', + name: 'Sensitive Hearing', + description: 'Felinids are more sensitive to loud sounds, such as \ + flashbangs.', + }, + ], }, lore: [ - "Bio-engineering at its felinest, felinids are the peak example of humanity's mastery of genetic code. One of many \"animalid\" variants, felinids are the most popular and common, as well as one of the biggest points of contention in genetic-modification.", - "Body modders were eager to splice human and feline DNA in search of the holy trifecta: ears, eyes, and tail. These traits were in high demand, with the corresponding side effects of vocal and neurochemical changes being seen as a minor inconvenience.", - "Sadly for the felinids, they were not minor inconveniences. Shunned as subhuman and monstrous by many, felinids (and other animalids) sought their greener pastures out in the colonies, cloistering in communities of their own kind. As a result, outer human space has a high animalid population.", + 'Bio-engineering at its felinest, felinids are the peak example of humanity\'s mastery of genetic code. One of many "animalid" variants, felinids are the most popular and common, as well as one of the biggest points of contention in genetic-modification.', + 'Body modders were eager to splice human and feline DNA in search of the holy trifecta: ears, eyes, and tail. These traits were in high demand, with the corresponding side effects of vocal and neurochemical changes being seen as a minor inconvenience.', + 'Sadly for the felinids, they were not minor inconveniences. Shunned as subhuman and monstrous by many, felinids (and other animalids) sought their greener pastures out in the colonies, cloistering in communities of their own kind. As a result, outer human space has a high animalid population.', ], }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/human.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/human.ts index 30b1c28853b41..08fa2cbfe2288 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/human.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/human.ts @@ -1,29 +1,34 @@ -import { Species } from "./base"; +import { Species } from './base'; const Human: Species = { - description: "Humans are the dominant species in the known galaxy, their \ - kind extend from old Earth to the edges of known space.", + description: + 'Humans are the dominant species in the known galaxy, their \ + kind extend from old Earth to the edges of known space.', features: { - good: [{ - icon: "robot", - name: "Asimov Superiority", - description: "The AI and their cyborgs are, by default, subservient only \ + good: [ + { + icon: 'robot', + name: 'Asimov Superiority', + description: + 'The AI and their cyborgs are, by default, subservient only \ to humans. As a human, silicons are required to both protect and obey \ - you.", - }, { - icon: "bullhorn", - name: "Chain of Command", - description: "Nanotrasen only recognizes humans for command roles, such \ - as Captain.", - }], + you.', + }, + { + icon: 'bullhorn', + name: 'Chain of Command', + description: 'Nanotrasen only recognizes humans for command roles, such \ + as Captain.', + }, + ], neutral: [], bad: [], }, lore: [ - "These primate-descended creatures, originating from the mostly harmless Earth, have long-since outgrown their home and semi-benign designation. The space age has taken humans out of their solar system and into the galaxy-at-large.", - "In traditional human fashion, this near-record pace from terra firma to the final frontier spat in the face of other races they now shared a stage with. This included the lizards - if anyone was offended by these upstarts, it was certainly lizardkind.", - "Humanity never managed to find the kind of peace to fully unite under one banner like other species. The pencil and paper pushing of the UN bureaucrat lives on in the mosaic that is TerraGov; a composite of the nation-states that still live on in human society.", - "The human spirit of opportunity and enterprise continues on in its peak form: the hypercorporation. Acting outside of TerraGov's influence, literally and figuratively, hypercorporations buy the senate votes they need and establish territory far past the Earth Government's reach. In hypercorporation territory company policy is law, giving new meaning to \"employee termination\".", + 'These primate-descended creatures, originating from the mostly harmless Earth, have long-since outgrown their home and semi-benign designation. The space age has taken humans out of their solar system and into the galaxy-at-large.', + 'In traditional human fashion, this near-record pace from terra firma to the final frontier spat in the face of other races they now shared a stage with. This included the lizards - if anyone was offended by these upstarts, it was certainly lizardkind.', + 'Humanity never managed to find the kind of peace to fully unite under one banner like other species. The pencil and paper pushing of the UN bureaucrat lives on in the mosaic that is TerraGov; a composite of the nation-states that still live on in human society.', + 'The human spirit of opportunity and enterprise continues on in its peak form: the hypercorporation. Acting outside of TerraGov\'s influence, literally and figuratively, hypercorporations buy the senate votes they need and establish territory far past the Earth Government\'s reach. In hypercorporation territory company policy is law, giving new meaning to "employee termination".', ], }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/lizard.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/lizard.ts index 0345b469dab97..2f23f36100a8c 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/lizard.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/lizard.ts @@ -1,31 +1,37 @@ -import { createLanguagePerk, Species } from "./base"; +import { createLanguagePerk, Species } from './base'; const Lizard: Species = { - description: "The militaristic hail originally from Tizira, but have grown \ + description: + 'The militaristic hail originally from Tizira, but have grown \ throughout their centuries in the stars to possess a large spacefaring \ empire: though now they must contend with their younger, more \ - technologically advanced human neighbours.", + technologically advanced human neighbours.', features: { - good: [createLanguagePerk("Draconic")], - neutral: [{ - icon: "thermometer-empty", - name: "Cold-blooded", - description: "Higher tolerance for high temperatures, but lower \ - tolerance for cold temperatures.", - }], - bad: [{ - icon: "tint", - name: "Exotic Blood", - description: "Lizards have a unique \"L\" type blood, which can make \ - receiving medical treatment more difficult.", - }], + good: [createLanguagePerk('Draconic')], + neutral: [ + { + icon: 'thermometer-empty', + name: 'Cold-blooded', + description: 'Higher tolerance for high temperatures, but lower \ + tolerance for cold temperatures.', + }, + ], + bad: [ + { + icon: 'tint', + name: 'Exotic Blood', + description: + 'Lizards have a unique "L" type blood, which can make \ + receiving medical treatment more difficult.', + }, + ], }, lore: [ - "The face of conspiracy theory was changed forever the day mankind met the lizards.", + 'The face of conspiracy theory was changed forever the day mankind met the lizards.', "Hailing from the arid world of Tizira, lizards were travelling the stars back when mankind was first discovering how neat trains could be. However, much like the space-fable of the space-tortoise and space-hare, lizards have rejected their kin's motto of \"slow and steady\" in favor of resting on their laurels and getting completely surpassed by 'bald apes', due in no small part to their lack of access to plasma.", "The history between lizards and humans has resulted in many conflicts that lizards ended on the losing side of, with the finale being an explosive remodeling of their moon. Today's lizard-human relations are seeing the continuance of a record period of peace.", - "Lizard culture is inherently militaristic, though the influence the military has on lizard culture begins to lessen the further colonies lie from their homeworld - with some distanced colonies finding themselves subsumed by the cultural practices of other species nearby.", - "On their homeworld, lizards celebrate their 16th birthday by enrolling in a mandatory 5 year military tour of duty. Roles range from combat to civil service and everything in between. As the old slogan goes: \"Your place will be found!\"", + 'Lizard culture is inherently militaristic, though the influence the military has on lizard culture begins to lessen the further colonies lie from their homeworld - with some distanced colonies finding themselves subsumed by the cultural practices of other species nearby.', + 'On their homeworld, lizards celebrate their 16th birthday by enrolling in a mandatory 5 year military tour of duty. Roles range from combat to civil service and everything in between. As the old slogan goes: "Your place will be found!"', ], }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/moth.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/moth.ts index 338c650ddbb03..1dde183aa899e 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/moth.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/moth.ts @@ -1,36 +1,45 @@ -import { createLanguagePerk, Species } from "./base"; +import { createLanguagePerk, Species } from './base'; const Moth: Species = { - description: "Hailing from a planet that was lost long ago, the moths travel \ + description: + 'Hailing from a planet that was lost long ago, the moths travel \ the galaxy as a nomadic people aboard a colossal fleet of ships, seeking a \ - new homeland.", + new homeland.', features: { - good: [{ - icon: "feather-alt", - name: "Precious Wings", - description: "Moths can fly in pressurized, zero-g environments using \ - their wings.", - }, { - icon: "tshirt", - name: "Meal Plan", - description: "Moths can eat clothes for nourishment.", - }, createLanguagePerk("Moffic")], + good: [ + { + icon: 'feather-alt', + name: 'Precious Wings', + description: 'Moths can fly in pressurized, zero-g environments using \ + their wings.', + }, + { + icon: 'tshirt', + name: 'Meal Plan', + description: 'Moths can eat clothes for nourishment.', + }, + createLanguagePerk('Moffic'), + ], neutral: [], - bad: [{ - icon: "fire", - name: "Ablazed Wings", - description: "Moth wings are fragile, and can be easily burnt off.", - }, { - icon: "sun", - name: "Bright Lights", - description: "Moths need an extra layer of flash protection to protect \ + bad: [ + { + icon: 'fire', + name: 'Ablazed Wings', + description: 'Moth wings are fragile, and can be easily burnt off.', + }, + { + icon: 'sun', + name: 'Bright Lights', + description: + 'Moths need an extra layer of flash protection to protect \ themselves, such as against security officers or when welding. Welding \ - masks will work.", - }], + masks will work.', + }, + ], }, lore: [ - "Their homeworld lost to the ages, the moths live aboard the Grand Nomad Fleet. Made up of what could be found, bartered, repaired, or stolen the armada is a colossal patchwork built on a history of politely flagging travelers down and taking their things. Occasionally a moth will decide to leave the fleet, usually to strike out for fortunes to send back home.", - "Nomadic life produces a tight-knit culture, with moths valuing their friends, family, and vessels highly. Moths are gregarious by nature and do best in communal spaces. This has served them well on the galactic stage, maintaining a friendly and personable reputation even in the face of hostile encounters. It seems that the galaxy has come to accept these former pirates.", + 'Their homeworld lost to the ages, the moths live aboard the Grand Nomad Fleet. Made up of what could be found, bartered, repaired, or stolen the armada is a colossal patchwork built on a history of politely flagging travelers down and taking their things. Occasionally a moth will decide to leave the fleet, usually to strike out for fortunes to send back home.', + 'Nomadic life produces a tight-knit culture, with moths valuing their friends, family, and vessels highly. Moths are gregarious by nature and do best in communal spaces. This has served them well on the galactic stage, maintaining a friendly and personable reputation even in the face of hostile encounters. It seems that the galaxy has come to accept these former pirates.', "Surprisingly, living together in a giant fleet hasn't flattened variance in dialect and culture. These differences are welcomed and encouraged within the fleet for the variety that they bring.", ], }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/plasmaman.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/plasmaman.ts index 34a7bee72d240..787bb47e49f6d 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/plasmaman.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/species/plasmaman.ts @@ -1,70 +1,87 @@ -import { createLanguagePerk, Species } from "./base"; +import { createLanguagePerk, Species } from './base'; const Plasmaman: Species = { - description: "Found on the Icemoon of Freyja, plasmamen consist of colonial \ + description: + "Found on the Icemoon of Freyja, plasmamen consist of colonial \ fungal organisms which together form a sentient being. In human space, \ they're usually attached to skeletons to afford a human touch.", features: { - good: [{ - icon: "shield-alt", - name: "Protected", - description: "Plasmamen are immune to radiation, poisons, and most \ - diseases.", - }, { - icon: "tint-slash", - name: "Bloodletted", - description: "Plasmamen do not have blood.", - }, { - icon: "bone", - name: "Wound Resistance", - description: "Plasmamen have higher tolerance for damage that would \ - wound others.", - }, { - icon: "temperature-low", - name: "Cold Resistance", - description: "Plasmamen have a higher resistance to cold temperatures.", - }, { - icon: "wind", - name: "Plasma Healing", - description: "Plasmamen can heal wounds by consuming plasma.", - }, { - icon: "hard-hat", - name: "Protective Helmet", - description: "Plasmamen's helmets provide them shielding from the \ + good: [ + { + icon: 'shield-alt', + name: 'Protected', + description: 'Plasmamen are immune to radiation, poisons, and most \ + diseases.', + }, + { + icon: 'tint-slash', + name: 'Bloodletted', + description: 'Plasmamen do not have blood.', + }, + { + icon: 'bone', + name: 'Wound Resistance', + description: 'Plasmamen have higher tolerance for damage that would \ + wound others.', + }, + { + icon: 'temperature-low', + name: 'Cold Resistance', + description: 'Plasmamen have a higher resistance to cold temperatures.', + }, + { + icon: 'wind', + name: 'Plasma Healing', + description: 'Plasmamen can heal wounds by consuming plasma.', + }, + { + icon: 'hard-hat', + name: 'Protective Helmet', + description: + "Plasmamen's helmets provide them shielding from the \ flashes of welding, as well as a flashlight.", - }, createLanguagePerk("Calcic")], + }, + createLanguagePerk('Calcic'), + ], neutral: [], - bad: [{ - icon: "fire", - name: "Human* Torch", - description: "Plasmamen instantly ignite when their body makes contact \ - with oxygen.", - }, { - icon: "wind", - name: "Plasma Breathing", - description: "Plasmamen must breathe plasma to survive. You receive a \ - tank when you arrive.", - }, { - icon: "temperature-high", - name: "Heat Weakness", - description: "Plasmamen have a lower resistance to high temperatures.", - }, { - icon: "fist-raised", - name: "Total Weakness", - description: "Plasmamen take more burn and brute damage.", - }, { - icon: "briefcase-medical", - name: "An Apple a Day", - description: "Plasmamen take specialized medical knowledge to be \ + bad: [ + { + icon: 'fire', + name: 'Human* Torch', + description: 'Plasmamen instantly ignite when their body makes contact \ + with oxygen.', + }, + { + icon: 'wind', + name: 'Plasma Breathing', + description: 'Plasmamen must breathe plasma to survive. You receive a \ + tank when you arrive.', + }, + { + icon: 'temperature-high', + name: 'Heat Weakness', + description: 'Plasmamen have a lower resistance to high temperatures.', + }, + { + icon: 'fist-raised', + name: 'Total Weakness', + description: 'Plasmamen take more burn and brute damage.', + }, + { + icon: 'briefcase-medical', + name: 'An Apple a Day', + description: + 'Plasmamen take specialized medical knowledge to be \ treated. Do not expect speedy revival, if you are lucky enough to get \ - one at all.", - }], + one at all.', + }, + ], }, lore: [ - "A confusing species, plasmamen are truly \"a fungus among us\". What appears to be a singular being is actually a colony of millions of organisms surrounding a found (or provided) skeletal structure.", - "Originally discovered by NT when a researcher fell into an open tank of liquid plasma, the previously unnoticed fungal colony overtook the body creating the first \"true\" plasmaman. The process has since been streamlined via generous donations of convict corpses and plasmamen have been deployed en masse throughout NT to bolster the workforce.", - "New to the galactic stage, plasmamen are a blank slate. Their appearance, generally regarded as \"ghoulish\", inspires a lot of apprehension in their crewmates. It might be the whole \"flammable purple skeleton\" thing.", - "The colonids that make up plasmamen require the plasma-rich atmosphere they evolved in. Their psuedo-nervous system runs with externalized electrical impulses that immediately ignite their plasma-based bodies when oxygen is present.", + 'A confusing species, plasmamen are truly "a fungus among us". What appears to be a singular being is actually a colony of millions of organisms surrounding a found (or provided) skeletal structure.', + 'Originally discovered by NT when a researcher fell into an open tank of liquid plasma, the previously unnoticed fungal colony overtook the body creating the first "true" plasmaman. The process has since been streamlined via generous donations of convict corpses and plasmamen have been deployed en masse throughout NT to bolster the workforce.', + 'New to the galactic stage, plasmamen are a blank slate. Their appearance, generally regarded as "ghoulish", inspires a lot of apprehension in their crewmates. It might be the whole "flammable purple skeleton" thing.', + 'The colonids that make up plasmamen require the plasma-rich atmosphere they evolved in. Their psuedo-nervous system runs with externalized electrical impulses that immediately ignite their plasma-based bodies when oxygen is present.', ], }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/useRandomToggleState.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/useRandomToggleState.ts index 6721ee70cf5c4..67d0319503778 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/useRandomToggleState.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/useRandomToggleState.ts @@ -1,4 +1,3 @@ -import { useLocalState } from "../../backend"; +import { useLocalState } from '../../backend'; -export const useRandomToggleState - = context => useLocalState(context, "randomToggle", false); +export const useRandomToggleState = (context) => useLocalState(context, 'randomToggle', false); diff --git a/tgui/packages/tgui/stories/Popper.stories.js b/tgui/packages/tgui/stories/Popper.stories.js index 30043de079b5d..08fd430fb2757 100644 --- a/tgui/packages/tgui/stories/Popper.stories.js +++ b/tgui/packages/tgui/stories/Popper.stories.js @@ -1,4 +1,4 @@ -import { Box, Popper } from "../components"; +import { Box, Popper } from '../components'; export const meta = { title: 'Popper', diff --git a/tgui/packages/tgui/styles/atomic/fit-text.scss b/tgui/packages/tgui/styles/atomic/fit-text.scss index 2a899601ba26a..1d49e13c8e35b 100644 --- a/tgui/packages/tgui/styles/atomic/fit-text.scss +++ b/tgui/packages/tgui/styles/atomic/fit-text.scss @@ -2,7 +2,11 @@ $mqIterations: 19; @mixin fontResize($iterations) { $i: 1; @while $i <= $iterations { - @media all and (min-width: 100px * $i) { .fit-text { font-size:0.1em * $i; } } + @media all and (min-width: 100px * $i) { + .fit-text { + font-size: 0.1em * $i; + } + } $i: $i + 1; } } diff --git a/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss b/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss index afb33d1899a3d..b753db4b9ab33 100644 --- a/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss +++ b/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss @@ -157,7 +157,6 @@ $department_map: ( } &--Captain { - &.head { .job-name { font-size: 15px; @@ -174,7 +173,7 @@ $department_map: ( border: 1px solid rgba(0, 0, 0, 0.3); &--off::after { - content: ""; + content: ''; background: rgba(0, 0, 0, 0.2); display: block; @@ -190,10 +189,7 @@ $department_map: ( &__PriorityHeader { font-weight: bold; - transform: - translateX(-4px) - translateY(-8px) - rotate(315deg); + transform: translateX(-4px) translateY(-8px) rotate(315deg); white-space: nowrap; width: 19px; } @@ -213,9 +209,9 @@ $department_map: ( transition: background-color 0.1s ease-in; $quality_map: ( - "positive": colors.$green, - "neutral": colors.$white, - "negative": colors.$red, + 'positive': colors.$green, + 'neutral': colors.$white, + 'negative': colors.$red, ); @each $quality, $color-value in $quality_map { From 9177b176076ca413a255de17be65796dd51b036c Mon Sep 17 00:00:00 2001 From: itsmeow Date: Thu, 8 Jun 2023 22:09:33 -0400 Subject: [PATCH 095/269] The great reorganization --- beestation.dme | 109 +++++++++--------- .../client/preferences/{ => entries}/admin.dm | 0 .../client/preferences/{ => entries}/age.dm | 0 .../{ => entries}/ai_core_display.dm | 0 .../{ => entries}/ambient_occlusion.dm | 0 .../{ => entries}/auto_fit_viewport.dm | 0 .../preferences/{ => entries}/body_type.dm | 0 .../{ => entries}/broadcast_login_logout.dm | 0 .../{ => entries}/buttons_locked.dm | 0 .../client/preferences/{ => entries}/chat.dm | 0 .../preferences/{ => entries}/clothing.dm | 0 .../{ => entries}/crew_objectives.dm | 0 .../{ => entries}/darkened_flash.dm | 0 .../client/preferences/{ => entries}/fps.dm | 0 .../preferences/{ => entries}/gender.dm | 0 .../client/preferences/{ => entries}/ghost.dm | 0 .../preferences/{ => entries}/glasses.dm | 0 .../preferences/{ => entries}/hotkeys.dm | 0 .../{ => entries}/item_outlines.dm | 0 .../preferences/{ => entries}/jobless_role.dm | 0 .../client/preferences/{ => entries}/names.dm | 0 .../client/preferences/{ => entries}/ooc.dm | 0 .../preferences/{ => entries}/parallax.dm | 0 .../client/preferences/{ => entries}/pda.dm | 0 .../preferences/{ => entries}/pixel_size.dm | 0 .../{ => entries}/preferred_map.dm | 0 .../preferences/{ => entries}/random.dm | 0 .../preferences/{ => entries}/rattle.dm | 0 .../preferences/{ => entries}/roundend.dm | 0 .../preferences/{ => entries}/runechat.dm | 0 .../{ => entries}/scaling_method.dm | 0 .../{ => entries}/security_department.dm | 0 .../preferences/{ => entries}/skin_tone.dm | 0 .../client/preferences/{ => entries}/sound.dm | 0 .../preferences/{ => entries}/species.dm | 0 .../{ => entries}/species_features/basic.dm | 0 .../species_features/ethereal.dm | 0 .../{ => entries}/species_features/felinid.dm | 0 .../{ => entries}/species_features/lizard.dm | 0 .../{ => entries}/species_features/moth.dm | 0 .../{ => entries}/species_features/mutants.dm | 0 .../species_features/plasmaman.dm | 0 .../client/preferences/{ => entries}/tgui.dm | 0 .../preferences/{ => entries}/tooltips.dm | 0 .../preferences/{ => entries}/ui_style.dm | 0 .../{ => entries}/underwear_color.dm | 0 .../{ => entries}/uplink_location.dm | 0 .../{ => entries}/window_flashing.dm | 0 code/modules/client/preferences/phobia.dm | 23 ---- .../{assets.dm => preference_assets.dm} | 0 .../{_preference.dm => preference_entry.dm} | 0 .../preference_verbs.dm} | 0 .../client/{ => preferences}/preferences.dm | 0 .../{ => preferences}/preferences_toggles.dm | 0 .../serialization}/preferences_character.dm | 0 .../serialization}/preferences_database.dm | 0 .../serialization}/preferences_player.dm | 0 57 files changed, 54 insertions(+), 78 deletions(-) rename code/modules/client/preferences/{ => entries}/admin.dm (100%) rename code/modules/client/preferences/{ => entries}/age.dm (100%) rename code/modules/client/preferences/{ => entries}/ai_core_display.dm (100%) rename code/modules/client/preferences/{ => entries}/ambient_occlusion.dm (100%) rename code/modules/client/preferences/{ => entries}/auto_fit_viewport.dm (100%) rename code/modules/client/preferences/{ => entries}/body_type.dm (100%) rename code/modules/client/preferences/{ => entries}/broadcast_login_logout.dm (100%) rename code/modules/client/preferences/{ => entries}/buttons_locked.dm (100%) rename code/modules/client/preferences/{ => entries}/chat.dm (100%) rename code/modules/client/preferences/{ => entries}/clothing.dm (100%) rename code/modules/client/preferences/{ => entries}/crew_objectives.dm (100%) rename code/modules/client/preferences/{ => entries}/darkened_flash.dm (100%) rename code/modules/client/preferences/{ => entries}/fps.dm (100%) rename code/modules/client/preferences/{ => entries}/gender.dm (100%) rename code/modules/client/preferences/{ => entries}/ghost.dm (100%) rename code/modules/client/preferences/{ => entries}/glasses.dm (100%) rename code/modules/client/preferences/{ => entries}/hotkeys.dm (100%) rename code/modules/client/preferences/{ => entries}/item_outlines.dm (100%) rename code/modules/client/preferences/{ => entries}/jobless_role.dm (100%) rename code/modules/client/preferences/{ => entries}/names.dm (100%) rename code/modules/client/preferences/{ => entries}/ooc.dm (100%) rename code/modules/client/preferences/{ => entries}/parallax.dm (100%) rename code/modules/client/preferences/{ => entries}/pda.dm (100%) rename code/modules/client/preferences/{ => entries}/pixel_size.dm (100%) rename code/modules/client/preferences/{ => entries}/preferred_map.dm (100%) rename code/modules/client/preferences/{ => entries}/random.dm (100%) rename code/modules/client/preferences/{ => entries}/rattle.dm (100%) rename code/modules/client/preferences/{ => entries}/roundend.dm (100%) rename code/modules/client/preferences/{ => entries}/runechat.dm (100%) rename code/modules/client/preferences/{ => entries}/scaling_method.dm (100%) rename code/modules/client/preferences/{ => entries}/security_department.dm (100%) rename code/modules/client/preferences/{ => entries}/skin_tone.dm (100%) rename code/modules/client/preferences/{ => entries}/sound.dm (100%) rename code/modules/client/preferences/{ => entries}/species.dm (100%) rename code/modules/client/preferences/{ => entries}/species_features/basic.dm (100%) rename code/modules/client/preferences/{ => entries}/species_features/ethereal.dm (100%) rename code/modules/client/preferences/{ => entries}/species_features/felinid.dm (100%) rename code/modules/client/preferences/{ => entries}/species_features/lizard.dm (100%) rename code/modules/client/preferences/{ => entries}/species_features/moth.dm (100%) rename code/modules/client/preferences/{ => entries}/species_features/mutants.dm (100%) rename code/modules/client/preferences/{ => entries}/species_features/plasmaman.dm (100%) rename code/modules/client/preferences/{ => entries}/tgui.dm (100%) rename code/modules/client/preferences/{ => entries}/tooltips.dm (100%) rename code/modules/client/preferences/{ => entries}/ui_style.dm (100%) rename code/modules/client/preferences/{ => entries}/underwear_color.dm (100%) rename code/modules/client/preferences/{ => entries}/uplink_location.dm (100%) rename code/modules/client/preferences/{ => entries}/window_flashing.dm (100%) delete mode 100644 code/modules/client/preferences/phobia.dm rename code/modules/client/preferences/{assets.dm => preference_assets.dm} (100%) rename code/modules/client/preferences/{_preference.dm => preference_entry.dm} (100%) rename code/modules/client/{preferences_menu.dm => preferences/preference_verbs.dm} (100%) rename code/modules/client/{ => preferences}/preferences.dm (100%) rename code/modules/client/{ => preferences}/preferences_toggles.dm (100%) rename code/modules/client/{ => preferences/serialization}/preferences_character.dm (100%) rename code/modules/client/{ => preferences/serialization}/preferences_database.dm (100%) rename code/modules/client/{ => preferences/serialization}/preferences_player.dm (100%) diff --git a/beestation.dme b/beestation.dme index 91f95f3e74a92..9563632a24401 100644 --- a/beestation.dme +++ b/beestation.dme @@ -2124,11 +2124,6 @@ #include "code\modules\client\helpers.dm" #include "code\modules\client\message.dm" #include "code\modules\client\player_details.dm" -#include "code\modules\client\preferences.dm" -#include "code\modules\client\preferences_character.dm" -#include "code\modules\client\preferences_database.dm" -#include "code\modules\client\preferences_menu.dm" -#include "code\modules\client\preferences_player.dm" #include "code\modules\client\loadout\loadout.dm" #include "code\modules\client\loadout\loadout_accessories.dm" #include "code\modules\client\loadout\loadout_colorizers.dm" @@ -2139,49 +2134,57 @@ #include "code\modules\client\loadout\loadout_ooc.dm" #include "code\modules\client\loadout\loadout_suit.dm" #include "code\modules\client\loadout\loadout_uniform.dm" -#include "code\modules\client\preferences\_preference.dm" -#include "code\modules\client\preferences\admin.dm" -#include "code\modules\client\preferences\age.dm" -#include "code\modules\client\preferences\ai_core_display.dm" -#include "code\modules\client\preferences\ambient_occlusion.dm" -#include "code\modules\client\preferences\assets.dm" -#include "code\modules\client\preferences\auto_fit_viewport.dm" -#include "code\modules\client\preferences\body_type.dm" -#include "code\modules\client\preferences\broadcast_login_logout.dm" -#include "code\modules\client\preferences\buttons_locked.dm" -#include "code\modules\client\preferences\chat.dm" -#include "code\modules\client\preferences\clothing.dm" -#include "code\modules\client\preferences\crew_objectives.dm" -#include "code\modules\client\preferences\darkened_flash.dm" -#include "code\modules\client\preferences\fps.dm" -#include "code\modules\client\preferences\gender.dm" -#include "code\modules\client\preferences\ghost.dm" -#include "code\modules\client\preferences\glasses.dm" -#include "code\modules\client\preferences\hotkeys.dm" -#include "code\modules\client\preferences\item_outlines.dm" -#include "code\modules\client\preferences\jobless_role.dm" -#include "code\modules\client\preferences\names.dm" -#include "code\modules\client\preferences\ooc.dm" -#include "code\modules\client\preferences\parallax.dm" -#include "code\modules\client\preferences\pda.dm" -#include "code\modules\client\preferences\phobia.dm" -#include "code\modules\client\preferences\pixel_size.dm" -#include "code\modules\client\preferences\preferred_map.dm" -#include "code\modules\client\preferences\random.dm" -#include "code\modules\client\preferences\rattle.dm" -#include "code\modules\client\preferences\roundend.dm" -#include "code\modules\client\preferences\runechat.dm" -#include "code\modules\client\preferences\scaling_method.dm" -#include "code\modules\client\preferences\security_department.dm" -#include "code\modules\client\preferences\skin_tone.dm" -#include "code\modules\client\preferences\sound.dm" -#include "code\modules\client\preferences\species.dm" -#include "code\modules\client\preferences\tgui.dm" -#include "code\modules\client\preferences\tooltips.dm" -#include "code\modules\client\preferences\ui_style.dm" -#include "code\modules\client\preferences\underwear_color.dm" -#include "code\modules\client\preferences\uplink_location.dm" -#include "code\modules\client\preferences\window_flashing.dm" +#include "code\modules\client\preferences\preference_assets.dm" +#include "code\modules\client\preferences\preference_entry.dm" +#include "code\modules\client\preferences\preference_verbs.dm" +#include "code\modules\client\preferences\preferences.dm" +#include "code\modules\client\preferences\entries\admin.dm" +#include "code\modules\client\preferences\entries\age.dm" +#include "code\modules\client\preferences\entries\ai_core_display.dm" +#include "code\modules\client\preferences\entries\ambient_occlusion.dm" +#include "code\modules\client\preferences\entries\auto_fit_viewport.dm" +#include "code\modules\client\preferences\entries\body_type.dm" +#include "code\modules\client\preferences\entries\broadcast_login_logout.dm" +#include "code\modules\client\preferences\entries\buttons_locked.dm" +#include "code\modules\client\preferences\entries\chat.dm" +#include "code\modules\client\preferences\entries\clothing.dm" +#include "code\modules\client\preferences\entries\crew_objectives.dm" +#include "code\modules\client\preferences\entries\darkened_flash.dm" +#include "code\modules\client\preferences\entries\fps.dm" +#include "code\modules\client\preferences\entries\gender.dm" +#include "code\modules\client\preferences\entries\ghost.dm" +#include "code\modules\client\preferences\entries\glasses.dm" +#include "code\modules\client\preferences\entries\hotkeys.dm" +#include "code\modules\client\preferences\entries\item_outlines.dm" +#include "code\modules\client\preferences\entries\jobless_role.dm" +#include "code\modules\client\preferences\entries\names.dm" +#include "code\modules\client\preferences\entries\ooc.dm" +#include "code\modules\client\preferences\entries\parallax.dm" +#include "code\modules\client\preferences\entries\pda.dm" +#include "code\modules\client\preferences\entries\pixel_size.dm" +#include "code\modules\client\preferences\entries\preferred_map.dm" +#include "code\modules\client\preferences\entries\random.dm" +#include "code\modules\client\preferences\entries\rattle.dm" +#include "code\modules\client\preferences\entries\roundend.dm" +#include "code\modules\client\preferences\entries\runechat.dm" +#include "code\modules\client\preferences\entries\scaling_method.dm" +#include "code\modules\client\preferences\entries\security_department.dm" +#include "code\modules\client\preferences\entries\skin_tone.dm" +#include "code\modules\client\preferences\entries\sound.dm" +#include "code\modules\client\preferences\entries\species.dm" +#include "code\modules\client\preferences\entries\tgui.dm" +#include "code\modules\client\preferences\entries\tooltips.dm" +#include "code\modules\client\preferences\entries\ui_style.dm" +#include "code\modules\client\preferences\entries\underwear_color.dm" +#include "code\modules\client\preferences\entries\uplink_location.dm" +#include "code\modules\client\preferences\entries\window_flashing.dm" +#include "code\modules\client\preferences\entries\species_features\basic.dm" +#include "code\modules\client\preferences\entries\species_features\ethereal.dm" +#include "code\modules\client\preferences\entries\species_features\felinid.dm" +#include "code\modules\client\preferences\entries\species_features\lizard.dm" +#include "code\modules\client\preferences\entries\species_features\moth.dm" +#include "code\modules\client\preferences\entries\species_features\mutants.dm" +#include "code\modules\client\preferences\entries\species_features\plasmaman.dm" #include "code\modules\client\preferences\middleware\_middleware.dm" #include "code\modules\client\preferences\middleware\antags.dm" #include "code\modules\client\preferences\middleware\jobs.dm" @@ -2191,13 +2194,9 @@ #include "code\modules\client\preferences\middleware\quirks.dm" #include "code\modules\client\preferences\middleware\random.dm" #include "code\modules\client\preferences\middleware\species.dm" -#include "code\modules\client\preferences\species_features\basic.dm" -#include "code\modules\client\preferences\species_features\ethereal.dm" -#include "code\modules\client\preferences\species_features\felinid.dm" -#include "code\modules\client\preferences\species_features\lizard.dm" -#include "code\modules\client\preferences\species_features\moth.dm" -#include "code\modules\client\preferences\species_features\mutants.dm" -#include "code\modules\client\preferences\species_features\plasmaman.dm" +#include "code\modules\client\preferences\serialization\preferences_character.dm" +#include "code\modules\client\preferences\serialization\preferences_database.dm" +#include "code\modules\client\preferences\serialization\preferences_player.dm" #include "code\modules\client\verbs\input_box.dm" #include "code\modules\client\verbs\looc.dm" #include "code\modules\client\verbs\ooc.dm" diff --git a/code/modules/client/preferences/admin.dm b/code/modules/client/preferences/entries/admin.dm similarity index 100% rename from code/modules/client/preferences/admin.dm rename to code/modules/client/preferences/entries/admin.dm diff --git a/code/modules/client/preferences/age.dm b/code/modules/client/preferences/entries/age.dm similarity index 100% rename from code/modules/client/preferences/age.dm rename to code/modules/client/preferences/entries/age.dm diff --git a/code/modules/client/preferences/ai_core_display.dm b/code/modules/client/preferences/entries/ai_core_display.dm similarity index 100% rename from code/modules/client/preferences/ai_core_display.dm rename to code/modules/client/preferences/entries/ai_core_display.dm diff --git a/code/modules/client/preferences/ambient_occlusion.dm b/code/modules/client/preferences/entries/ambient_occlusion.dm similarity index 100% rename from code/modules/client/preferences/ambient_occlusion.dm rename to code/modules/client/preferences/entries/ambient_occlusion.dm diff --git a/code/modules/client/preferences/auto_fit_viewport.dm b/code/modules/client/preferences/entries/auto_fit_viewport.dm similarity index 100% rename from code/modules/client/preferences/auto_fit_viewport.dm rename to code/modules/client/preferences/entries/auto_fit_viewport.dm diff --git a/code/modules/client/preferences/body_type.dm b/code/modules/client/preferences/entries/body_type.dm similarity index 100% rename from code/modules/client/preferences/body_type.dm rename to code/modules/client/preferences/entries/body_type.dm diff --git a/code/modules/client/preferences/broadcast_login_logout.dm b/code/modules/client/preferences/entries/broadcast_login_logout.dm similarity index 100% rename from code/modules/client/preferences/broadcast_login_logout.dm rename to code/modules/client/preferences/entries/broadcast_login_logout.dm diff --git a/code/modules/client/preferences/buttons_locked.dm b/code/modules/client/preferences/entries/buttons_locked.dm similarity index 100% rename from code/modules/client/preferences/buttons_locked.dm rename to code/modules/client/preferences/entries/buttons_locked.dm diff --git a/code/modules/client/preferences/chat.dm b/code/modules/client/preferences/entries/chat.dm similarity index 100% rename from code/modules/client/preferences/chat.dm rename to code/modules/client/preferences/entries/chat.dm diff --git a/code/modules/client/preferences/clothing.dm b/code/modules/client/preferences/entries/clothing.dm similarity index 100% rename from code/modules/client/preferences/clothing.dm rename to code/modules/client/preferences/entries/clothing.dm diff --git a/code/modules/client/preferences/crew_objectives.dm b/code/modules/client/preferences/entries/crew_objectives.dm similarity index 100% rename from code/modules/client/preferences/crew_objectives.dm rename to code/modules/client/preferences/entries/crew_objectives.dm diff --git a/code/modules/client/preferences/darkened_flash.dm b/code/modules/client/preferences/entries/darkened_flash.dm similarity index 100% rename from code/modules/client/preferences/darkened_flash.dm rename to code/modules/client/preferences/entries/darkened_flash.dm diff --git a/code/modules/client/preferences/fps.dm b/code/modules/client/preferences/entries/fps.dm similarity index 100% rename from code/modules/client/preferences/fps.dm rename to code/modules/client/preferences/entries/fps.dm diff --git a/code/modules/client/preferences/gender.dm b/code/modules/client/preferences/entries/gender.dm similarity index 100% rename from code/modules/client/preferences/gender.dm rename to code/modules/client/preferences/entries/gender.dm diff --git a/code/modules/client/preferences/ghost.dm b/code/modules/client/preferences/entries/ghost.dm similarity index 100% rename from code/modules/client/preferences/ghost.dm rename to code/modules/client/preferences/entries/ghost.dm diff --git a/code/modules/client/preferences/glasses.dm b/code/modules/client/preferences/entries/glasses.dm similarity index 100% rename from code/modules/client/preferences/glasses.dm rename to code/modules/client/preferences/entries/glasses.dm diff --git a/code/modules/client/preferences/hotkeys.dm b/code/modules/client/preferences/entries/hotkeys.dm similarity index 100% rename from code/modules/client/preferences/hotkeys.dm rename to code/modules/client/preferences/entries/hotkeys.dm diff --git a/code/modules/client/preferences/item_outlines.dm b/code/modules/client/preferences/entries/item_outlines.dm similarity index 100% rename from code/modules/client/preferences/item_outlines.dm rename to code/modules/client/preferences/entries/item_outlines.dm diff --git a/code/modules/client/preferences/jobless_role.dm b/code/modules/client/preferences/entries/jobless_role.dm similarity index 100% rename from code/modules/client/preferences/jobless_role.dm rename to code/modules/client/preferences/entries/jobless_role.dm diff --git a/code/modules/client/preferences/names.dm b/code/modules/client/preferences/entries/names.dm similarity index 100% rename from code/modules/client/preferences/names.dm rename to code/modules/client/preferences/entries/names.dm diff --git a/code/modules/client/preferences/ooc.dm b/code/modules/client/preferences/entries/ooc.dm similarity index 100% rename from code/modules/client/preferences/ooc.dm rename to code/modules/client/preferences/entries/ooc.dm diff --git a/code/modules/client/preferences/parallax.dm b/code/modules/client/preferences/entries/parallax.dm similarity index 100% rename from code/modules/client/preferences/parallax.dm rename to code/modules/client/preferences/entries/parallax.dm diff --git a/code/modules/client/preferences/pda.dm b/code/modules/client/preferences/entries/pda.dm similarity index 100% rename from code/modules/client/preferences/pda.dm rename to code/modules/client/preferences/entries/pda.dm diff --git a/code/modules/client/preferences/pixel_size.dm b/code/modules/client/preferences/entries/pixel_size.dm similarity index 100% rename from code/modules/client/preferences/pixel_size.dm rename to code/modules/client/preferences/entries/pixel_size.dm diff --git a/code/modules/client/preferences/preferred_map.dm b/code/modules/client/preferences/entries/preferred_map.dm similarity index 100% rename from code/modules/client/preferences/preferred_map.dm rename to code/modules/client/preferences/entries/preferred_map.dm diff --git a/code/modules/client/preferences/random.dm b/code/modules/client/preferences/entries/random.dm similarity index 100% rename from code/modules/client/preferences/random.dm rename to code/modules/client/preferences/entries/random.dm diff --git a/code/modules/client/preferences/rattle.dm b/code/modules/client/preferences/entries/rattle.dm similarity index 100% rename from code/modules/client/preferences/rattle.dm rename to code/modules/client/preferences/entries/rattle.dm diff --git a/code/modules/client/preferences/roundend.dm b/code/modules/client/preferences/entries/roundend.dm similarity index 100% rename from code/modules/client/preferences/roundend.dm rename to code/modules/client/preferences/entries/roundend.dm diff --git a/code/modules/client/preferences/runechat.dm b/code/modules/client/preferences/entries/runechat.dm similarity index 100% rename from code/modules/client/preferences/runechat.dm rename to code/modules/client/preferences/entries/runechat.dm diff --git a/code/modules/client/preferences/scaling_method.dm b/code/modules/client/preferences/entries/scaling_method.dm similarity index 100% rename from code/modules/client/preferences/scaling_method.dm rename to code/modules/client/preferences/entries/scaling_method.dm diff --git a/code/modules/client/preferences/security_department.dm b/code/modules/client/preferences/entries/security_department.dm similarity index 100% rename from code/modules/client/preferences/security_department.dm rename to code/modules/client/preferences/entries/security_department.dm diff --git a/code/modules/client/preferences/skin_tone.dm b/code/modules/client/preferences/entries/skin_tone.dm similarity index 100% rename from code/modules/client/preferences/skin_tone.dm rename to code/modules/client/preferences/entries/skin_tone.dm diff --git a/code/modules/client/preferences/sound.dm b/code/modules/client/preferences/entries/sound.dm similarity index 100% rename from code/modules/client/preferences/sound.dm rename to code/modules/client/preferences/entries/sound.dm diff --git a/code/modules/client/preferences/species.dm b/code/modules/client/preferences/entries/species.dm similarity index 100% rename from code/modules/client/preferences/species.dm rename to code/modules/client/preferences/entries/species.dm diff --git a/code/modules/client/preferences/species_features/basic.dm b/code/modules/client/preferences/entries/species_features/basic.dm similarity index 100% rename from code/modules/client/preferences/species_features/basic.dm rename to code/modules/client/preferences/entries/species_features/basic.dm diff --git a/code/modules/client/preferences/species_features/ethereal.dm b/code/modules/client/preferences/entries/species_features/ethereal.dm similarity index 100% rename from code/modules/client/preferences/species_features/ethereal.dm rename to code/modules/client/preferences/entries/species_features/ethereal.dm diff --git a/code/modules/client/preferences/species_features/felinid.dm b/code/modules/client/preferences/entries/species_features/felinid.dm similarity index 100% rename from code/modules/client/preferences/species_features/felinid.dm rename to code/modules/client/preferences/entries/species_features/felinid.dm diff --git a/code/modules/client/preferences/species_features/lizard.dm b/code/modules/client/preferences/entries/species_features/lizard.dm similarity index 100% rename from code/modules/client/preferences/species_features/lizard.dm rename to code/modules/client/preferences/entries/species_features/lizard.dm diff --git a/code/modules/client/preferences/species_features/moth.dm b/code/modules/client/preferences/entries/species_features/moth.dm similarity index 100% rename from code/modules/client/preferences/species_features/moth.dm rename to code/modules/client/preferences/entries/species_features/moth.dm diff --git a/code/modules/client/preferences/species_features/mutants.dm b/code/modules/client/preferences/entries/species_features/mutants.dm similarity index 100% rename from code/modules/client/preferences/species_features/mutants.dm rename to code/modules/client/preferences/entries/species_features/mutants.dm diff --git a/code/modules/client/preferences/species_features/plasmaman.dm b/code/modules/client/preferences/entries/species_features/plasmaman.dm similarity index 100% rename from code/modules/client/preferences/species_features/plasmaman.dm rename to code/modules/client/preferences/entries/species_features/plasmaman.dm diff --git a/code/modules/client/preferences/tgui.dm b/code/modules/client/preferences/entries/tgui.dm similarity index 100% rename from code/modules/client/preferences/tgui.dm rename to code/modules/client/preferences/entries/tgui.dm diff --git a/code/modules/client/preferences/tooltips.dm b/code/modules/client/preferences/entries/tooltips.dm similarity index 100% rename from code/modules/client/preferences/tooltips.dm rename to code/modules/client/preferences/entries/tooltips.dm diff --git a/code/modules/client/preferences/ui_style.dm b/code/modules/client/preferences/entries/ui_style.dm similarity index 100% rename from code/modules/client/preferences/ui_style.dm rename to code/modules/client/preferences/entries/ui_style.dm diff --git a/code/modules/client/preferences/underwear_color.dm b/code/modules/client/preferences/entries/underwear_color.dm similarity index 100% rename from code/modules/client/preferences/underwear_color.dm rename to code/modules/client/preferences/entries/underwear_color.dm diff --git a/code/modules/client/preferences/uplink_location.dm b/code/modules/client/preferences/entries/uplink_location.dm similarity index 100% rename from code/modules/client/preferences/uplink_location.dm rename to code/modules/client/preferences/entries/uplink_location.dm diff --git a/code/modules/client/preferences/window_flashing.dm b/code/modules/client/preferences/entries/window_flashing.dm similarity index 100% rename from code/modules/client/preferences/window_flashing.dm rename to code/modules/client/preferences/entries/window_flashing.dm diff --git a/code/modules/client/preferences/phobia.dm b/code/modules/client/preferences/phobia.dm deleted file mode 100644 index 5e81a97bae490..0000000000000 --- a/code/modules/client/preferences/phobia.dm +++ /dev/null @@ -1,23 +0,0 @@ -/*/datum/preference/choiced/phobia - category = PREFERENCE_CATEGORY_SECONDARY_FEATURES - db_key = "phobia" - preference_type = PREFERENCE_CHARACTER - -/datum/preference/choiced/phobia/init_possible_values() - return list() -*/ - -// TODO tgui-prefs -/* -/datum/preference/choiced/phobia/init_possible_values() - return GLOB.phobia_types - -/datum/preference/choiced/phobia/is_accessible(datum/preferences/preferences) - if (!..(preferences)) - return FALSE - - return "Phobia" in preferences.all_quirks - -/datum/preference/choiced/phobia/apply_to_human(mob/living/carbon/human/target, value) - return -*/ diff --git a/code/modules/client/preferences/assets.dm b/code/modules/client/preferences/preference_assets.dm similarity index 100% rename from code/modules/client/preferences/assets.dm rename to code/modules/client/preferences/preference_assets.dm diff --git a/code/modules/client/preferences/_preference.dm b/code/modules/client/preferences/preference_entry.dm similarity index 100% rename from code/modules/client/preferences/_preference.dm rename to code/modules/client/preferences/preference_entry.dm diff --git a/code/modules/client/preferences_menu.dm b/code/modules/client/preferences/preference_verbs.dm similarity index 100% rename from code/modules/client/preferences_menu.dm rename to code/modules/client/preferences/preference_verbs.dm diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences/preferences.dm similarity index 100% rename from code/modules/client/preferences.dm rename to code/modules/client/preferences/preferences.dm diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences/preferences_toggles.dm similarity index 100% rename from code/modules/client/preferences_toggles.dm rename to code/modules/client/preferences/preferences_toggles.dm diff --git a/code/modules/client/preferences_character.dm b/code/modules/client/preferences/serialization/preferences_character.dm similarity index 100% rename from code/modules/client/preferences_character.dm rename to code/modules/client/preferences/serialization/preferences_character.dm diff --git a/code/modules/client/preferences_database.dm b/code/modules/client/preferences/serialization/preferences_database.dm similarity index 100% rename from code/modules/client/preferences_database.dm rename to code/modules/client/preferences/serialization/preferences_database.dm diff --git a/code/modules/client/preferences_player.dm b/code/modules/client/preferences/serialization/preferences_player.dm similarity index 100% rename from code/modules/client/preferences_player.dm rename to code/modules/client/preferences/serialization/preferences_player.dm From c3e0e4f193b9e167d6f8e21aad158db372ec9f7a Mon Sep 17 00:00:00 2001 From: Mothblocks <35135081+Mothblocks@users.noreply.github.com> Date: Tue, 26 Apr 2022 18:44:55 -0700 Subject: [PATCH 096/269] Remove data/ from /datum/asset/json url mapping (#66549) * Remove data/ from /datum/asset/json url mapping Closes #66535. * Create JSON in data folder --- code/modules/asset_cache/asset_list.dm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/modules/asset_cache/asset_list.dm b/code/modules/asset_cache/asset_list.dm index df793e8984bb1..408bdbadb05fb 100644 --- a/code/modules/asset_cache/asset_list.dm +++ b/code/modules/asset_cache/asset_list.dm @@ -520,18 +520,18 @@ GLOBAL_LIST_EMPTY(asset_datums) /datum/asset/json/send(client) if(!..()) return FALSE - return SSassets.transport.send_assets(client, "data/[name].json") + return SSassets.transport.send_assets(client, "[name].json") /datum/asset/json/get_url_mappings() return list( - "[name].json" = SSassets.transport.get_asset_url("data/[name].json"), + "[name].json" = SSassets.transport.get_asset_url("[name].json"), ) /datum/asset/json/register() var/filename = "data/[name].json" fdel(filename) text2file(json_encode(generate()), filename) - SSassets.transport.register_asset(filename, fcopy_rsc(filename)) + SSassets.transport.register_asset("[name].json", fcopy_rsc(filename)) fdel(filename) /// Returns the data that will be JSON encoded From 4a16e7f45ba70795936c5d752f15fe84f113cc96 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Fri, 9 Jun 2023 00:05:49 -0400 Subject: [PATCH 097/269] Fixes the fucking null assets Syntax error for some fucking reason please god end me --- code/modules/asset_cache/asset_list.dm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/modules/asset_cache/asset_list.dm b/code/modules/asset_cache/asset_list.dm index 408bdbadb05fb..f4451f326cd3d 100644 --- a/code/modules/asset_cache/asset_list.dm +++ b/code/modules/asset_cache/asset_list.dm @@ -520,7 +520,8 @@ GLOBAL_LIST_EMPTY(asset_datums) /datum/asset/json/send(client) if(!..()) return FALSE - return SSassets.transport.send_assets(client, "[name].json") + SSassets.transport.send_assets(client, "[name].json") + return TRUE /datum/asset/json/get_url_mappings() return list( From c97139a6d45f3827200259fc0483f80f36621d05 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Fri, 9 Jun 2023 04:34:13 -0400 Subject: [PATCH 098/269] Fix un-datumized player prefs not saving --- .../client/preferences/serialization/preferences_player.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/client/preferences/serialization/preferences_player.dm b/code/modules/client/preferences/serialization/preferences_player.dm index e0a63c40c07c3..69d5264771eb2 100644 --- a/code/modules/client/preferences/serialization/preferences_player.dm +++ b/code/modules/client/preferences/serialization/preferences_player.dm @@ -35,7 +35,7 @@ return TRUE /datum/preferences_holder/preferences_player/proc/write_to_database(datum/preferences/prefs) - write_data(prefs) + . = write_data(prefs) dirty_prefs.Cut() // clear all dirty preferences /datum/preferences_holder/preferences_player/proc/write_data(datum/preferences/prefs) From 64a291347bdf25738625ff7763351b3dee9713be Mon Sep 17 00:00:00 2001 From: itsmeow Date: Fri, 9 Jun 2023 04:35:24 -0400 Subject: [PATCH 099/269] Basic loadouts --- code/modules/asset_cache/asset_list_items.dm | 55 ++++---- code/modules/client/loadout/loadout_ooc.dm | 2 + .../client/preferences/middleware/loadout.dm | 59 ++++++++- .../client/preferences/preference_assets.dm | 17 +++ .../modules/client/preferences/preferences.dm | 3 +- code/modules/clothing/under/accessories.dm | 6 +- .../CharacterPreferenceWindow.tsx | 2 +- .../PreferencesMenu/LoadoutPage.tsx | 118 ++++++++++++++++-- .../tgui/interfaces/PreferencesMenu/data.ts | 6 + .../styles/atomic/section-background.scss | 10 ++ tgui/packages/tgui/styles/main.scss | 1 + 11 files changed, 237 insertions(+), 42 deletions(-) create mode 100644 tgui/packages/tgui/styles/atomic/section-background.scss diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm index 8f02c89aa5228..f0ebe97e0af4a 100644 --- a/code/modules/asset_cache/asset_list_items.dm +++ b/code/modules/asset_cache/asset_list_items.dm @@ -401,35 +401,38 @@ var/atom/item = k if (!ispath(item, /atom)) continue - - var/icon_file - if (initial(item.greyscale_colors) && initial(item.greyscale_config)) - icon_file = SSgreyscale.GetColoredIconByType(initial(item.greyscale_config), initial(item.greyscale_colors)) - else - icon_file = initial(item.icon) - var/icon_state = initial(item.icon_state) - var/icon/I - - var/icon_states_list = icon_states(icon_file) - if(icon_state in icon_states_list) - I = icon(icon_file, icon_state, SOUTH) - var/c = initial(item.color) - if (!isnull(c) && c != "#FFFFFF") - I.Blend(c, ICON_MULTIPLY) - else - var/icon_states_string - for (var/an_icon_state in icon_states_list) - if (!icon_states_string) - icon_states_string = "[json_encode(an_icon_state)](\ref[an_icon_state])" - else - icon_states_string += ", [json_encode(an_icon_state)](\ref[an_icon_state])" - stack_trace("[item] does not have a valid icon state, icon=[icon_file], icon_state=[json_encode(icon_state)](\ref[icon_state]), icon_states=[icon_states_string]") - I = icon('icons/turf/floors.dmi', "", SOUTH) - var/imgid = replacetext(replacetext("[item]", "/obj/item/", ""), "/", "-") - + var/icon/I = get_display_icon_for(item) Insert(imgid, I) +/proc/get_display_icon_for(atom/item) + if (!ispath(item, /atom)) + return + var/icon_file + if (initial(item.greyscale_colors) && initial(item.greyscale_config)) + icon_file = SSgreyscale.GetColoredIconByType(initial(item.greyscale_config), initial(item.greyscale_colors)) + else + icon_file = initial(item.icon) + var/icon_state = initial(item.icon_state) + var/icon/I + + var/icon_states_list = icon_states(icon_file) + if(icon_state in icon_states_list) + I = icon(icon_file, icon_state, SOUTH) + var/c = initial(item.color) + if (!isnull(c) && c != "#FFFFFF") + I.Blend(c, ICON_MULTIPLY) + else + var/icon_states_string + for (var/an_icon_state in icon_states_list) + if (!icon_states_string) + icon_states_string = "[json_encode(an_icon_state)](\ref[an_icon_state])" + else + icon_states_string += ", [json_encode(an_icon_state)](\ref[an_icon_state])" + stack_trace("[item] does not have a valid icon state, icon=[icon_file], icon_state=[json_encode(icon_state)](\ref[icon_state]), icon_states=[icon_states_string]") + I = icon('icons/turf/floors.dmi', "", SOUTH) + return I + /datum/asset/simple/bee_antags assets = list( "traitor.png" = 'html/img/traitor.png', diff --git a/code/modules/client/loadout/loadout_ooc.dm b/code/modules/client/loadout/loadout_ooc.dm index 38a1a860a1145..cb755d3a4febe 100644 --- a/code/modules/client/loadout/loadout_ooc.dm +++ b/code/modules/client/loadout/loadout_ooc.dm @@ -7,6 +7,7 @@ display_name = "extra character slot" description = "An extra charslot. Pretty self-explanatory." cost = 10000 + path = /obj/item/toy/figure/captain /datum/gear/ooc/char_slot/purchase(var/client/C) // TODO tgui-prefs C?.prefs?.set_max_character_slots(C.prefs.max_usable_slots + 1) @@ -15,6 +16,7 @@ display_name = "antag token" description = "If you can afford it, you deserve it." cost = 100000 + path = /obj/item/coin/antagtoken /datum/gear/ooc/real_antagtoken/purchase(var/client/C) C.inc_antag_token_count(1) diff --git a/code/modules/client/preferences/middleware/loadout.dm b/code/modules/client/preferences/middleware/loadout.dm index 4e8c738dfdbbb..d165931791196 100644 --- a/code/modules/client/preferences/middleware/loadout.dm +++ b/code/modules/client/preferences/middleware/loadout.dm @@ -1,10 +1,15 @@ /datum/preference_middleware/loadout - action_delegations = list() + action_delegations = list( + "purchase_gear" = PROC_REF(purchase_gear), + "equip_gear" = PROC_REF(equip_gear), + ) /datum/preference_middleware/loadout/get_ui_data(mob/user) var/list/data = list() data["equipped_gear"] = preferences.equipped_gear data["purchased_gear"] = preferences.purchased_gear + data["metacurrency_balance"] = preferences.parent.get_metabalance() + data["is_donator"] = IS_PATRON(user.ckey) || (user in GLOB.admins) return data /datum/preference_middleware/loadout/get_constant_data() @@ -12,6 +17,8 @@ var/list/categories = list() for(var/category_id in GLOB.loadout_categories) var/datum/loadout_category/LC = GLOB.loadout_categories[category_id] + if(LC.category == "Donator" && !CONFIG_GET(flag/donator_items)) // Don't show donator items if the server has them off + continue var/list/category = list() category["name"] = LC.category var/list/gear = list() @@ -28,4 +35,54 @@ category["gear"] = gear categories += list(category) data["categories"] = categories + data["metacurrency_name"] = CONFIG_GET(string/metacurrency_name) return data + +/datum/preference_middleware/loadout/proc/purchase_gear(list/params, mob/user) + var/datum/gear/TG = GLOB.gear_datums[params["id"]] + if(!istype(TG)) + return + if(TG.id in preferences.equipped_gear) + to_chat(user, "You already own \the [TG.display_name]!") + return TRUE + if(TG.sort_category == "Donator") + if(user.client && CONFIG_GET(flag/donator_items) && alert(user.client, "This item is only accessible to our patrons. Would you like to subscribe?", "Patron Locked", "Yes", "No") == "Yes") + user.client.donate() + return + + if(TG.cost < user.client.get_metabalance_db()) + preferences.purchased_gear += TG.id + TG.purchase(user.client) + user.client.inc_metabalance((TG.cost * -1), TRUE, "Purchased [TG.display_name].") + preferences.save_preferences() + return TRUE + else + to_chat(user, "You don't have enough [CONFIG_GET(string/metacurrency_name)]s to purchase \the [TG.display_name]!") + +/datum/preference_middleware/loadout/proc/equip_gear(list/params, mob/user) + var/datum/gear/TG = GLOB.gear_datums[params["id"]] + if(!istype(TG)) + return + if(TG.id in preferences.equipped_gear) + preferences.equipped_gear -= TG.id + return TRUE + else + var/list/type_blacklist = list() + var/list/slot_blacklist = list() + for(var/gear_id in preferences.equipped_gear) + var/datum/gear/G = GLOB.gear_datums[gear_id] + if(istype(G)) + if(!(G.subtype_path in type_blacklist)) + type_blacklist += G.subtype_path + if(!(G.slot in slot_blacklist)) + slot_blacklist += G.slot + if((TG.id in preferences.purchased_gear)) + if(!(TG.subtype_path in type_blacklist) || !(TG.slot in slot_blacklist)) + preferences.equipped_gear += TG.id + return TRUE + else + to_chat(user, "Can't equip [TG.display_name]. It conflicts with an already-equipped item.") + else + log_href_exploit(user, "Attempting to equip [TG.type] when they do not own it.") + return TRUE + preferences.save_preferences() diff --git a/code/modules/client/preferences/preference_assets.dm b/code/modules/client/preferences/preference_assets.dm index 4838490f7ae0f..57a7ab7df2c9a 100644 --- a/code/modules/client/preferences/preference_assets.dm +++ b/code/modules/client/preferences/preference_assets.dm @@ -41,6 +41,23 @@ /datum/preference/proc/get_spritesheet_key(value) return "[db_key]___[sanitize_css_class_name(value)]" +/datum/asset/spritesheet/preferences_loadout + name = "preferences_loadout" + early = TRUE + +/datum/asset/spritesheet/preferences_loadout/register() + for(var/gear_id in GLOB.gear_datums) + var/datum/gear/G = GLOB.gear_datums[gear_id] + if(!ispath(G.path, /atom)) + continue + var/icon/regular_icon = get_display_icon_for(G.path) + Insert("loadout_gear___[gear_id]", regular_icon) + if(!ispath(G.skirt_path, /atom)) + continue + var/icon/skirt_icon = get_display_icon_for(G.skirt_path) + Insert("loadout_gear___[gear_id]_skirt", skirt_icon) + return ..() + /// Sends information needed for shared details on individual preferences /datum/asset/json/preferences name = "preferences" diff --git a/code/modules/client/preferences/preferences.dm b/code/modules/client/preferences/preferences.dm index 4989e771de598..1e4a161d04cd3 100644 --- a/code/modules/client/preferences/preferences.dm +++ b/code/modules/client/preferences/preferences.dm @@ -194,6 +194,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) /datum/preferences/ui_assets(mob/user) var/list/assets = list( get_asset_datum(/datum/asset/spritesheet/preferences), + get_asset_datum(/datum/asset/spritesheet/preferences_loadout), get_asset_datum(/datum/asset/json/preferences), ) @@ -547,7 +548,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/character_preview_view) /// Handles adding and removing donator items from clients /datum/preferences/proc/handle_donator_items() var/datum/loadout_category/DLC = GLOB.loadout_categories["Donator"] // stands for donator loadout category but the other def for DLC works too xD - if(!LAZYLEN(GLOB.patrons) || !CONFIG_GET(flag/donator_items)) // donator items are only accesibile by servers with a patreon + if(!CONFIG_GET(flag/donator_items)) // donator items are only accesibile by servers with a patreon return if(IS_PATRON(parent.ckey) || (parent in GLOB.admins)) for(var/gear_id in DLC.gear) diff --git a/code/modules/clothing/under/accessories.dm b/code/modules/clothing/under/accessories.dm index 6e729d3027a0e..cf47d13e89488 100755 --- a/code/modules/clothing/under/accessories.dm +++ b/code/modules/clothing/under/accessories.dm @@ -265,12 +265,14 @@ /obj/item/clothing/accessory/armband/blue name = "blue armband" desc = "A fancy blue armband!" - color = list(0,0,1, 0,1,0, 1,0,0) + icon_state = "medband" + color = "#0000ff" /obj/item/clothing/accessory/armband/green name = "green armband" desc = "A fancy green armband!" - color = list(0,1,0, 1,0,0, 0,0,1) + icon_state = "medband" + color = "#00ff00" /obj/item/clothing/accessory/armband/deputy name = "security deputy armband" diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx index 52b280ae8a337..a89a6b78de854 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx @@ -80,7 +80,7 @@ export const CharacterPreferenceWindow = (props, context) => { } return ( - + diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx index 1f48239ebc064..b61d55c27fa12 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx @@ -1,24 +1,120 @@ -import { Box, Tabs, Button } from '../../components'; +import { Box, Tabs, Button, Section, Stack, Flex, Table } from '../../components'; import { PreferencesMenuData } from './data'; -import { useBackend } from '../../backend'; +import { useBackend, useLocalState } from '../../backend'; import { ServerPreferencesFetcher } from './ServerPreferencesFetcher'; +import { CharacterPreview } from './CharacterPreview'; export const LoadoutPage = (props, context) => { - const { act } = useBackend(context); + const { act, data } = useBackend(context); + const { purchased_gear = [], equipped_gear = [], character_preferences, metacurrency_balance = 0 } = data; + const jumpsuit_style = character_preferences.clothing.jumpsuit_style; return ( { - if (!data) { + render={(serverData) => { + if (!serverData) { return Loading loadout data...; } - const { categories = [], purchased_gear = [], equipped_gear = [] } = data.loadout; + const { categories = [], metacurrency_name } = serverData.loadout; + const [selectedCategory, setSelectedCategory] = useLocalState(context, 'category', categories[0].name); + + let selectedCategoryObject = categories.filter((c) => c.name === selectedCategory)[0]; return ( - - {categories.map((category) => ( - {category.name} - ))} - + + + + +
    From 923f4b93bd6f5fcb66b03b7e17511ea05c156161 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Sat, 10 Jun 2023 02:24:11 -0400 Subject: [PATCH 107/269] Remove BYOND bug workaround hack. Lummy pls fix --- code/modules/client/preferences/preferences.dm | 4 ---- 1 file changed, 4 deletions(-) diff --git a/code/modules/client/preferences/preferences.dm b/code/modules/client/preferences/preferences.dm index 816570b841943..f490c085c317c 100644 --- a/code/modules/client/preferences/preferences.dm +++ b/code/modules/client/preferences/preferences.dm @@ -139,10 +139,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) ui.set_autoupdate(FALSE) ui.open() - // HACK: Without this the character starts out really tiny because of some BYOND bug. - // You can fix it by changing a preference, so let's just forcably update the body to emulate this. - addtimer(CALLBACK(character_preview_view, /atom/movable/screen/character_preview_view/proc/update_body), 1 SECONDS) - /datum/preferences/ui_state(mob/user) return GLOB.always_state From 7c2c4bf6fd043d1e79f89b247c89f736c045f382 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Sat, 10 Jun 2023 03:10:49 -0400 Subject: [PATCH 108/269] Update SQL Schema --- SQL/beestation_schema.sql | 84 +++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 30 deletions(-) diff --git a/SQL/beestation_schema.sql b/SQL/beestation_schema.sql index 7b17041f18af8..848ade9fa208a 100644 --- a/SQL/beestation_schema.sql +++ b/SQL/beestation_schema.sql @@ -84,36 +84,60 @@ DROP TABLE IF EXISTS `SS13_characters`; CREATE TABLE IF NOT EXISTS `SS13_characters` ( `slot` INT(11) UNSIGNED NOT NULL, `ckey` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci', - `species` VARCHAR(32) NOT NULL COLLATE 'utf8mb4_general_ci', - `real_name` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci', - `name_is_always_random` TINYINT(1) NOT NULL, - `body_is_always_random` TINYINT(1) NOT NULL, - `gender` VARCHAR(16) NOT NULL COLLATE 'utf8mb4_general_ci', - `age` TINYINT(3) UNSIGNED NOT NULL, - `hair_color` VARCHAR(8) NOT NULL COLLATE 'utf8mb4_general_ci', - `gradient_color` VARCHAR(8) NOT NULL COLLATE 'utf8mb4_general_ci', - `facial_hair_color` VARCHAR(8) NOT NULL COLLATE 'utf8mb4_general_ci', - `eye_color` VARCHAR(8) NOT NULL COLLATE 'utf8mb4_general_ci', - `skin_tone` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci', - `hair_style_name` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci', - `gradient_style` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci', - `facial_style_name` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci', - `underwear` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci', - `underwear_color` VARCHAR(32) NOT NULL COLLATE 'utf8mb4_general_ci', - `undershirt` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci', - `socks` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci', - `backbag` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci', - `jumpsuit_style` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci', - `uplink_loc` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci', - `features` MEDIUMTEXT NOT NULL COLLATE 'utf8mb4_general_ci', - `custom_names` MEDIUMTEXT NOT NULL COLLATE 'utf8mb4_general_ci', - `helmet_style` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci', - `preferred_ai_core_display` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci', - `preferred_security_department` VARCHAR(32) NOT NULL COLLATE 'utf8mb4_general_ci', - `joblessrole` TINYINT(4) UNSIGNED NOT NULL, - `job_preferences` MEDIUMTEXT NOT NULL COLLATE 'utf8mb4_general_ci', - `all_quirks` MEDIUMTEXT NOT NULL COLLATE 'utf8mb4_general_ci', - `equipped_gear` MEDIUMTEXT NOT NULL COLLATE 'utf8mb4_general_ci', + `species` VARCHAR(32) COLLATE 'utf8mb4_general_ci', + `real_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `human_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `mime_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `clown_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `cyborg_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `ai_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `religion_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `deity_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `bible_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `name_is_always_random` TINYINT(1), + `body_is_always_random` TINYINT(1), + `gender` VARCHAR(16) COLLATE 'utf8mb4_general_ci', + `body_type` VARCHAR(16) COLLATE 'utf8mb4_general_ci', + `age` TINYINT(3) UNSIGNED, + `hair_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci', + `gradient_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci', + `facial_hair_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci', + `eye_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci', + `skin_tone` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `hair_style_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `gradient_style` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `facial_style_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `underwear` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `underwear_color` VARCHAR(32) COLLATE 'utf8mb4_general_ci', + `undershirt` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `socks` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `backbag` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `jumpsuit_style` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `uplink_loc` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `features` MEDIUMTEXT COLLATE 'utf8mb4_general_ci', + `feature_moth_antennae` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `feature_moth_markings` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `feature_moth_wings` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `feature_ethcolor` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `feature_lizard_body_markings` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `feature_lizard_frills` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `feature_lizard_horns` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `feature_lizard_legs` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `feature_lizard_snout` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `feature_lizard_spines` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `feature_lizard_tail` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `feature_mcolor` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `feature_human_tail` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `feature_human_ears` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `custom_names` MEDIUMTEXT COLLATE 'utf8mb4_general_ci', + `helmet_style` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `preferred_ai_core_display` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `preferred_security_department` VARCHAR(32) COLLATE 'utf8mb4_general_ci', + `joblessrole` TINYINT(4) UNSIGNED, + `job_preferences` MEDIUMTEXT COLLATE 'utf8mb4_general_ci', + `all_quirks` MEDIUMTEXT COLLATE 'utf8mb4_general_ci', + `equipped_gear` MEDIUMTEXT COLLATE 'utf8mb4_general_ci', + `randomise` MEDIUMTEXT COLLATE 'utf8mb4_general_ci', PRIMARY KEY (`slot`, `ckey`) USING BTREE ) COLLATE='utf8mb4_general_ci' ENGINE=InnoDB; From 7e4b49c95423708d1f88cacc544c5931f19615fc Mon Sep 17 00:00:00 2001 From: itsmeow Date: Sat, 10 Jun 2023 03:10:58 -0400 Subject: [PATCH 109/269] Loadout and dummy fixes --- beestation.dme | 2 +- code/modules/client/loadout/loadout.dm | 4 ++++ code/modules/client/loadout/loadout_ooc.dm | 2 ++ .../client/preferences/middleware/loadout.dm | 4 +++- .../preferences/preference_setup.dm} | 11 +++++------ code/modules/client/preferences/preferences.dm | 1 + code/modules/jobs/job_types/_job.dm | 2 ++ code/modules/mob/living/carbon/human/dummy.dm | 3 +++ .../interfaces/PreferencesMenu/LoadoutPage.tsx | 18 +++++++++++------- .../tgui/interfaces/PreferencesMenu/data.ts | 2 ++ 10 files changed, 34 insertions(+), 15 deletions(-) rename code/modules/{mob/dead/new_player/preferences_setup.dm => client/preferences/preference_setup.dm} (81%) diff --git a/beestation.dme b/beestation.dme index 9563632a24401..9834e63e54c24 100644 --- a/beestation.dme +++ b/beestation.dme @@ -2136,6 +2136,7 @@ #include "code\modules\client\loadout\loadout_uniform.dm" #include "code\modules\client\preferences\preference_assets.dm" #include "code\modules\client\preferences\preference_entry.dm" +#include "code\modules\client\preferences\preference_setup.dm" #include "code\modules\client\preferences\preference_verbs.dm" #include "code\modules\client\preferences\preferences.dm" #include "code\modules\client\preferences\entries\admin.dm" @@ -2720,7 +2721,6 @@ #include "code\modules\mob\dead\new_player\logout.dm" #include "code\modules\mob\dead\new_player\new_player.dm" #include "code\modules\mob\dead\new_player\poll.dm" -#include "code\modules\mob\dead\new_player\preferences_setup.dm" #include "code\modules\mob\dead\new_player\sprite_accessories.dm" #include "code\modules\mob\dead\observer\login.dm" #include "code\modules\mob\dead\observer\logout.dm" diff --git a/code/modules/client/loadout/loadout.dm b/code/modules/client/loadout/loadout.dm index 95e02da80ba9f..7fbf25d69755a 100644 --- a/code/modules/client/loadout/loadout.dm +++ b/code/modules/client/loadout/loadout.dm @@ -61,6 +61,10 @@ GLOBAL_LIST_EMPTY(gear_datums) var/skirt_display_name var/skirt_path = null var/skirt_description + /// If this gear is actually granting an item, and can be equipped. + var/is_equippable = TRUE + /// If this gear can be purchased again - used for non-items + var/multi_purchase = FALSE /datum/gear/New() ..() diff --git a/code/modules/client/loadout/loadout_ooc.dm b/code/modules/client/loadout/loadout_ooc.dm index 2c5e0d2544e63..5d0557a93df4c 100644 --- a/code/modules/client/loadout/loadout_ooc.dm +++ b/code/modules/client/loadout/loadout_ooc.dm @@ -2,6 +2,7 @@ subtype_path = /datum/gear/ooc sort_category = "OOC" cost = 10000 + is_equippable = FALSE /datum/gear/ooc/char_slot display_name = "extra character slot" @@ -18,6 +19,7 @@ description = "If you can afford it, you deserve it." cost = 100000 path = /obj/item/coin/antagtoken + multi_purchase = TRUE /datum/gear/ooc/real_antagtoken/purchase(var/client/C) INVOKE_ASYNC(C, TYPE_PROC_REF(/client, inc_antag_token_count), 1) diff --git a/code/modules/client/preferences/middleware/loadout.dm b/code/modules/client/preferences/middleware/loadout.dm index 0655738ff9000..d7680eb0efeb2 100644 --- a/code/modules/client/preferences/middleware/loadout.dm +++ b/code/modules/client/preferences/middleware/loadout.dm @@ -31,6 +31,8 @@ gear_entry["donator"] = G.sort_category == "Donator" gear_entry["cost"] = G.cost gear_entry["allowed_roles"] = G.allowed_roles + gear_entry["is_equippable"] = G.is_equippable + gear_entry["multi_purchase"] = G.multi_purchase gear += list(gear_entry) category["gear"] = gear categories += list(category) @@ -42,7 +44,7 @@ var/datum/gear/TG = GLOB.gear_datums[params["id"]] if(!istype(TG)) return - if(TG.id in preferences.equipped_gear) + if(((TG.id in preferences.purchased_gear) || (TG.id in preferences.equipped_gear)) && !TG.multi_purchase) to_chat(user, "You already own \the [TG.display_name]!") return TRUE if(TG.sort_category == "Donator") diff --git a/code/modules/mob/dead/new_player/preferences_setup.dm b/code/modules/client/preferences/preference_setup.dm similarity index 81% rename from code/modules/mob/dead/new_player/preferences_setup.dm rename to code/modules/client/preferences/preference_setup.dm index bbfa67b9f7a29..a87f66b8d692b 100644 --- a/code/modules/mob/dead/new_player/preferences_setup.dm +++ b/code/modules/client/preferences/preference_setup.dm @@ -36,12 +36,11 @@ /datum/preferences/proc/render_new_preview_appearance(mob/living/carbon/human/dummy/mannequin) var/datum/job/preview_job = get_highest_priority_job() - if(preview_job) - // Silicons only need a very basic preview since there is no customization for them. - if (istype(preview_job, /datum/job/ai)) - return image('icons/mob/ai.dmi', icon_state = resolve_ai_icon(read_character_preference(/datum/preference/choiced/ai_core_display)), dir = SOUTH) - if (istype(preview_job, /datum/job/cyborg)) - return image('icons/mob/robots.dmi', icon_state = "robot", dir = SOUTH) + // Silicons only need a very basic preview since there is no customization for them. + if (istype(preview_job, /datum/job/ai)) + return image('icons/mob/ai.dmi', icon_state = resolve_ai_icon(read_character_preference(/datum/preference/choiced/ai_core_display)), dir = SOUTH) + if (istype(preview_job, /datum/job/cyborg)) + return image('icons/mob/robots.dmi', icon_state = "robot", dir = SOUTH) // Set up the dummy for its photoshoot apply_prefs_to(mannequin, TRUE) diff --git a/code/modules/client/preferences/preferences.dm b/code/modules/client/preferences/preferences.dm index f490c085c317c..85ee88464fe71 100644 --- a/code/modules/client/preferences/preferences.dm +++ b/code/modules/client/preferences/preferences.dm @@ -406,6 +406,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/character_preview_view) // Without this, it doesn't show up in the menu body.appearance_flags &= ~KEEP_TOGETHER + body.wipe_state() // cleanup the body immediately since it spawns with overlays, AI and cyborgs will retain them. vis_contents += body /// Registers the relevant map objects to a client diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm index 63e0faa77ebb0..c0d1966dabaae 100644 --- a/code/modules/jobs/job_types/_job.dm +++ b/code/modules/jobs/job_types/_job.dm @@ -130,6 +130,8 @@ for(var/gear in preference_source.prefs.equipped_gear) var/datum/gear/G = GLOB.gear_datums[gear] if(G) + if(!G.is_equippable) + continue var/permitted = FALSE if(G.allowed_roles && H.mind && (H.mind.assigned_role in G.allowed_roles)) diff --git a/code/modules/mob/living/carbon/human/dummy.dm b/code/modules/mob/living/carbon/human/dummy.dm index 04aa0d070a062..92a2678570570 100644 --- a/code/modules/mob/living/carbon/human/dummy.dm +++ b/code/modules/mob/living/carbon/human/dummy.dm @@ -17,6 +17,9 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy) /mob/living/carbon/human/dummy/proc/wipe_state() delete_equipment() cut_overlays() + // Wipe anything from custom icon appearances (AI/cyborg) + icon = initial(icon) + icon_state = initial(icon_state) /mob/living/carbon/human/dummy/setup_human_dna() create_dna(src) diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx index 1d86d1e0f5521..892d49fe70b7d 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx @@ -1,5 +1,5 @@ import { Box, Tabs, Button, Section, Stack, Flex, Table, Icon } from '../../components'; -import { PreferencesMenuData } from './data'; +import { LoadoutGear, PreferencesMenuData } from './data'; import { useBackend, useLocalState } from '../../backend'; import { ServerPreferencesFetcher } from './ServerPreferencesFetcher'; import { CharacterPreview } from './CharacterPreview'; @@ -20,6 +20,8 @@ export const LoadoutPage = (props, context) => { let selectedCategoryObject = categories.filter((c) => c.name === selectedCategory)[0]; + const isPurchased = (gear: LoadoutGear) => purchased_gear.includes(gear.id) && !gear.multi_purchase; + return ( @@ -82,7 +84,7 @@ export const LoadoutPage = (props, context) => { {selectedCategoryObject && selectedCategoryObject.gear - .filter((gear) => !onlyPurchased || purchased_gear.includes(gear.id)) + .filter((gear) => !onlyPurchased || isPurchased(gear)) .map((gear) => ( @@ -102,12 +104,12 @@ export const LoadoutPage = (props, context) => { ? gear.skirt_display_name : gear.display_name} - {!gear.donator && {gear.cost}} + - + ))} - + ); }; @@ -110,11 +113,12 @@ export const CharacterPreferenceWindow = (props, context) => { }> - - + + { act('change_slot', { slot: slot + 1, @@ -122,13 +126,13 @@ export const CharacterPreferenceWindow = (props, context) => { }} profiles={data.character_profiles} /> - + - {!data.content_unlocked && Buy BYOND premium for more slots!} + + + - - - + { - + - + + + - {pageContents} - + {pageContents} + ); diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx index a3a401c6dc7e7..bcc619cdcade1 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx @@ -40,7 +40,7 @@ export const LoadoutPage = (props, context) => { {currency_text} - + From 6e77ee7f82e8de3e50d3c730e33f7675a9bdab0a Mon Sep 17 00:00:00 2001 From: itsmeow Date: Fri, 16 Jun 2023 08:54:48 -0400 Subject: [PATCH 146/269] Gamemode runtime fix --- code/game/gamemodes/game_mode.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index e9545d1845152..85f21482a35f9 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -513,8 +513,8 @@ for(var/mob/dead/new_player/player in players) if(player.client && player.ready == PLAYER_READY_TO_PLAY) - if(role_preference_enabled(player.client, role_preference)) - if(!is_banned_from(player.ckey, banning_key) && !QDELETED(player)) + if(!role_preference || role_preference_enabled(player.client, role_preference)) + if((!banning_key || !is_banned_from(player.ckey, banning_key)) && !QDELETED(player)) candidates += player.mind // Get a list of all the people who want to be the antagonist for this round if(restricted_jobs) From 03ea7fd47ded5d4b9d7746df422bb2628801a84e Mon Sep 17 00:00:00 2001 From: itsmeow Date: Fri, 16 Jun 2023 08:55:39 -0400 Subject: [PATCH 147/269] Body size and HUD change fix --- SQL/beestation_schema.sql | 3 ++- code/_globalvars/lists/flavor_misc.dm | 2 +- code/_onclick/hud/hud.dm | 6 ++++++ code/datums/dna.dm | 2 +- .../preferences/entries/character/body_type.dm | 16 +++++++++++++++- code/modules/client/preferences/preferences.dm | 1 + .../submodules/preference_character_preview.dm | 3 +++ .../features/character_preferences/body_type.tsx | 5 +++++ 8 files changed, 34 insertions(+), 4 deletions(-) diff --git a/SQL/beestation_schema.sql b/SQL/beestation_schema.sql index 7ae52554572c0..350d6b41625a1 100644 --- a/SQL/beestation_schema.sql +++ b/SQL/beestation_schema.sql @@ -98,6 +98,7 @@ CREATE TABLE IF NOT EXISTS `SS13_characters` ( `body_is_always_random` TINYINT(1), `gender` VARCHAR(16) COLLATE 'utf8mb4_general_ci', `body_type` VARCHAR(16) COLLATE 'utf8mb4_general_ci', + `body_size` VARCHAR(16) COLLATE 'utf8mb4_general_ci', `age` TINYINT(3) UNSIGNED, `hair_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci', `gradient_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci', @@ -108,7 +109,7 @@ CREATE TABLE IF NOT EXISTS `SS13_characters` ( `gradient_style` VARCHAR(64) COLLATE 'utf8mb4_general_ci', `facial_style_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci', `underwear` VARCHAR(64) COLLATE 'utf8mb4_general_ci', - `underwear_color` VARCHAR(32) COLLATE 'utf8mb4_general_ci', + `underwear_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci', `undershirt` VARCHAR(64) COLLATE 'utf8mb4_general_ci', `socks` VARCHAR(64) COLLATE 'utf8mb4_general_ci', `backbag` VARCHAR(64) COLLATE 'utf8mb4_general_ci', diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm index 3f1623c1f6bf7..8f91b2a567bec 100644 --- a/code/_globalvars/lists/flavor_misc.dm +++ b/code/_globalvars/lists/flavor_misc.dm @@ -17,7 +17,7 @@ GLOBAL_LIST_EMPTY(undershirt_m) //stores only undershirt name GLOBAL_LIST_EMPTY(undershirt_f) //stores only undershirt name //Socks GLOBAL_LIST_EMPTY(socks_list) //stores /datum/sprite_accessory/socks indexed by name - //Body Sizes +/// Body sizes. The names (keys) are what is actually stored in the database. Don't get crazy with changing them. GLOBAL_LIST_INIT(body_sizes, list( "Normal" = BODY_SIZE_NORMAL, "Short" = BODY_SIZE_SHORT, diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index d17a4653273ca..ed6b25892f5b9 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -143,6 +143,9 @@ GLOBAL_LIST_INIT(available_ui_styles, list( if(!screenmob.client) return FALSE + if(screenmob.client.prefs?.character_preview_view) // Changing HUDs clears the screen, we need to reregister then. + screenmob.client.prefs.character_preview_view.unregister_from_client(screenmob.client) + screenmob.client.screen = list() screenmob.client.apply_clickcatcher() @@ -221,6 +224,9 @@ GLOBAL_LIST_INIT(available_ui_styles, list( else if (viewmob.hud_used) viewmob.hud_used.plane_masters_update() + if(screenmob.client.prefs?.character_preview_view) // Changing HUDs clears the screen, we need to reregister then. + screenmob.client.prefs.character_preview_view.register_to_client(screenmob.client) + return TRUE /datum/hud/proc/plane_masters_update() diff --git a/code/datums/dna.dm b/code/datums/dna.dm index bfd8318fd8dd1..c6a4431b210c0 100644 --- a/code/datums/dna.dm +++ b/code/datums/dna.dm @@ -302,7 +302,7 @@ var/desired_size = GLOB.body_sizes[features["body_size"]] - if(desired_size == current_body_size) + if(desired_size == current_body_size || current_body_size == 0) return var/change_multiplier = desired_size / current_body_size diff --git a/code/modules/client/preferences/entries/character/body_type.dm b/code/modules/client/preferences/entries/character/body_type.dm index d6abdfd08bbbe..b317f86586798 100644 --- a/code/modules/client/preferences/entries/character/body_type.dm +++ b/code/modules/client/preferences/entries/character/body_type.dm @@ -1,5 +1,5 @@ /datum/preference/choiced/body_type - category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES priority = PREFERENCE_PRIORITY_BODY_TYPE db_key = "body_type" preference_type = PREFERENCE_CHARACTER @@ -19,3 +19,17 @@ var/gender = preferences.read_character_preference(/datum/preference/choiced/gender) return gender != MALE && gender != FEMALE + +/datum/preference/choiced/body_size + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + db_key = "body_size" + preference_type = PREFERENCE_CHARACTER + +/datum/preference/choiced/body_size/init_possible_values() + return assoc_to_keys(GLOB.body_sizes) + +/datum/preference/choiced/body_size/create_default_value() + return "Normal" + +/datum/preference/choiced/body_size/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["body_size"] = value diff --git a/code/modules/client/preferences/preferences.dm b/code/modules/client/preferences/preferences.dm index 6a495917c2448..0dbf67e0fff6c 100644 --- a/code/modules/client/preferences/preferences.dm +++ b/code/modules/client/preferences/preferences.dm @@ -379,3 +379,4 @@ GLOBAL_LIST_EMPTY(preferences_datums) character.update_body() character.update_hair() character.update_body_parts() + character.dna.update_body_size() diff --git a/code/modules/client/preferences/submodules/preference_character_preview.dm b/code/modules/client/preferences/submodules/preference_character_preview.dm index 328332a2dd470..d201867fa79fe 100644 --- a/code/modules/client/preferences/submodules/preference_character_preview.dm +++ b/code/modules/client/preferences/submodules/preference_character_preview.dm @@ -12,6 +12,9 @@ // Set up the dummy for its photoshoot apply_prefs_to(mannequin, TRUE) + // Normalize size, since it doesn't scale properly in the preview. + mannequin.dna.features["body_size"] = "Normal" + mannequin.dna.update_body_size() if(preview_job) mannequin.job = preview_job.title diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/body_type.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/body_type.tsx index de9d6523e2c42..7ffb5b1085db3 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/body_type.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/body_type.tsx @@ -4,3 +4,8 @@ export const body_type: FeatureChoiced = { name: 'Body type', component: FeatureDropdownInput, }; + +export const body_size: FeatureChoiced = { + name: 'Body size', + component: FeatureDropdownInput, +}; From e956616225e3810b5cf3f47553801756cd19038f Mon Sep 17 00:00:00 2001 From: itsmeow Date: Fri, 16 Jun 2023 08:57:38 -0400 Subject: [PATCH 148/269] No katana for ninja --- code/modules/antagonists/role_preference/role_midrounds.dm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/modules/antagonists/role_preference/role_midrounds.dm b/code/modules/antagonists/role_preference/role_midrounds.dm index bb4a655342e85..874fad0c1093d 100644 --- a/code/modules/antagonists/role_preference/role_midrounds.dm +++ b/code/modules/antagonists/role_preference/role_midrounds.dm @@ -240,7 +240,8 @@ head = /obj/item/clothing/head/helmet/space/space_ninja gloves = /obj/item/clothing/gloves/space_ninja back = /obj/item/tank/jetpack/carbondioxide - belt = /obj/item/energy_katana + // No katana because it has trouble GCing + //belt = /obj/item/energy_katana /datum/role_preference/midround_living/malfunctioning_ai name = "Malfunctioning AI" From 4c2c1acb0e656dbc23a9b0f4cb5b6faf073db1e7 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Sat, 17 Jun 2023 03:42:05 -0400 Subject: [PATCH 149/269] IPC settings (warning extremely cursed) --- SQL/beestation_schema.sql | 3 + beestation.dme | 1 + .../character/species_features/basic.dm | 5 + .../entries/character/species_features/ipc.dm | 116 ++++++++++++++++++ .../mob/dead/new_player/sprite_accessories.dm | 2 +- 5 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 code/modules/client/preferences/entries/character/species_features/ipc.dm diff --git a/SQL/beestation_schema.sql b/SQL/beestation_schema.sql index 350d6b41625a1..56facfd60ea44 100644 --- a/SQL/beestation_schema.sql +++ b/SQL/beestation_schema.sql @@ -122,6 +122,9 @@ CREATE TABLE IF NOT EXISTS `SS13_characters` ( `feature_moth_markings` VARCHAR(64) COLLATE 'utf8mb4_general_ci', `feature_moth_wings` VARCHAR(64) COLLATE 'utf8mb4_general_ci', `feature_ethcolor` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `feature_ipc_screen` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `feature_ipc_antenna` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `feature_ipc_chassis` VARCHAR(64) COLLATE 'utf8mb4_general_ci', `feature_lizard_body_markings` VARCHAR(64) COLLATE 'utf8mb4_general_ci', `feature_lizard_frills` VARCHAR(64) COLLATE 'utf8mb4_general_ci', `feature_lizard_horns` VARCHAR(64) COLLATE 'utf8mb4_general_ci', diff --git a/beestation.dme b/beestation.dme index ebca604665583..876ac1344bdaa 100644 --- a/beestation.dme +++ b/beestation.dme @@ -2150,6 +2150,7 @@ #include "code\modules\client\preferences\entries\character\species_features\basic.dm" #include "code\modules\client\preferences\entries\character\species_features\ethereal.dm" #include "code\modules\client\preferences\entries\character\species_features\felinid.dm" +#include "code\modules\client\preferences\entries\character\species_features\ipc.dm" #include "code\modules\client\preferences\entries\character\species_features\lizard.dm" #include "code\modules\client\preferences\entries\character\species_features\moth.dm" #include "code\modules\client\preferences\entries\character\species_features\mutants.dm" diff --git a/code/modules/client/preferences/entries/character/species_features/basic.dm b/code/modules/client/preferences/entries/character/species_features/basic.dm index f984e9ba2cd39..ac41f49c07873 100644 --- a/code/modules/client/preferences/entries/character/species_features/basic.dm +++ b/code/modules/client/preferences/entries/character/species_features/basic.dm @@ -79,6 +79,11 @@ /datum/preference/color_legacy/hair_color/apply_to_human(mob/living/carbon/human/target, value) target.hair_color = value +/datum/preference/color_legacy/hair_color/is_accessible(datum/preferences/preferences, ignore_page) + if (!ignore_page && !should_show_on_page(preferences.current_window)) + return FALSE + return ..() || ispath(preferences.read_character_preference(/datum/preference/choiced/species), /datum/species/ipc) + /datum/preference/choiced/hairstyle db_key = "hair_style_name" preference_type = PREFERENCE_CHARACTER diff --git a/code/modules/client/preferences/entries/character/species_features/ipc.dm b/code/modules/client/preferences/entries/character/species_features/ipc.dm new file mode 100644 index 0000000000000..852a0c09e6395 --- /dev/null +++ b/code/modules/client/preferences/entries/character/species_features/ipc.dm @@ -0,0 +1,116 @@ +/datum/preference/choiced/ipc_screen + db_key = "feature_ipc_screen" + preference_type = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_FEATURES + main_feature_name = "Screen Style" + should_generate_icons = TRUE + relevant_mutant_bodypart = "ipc_screen" + +/datum/preference/choiced/ipc_screen/init_possible_values() + var/list/values = list() + + for (var/screen_name in GLOB.ipc_screens_list) + var/datum/sprite_accessory/screen = GLOB.ipc_screens_list[screen_name] + + var/icon/icon_with_screen = icon('icons/mob/species/ipc/bodyparts.dmi', "mcgipc_head", dir = SOUTH) + if (screen.icon_state != "none") + var/icon/screen_icon = icon(screen.icon, "m_ipc_screen_[screen.icon_state]_ADJ", dir = SOUTH) + icon_with_screen.Blend(screen_icon, ICON_OVERLAY) + icon_with_screen.Scale(64, 64) + icon_with_screen.Crop(15, 64, 15 + 31, 64 - 31) + + values[screen.name] = icon_with_screen + + return values + +/datum/preference/choiced/ipc_screen/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["ipc_screen"] = value + +/datum/preference/choiced/ipc_screen/compile_constant_data() + var/list/data = ..() + + data[SUPPLEMENTAL_FEATURE_KEY] = "eye_color" + + return data + +/// God forgive me for this +/datum/preference/color_legacy/eye_color/ipc + db_key = "eye_color" + category = PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES + relevant_species_trait = null + +/datum/preference/color_legacy/eye_color/ipc/is_accessible(datum/preferences/preferences, ignore_page) + return ..() && ispath(preferences.read_character_preference(/datum/preference/choiced/species), /datum/species/ipc) + +/datum/preference/choiced/ipc_antenna + db_key = "feature_ipc_antenna" + preference_type = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_FEATURES + main_feature_name = "Antenna Style" + should_generate_icons = TRUE + relevant_mutant_bodypart = "ipc_antenna" + +/datum/preference/choiced/ipc_antenna/init_possible_values() + var/list/values = list() + + for (var/antenna_name in GLOB.ipc_antennas_list) + var/datum/sprite_accessory/antenna = GLOB.ipc_antennas_list[antenna_name] + + var/icon/icon_with_antennae = icon('icons/mob/species/ipc/bodyparts.dmi', "mcgipc_head", dir = SOUTH) + if (antenna.icon_state != "none") + // weird snowflake shit + var/side = (antenna_name == "Light" || antenna_name == "Drone Eyes") ? "FRONT" : "ADJ" + var/icon/antenna_icon = icon(antenna.icon, "m_ipc_antenna_[antenna.icon_state]_[side]", dir = SOUTH) + icon_with_antennae.Blend(antenna_icon, ICON_OVERLAY) + icon_with_antennae.Scale(64, 64) + icon_with_antennae.Crop(15, 64, 15 + 31, 64 - 31) + + values[antenna.name] = icon_with_antennae + + return values + +/datum/preference/choiced/ipc_antenna/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["ipc_antenna"] = value + +/datum/preference/choiced/ipc_antenna/compile_constant_data() + var/list/data = ..() + + data[SUPPLEMENTAL_FEATURE_KEY] = "hair_color" + + return data + +/datum/preference/choiced/ipc_chassis + db_key = "feature_ipc_chassis" + preference_type = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_FEATURES + main_feature_name = "Chassis Style" + should_generate_icons = TRUE + relevant_mutant_bodypart = "ipc_chassis" + +/datum/preference/choiced/ipc_chassis/init_possible_values() + var/list/values = list() + var/list/body_parts = list( + BODY_ZONE_HEAD, + BODY_ZONE_CHEST, + BODY_ZONE_L_ARM, + BODY_ZONE_R_ARM, + BODY_ZONE_L_LEG, + BODY_ZONE_R_LEG, + ) + for (var/chassis_name in GLOB.ipc_chassis_list) + var/datum/sprite_accessory/chassis = GLOB.ipc_chassis_list[chassis_name] + var/icon/icon_with_chassis = icon('icons/effects/effects.dmi', "nothing") + + for (var/body_part in body_parts) + icon_with_chassis.Blend(icon('icons/mob/species/ipc/bodyparts.dmi', "[chassis.limbs_id]_[body_part]", dir = SOUTH), ICON_OVERLAY) + + // Zoom in + icon_with_chassis.Scale(64, 64) + icon_with_chassis.Crop(15, 64, 15 + 31, 64 - 31) + + values[chassis.name] = icon_with_chassis + + return values + +/datum/preference/choiced/ipc_chassis/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["ipc_chassis"] = value diff --git a/code/modules/mob/dead/new_player/sprite_accessories.dm b/code/modules/mob/dead/new_player/sprite_accessories.dm index 3c50479ebd544..da363e7658e9b 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories.dm @@ -2401,7 +2401,7 @@ /datum/sprite_accessory/ipc_antennas/none name = "None" - icon_state = "None" + icon_state = "none" /datum/sprite_accessory/ipc_antennas/angled name = "Angled" From 27446a405ca21688f8a8442db877c5b9977f4b96 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Sat, 17 Jun 2023 03:54:12 -0400 Subject: [PATCH 150/269] IPC fixes and new negative trait listing --- .../preferences/entries/character/species_features/ipc.dm | 6 ++++-- code/modules/mob/living/carbon/human/species_types/IPC.dm | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/code/modules/client/preferences/entries/character/species_features/ipc.dm b/code/modules/client/preferences/entries/character/species_features/ipc.dm index 852a0c09e6395..7ea48d8fbe04c 100644 --- a/code/modules/client/preferences/entries/character/species_features/ipc.dm +++ b/code/modules/client/preferences/entries/character/species_features/ipc.dm @@ -37,10 +37,12 @@ /datum/preference/color_legacy/eye_color/ipc db_key = "eye_color" category = PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES - relevant_species_trait = null + relevant_species_trait = EYECOLOR /datum/preference/color_legacy/eye_color/ipc/is_accessible(datum/preferences/preferences, ignore_page) - return ..() && ispath(preferences.read_character_preference(/datum/preference/choiced/species), /datum/species/ipc) + if (!ignore_page && !should_show_on_page(preferences.current_window)) + return FALSE + return ..() || ispath(preferences.read_character_preference(/datum/preference/choiced/species), /datum/species/ipc) /datum/preference/choiced/ipc_antenna db_key = "feature_ipc_antenna" diff --git a/code/modules/mob/living/carbon/human/species_types/IPC.dm b/code/modules/mob/living/carbon/human/species_types/IPC.dm index 722fdb841e61e..ddaad70935186 100644 --- a/code/modules/mob/living/carbon/human/species_types/IPC.dm +++ b/code/modules/mob/living/carbon/human/species_types/IPC.dm @@ -273,6 +273,12 @@ SPECIES_PERK_DESC = "IPCs have an entirely robotic body, meaning medical care is typically done through Robotics or Engineering. \ Whether this is helpful or not is heavily dependent on your coworkers. It does, however, mean you are usually able to perform self-repairs easily.", ), + list( + SPECIES_PERK_TYPE = SPECIES_NEGATIVE_PERK, + SPECIES_PERK_ICON = "magnet", + SPECIES_PERK_NAME = "EMP Vulnerable", + SPECIES_PERK_DESC = "IPC organs are cybernetic, and thus susceptible to electromagnetic interference. Getting hit by an EMP may stop your heart.", + ), ) return to_add From 21844b4dc1d8468c6d7aa97532b5f4eef535f8c8 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Sat, 17 Jun 2023 04:15:28 -0400 Subject: [PATCH 151/269] Fix loadout page not showing multipurchase items as too expensive --- tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx index bcc619cdcade1..c295d0bce27a8 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx @@ -113,12 +113,12 @@ export const LoadoutPage = (props, context) => { + }} + /> ); })} + {!currentSpecies.selectable && ( + ); diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx similarity index 59% rename from tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx rename to tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx index c1d0b6ba60062..47889aa291c8d 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx @@ -1,4 +1,4 @@ -import { FeatureColorInput, Feature, FeatureChoiced, FeatureDropdownInput } from './base'; +import { FeatureColorInput, Feature, FeatureChoiced, FeatureDropdownInput } from '../base'; export const eye_color: Feature = { name: 'Eye color', @@ -20,31 +20,6 @@ export const gradient_color: Feature = { component: FeatureColorInput, }; -export const feature_human_ears: FeatureChoiced = { - name: 'Ears', - component: FeatureDropdownInput, -}; - -export const feature_human_tail: FeatureChoiced = { - name: 'Tail', - component: FeatureDropdownInput, -}; - -export const feature_lizard_legs: FeatureChoiced = { - name: 'Legs', - component: FeatureDropdownInput, -}; - -export const feature_lizard_spines: FeatureChoiced = { - name: 'Spines', - component: FeatureDropdownInput, -}; - -export const feature_lizard_tail: FeatureChoiced = { - name: 'Tail', - component: FeatureDropdownInput, -}; - export const feature_mcolor: Feature = { name: 'Mutant color', component: FeatureColorInput, From 46b042929ebb465b057c773695d89ca513845390 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Sat, 17 Jun 2023 18:39:19 -0400 Subject: [PATCH 163/269] Improve popup window themeing, fix outside click tracker not properly unmounting --- .../tgui/components/TrackOutsideClicks.tsx | 5 ++++- .../PreferencesMenu/KeybindingsPage.tsx | 4 +++- .../interfaces/PreferencesMenu/MainPage.tsx | 5 ++--- .../tgui/interfaces/PreferencesMenu/names.tsx | 2 +- tgui/packages/tgui/styles/layouts/Layout.scss | 18 +++++++++++------- .../tgui/styles/layouts/PopupWindow.scss | 14 ++++++++++++++ tgui/packages/tgui/styles/main.scss | 1 + tgui/packages/tgui/styles/themes/generic.scss | 1 + 8 files changed, 37 insertions(+), 13 deletions(-) create mode 100644 tgui/packages/tgui/styles/layouts/PopupWindow.scss diff --git a/tgui/packages/tgui/components/TrackOutsideClicks.tsx b/tgui/packages/tgui/components/TrackOutsideClicks.tsx index 7b3914ecb271d..e19a6a3753370 100644 --- a/tgui/packages/tgui/components/TrackOutsideClicks.tsx +++ b/tgui/packages/tgui/components/TrackOutsideClicks.tsx @@ -2,6 +2,7 @@ import { Component, createRef } from 'inferno'; export class TrackOutsideClicks extends Component<{ onOutsideClick: () => void; + removeOnOutsideClick?: boolean; }> { ref = createRef(); @@ -9,7 +10,6 @@ export class TrackOutsideClicks extends Component<{ super(); this.handleOutsideClick = this.handleOutsideClick.bind(this); - document.addEventListener('click', this.handleOutsideClick); } @@ -24,6 +24,9 @@ export class TrackOutsideClicks extends Component<{ if (this.ref.current && !this.ref.current.contains(event.target)) { this.props.onOutsideClick(); + if (this.props.removeOnOutsideClick) { + document.removeEventListener('click', this.handleOutsideClick); + } } } diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx index 1ae40b59ee104..350ae46e2a9e8 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx @@ -125,7 +125,9 @@ class KeybindingButton extends Component<{ if (typingHotkey && onClick) { return ( // onClick will cancel it - {child} + + {child} + ); } else { return child; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx index d0c3aa2930bf8..f86cb466239fb 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx @@ -67,10 +67,9 @@ const ChoicedSelection = ( return ( @@ -226,7 +225,7 @@ const MainFeature = ( }} popperContent={ isOpen && ( - + - +
    diff --git a/tgui/packages/tgui/styles/layouts/Layout.scss b/tgui/packages/tgui/styles/layouts/Layout.scss index ae2fea1a2e7a7..41c4394ff98a4 100644 --- a/tgui/packages/tgui/styles/layouts/Layout.scss +++ b/tgui/packages/tgui/styles/layouts/Layout.scss @@ -8,16 +8,20 @@ $scrollbar-color-multiplier: 1 !default; +@mixin fancy-scrollbar($base-color, $color-multiplier) { + scrollbar-base-color: color.scale($base-color, $lightness: -25% * $color-multiplier); + scrollbar-face-color: color.scale($base-color, $lightness: 10% * $color-multiplier); + scrollbar-3dlight-color: color.scale($base-color, $lightness: 0% * $color-multiplier); + scrollbar-highlight-color: color.scale($base-color, $lightness: 0% * $color-multiplier); + scrollbar-track-color: color.scale($base-color, $lightness: -25% * $color-multiplier); + scrollbar-arrow-color: color.scale($base-color, $lightness: 50% * $color-multiplier); + scrollbar-shadow-color: color.scale($base-color, $lightness: 10% * $color-multiplier); +} + .Layout, .Layout * { // Fancy scrollbar - scrollbar-base-color: color.scale(base.$color-bg, $lightness: -25% * $scrollbar-color-multiplier); - scrollbar-face-color: color.scale(base.$color-bg, $lightness: 10% * $scrollbar-color-multiplier); - scrollbar-3dlight-color: color.scale(base.$color-bg, $lightness: 0% * $scrollbar-color-multiplier); - scrollbar-highlight-color: color.scale(base.$color-bg, $lightness: 0% * $scrollbar-color-multiplier); - scrollbar-track-color: color.scale(base.$color-bg, $lightness: -25% * $scrollbar-color-multiplier); - scrollbar-arrow-color: color.scale(base.$color-bg, $lightness: 50% * $scrollbar-color-multiplier); - scrollbar-shadow-color: color.scale(base.$color-bg, $lightness: 10% * $scrollbar-color-multiplier); + @include fancy-scrollbar(base.$color-bg, $scrollbar-color-multiplier); } .Layout__content { diff --git a/tgui/packages/tgui/styles/layouts/PopupWindow.scss b/tgui/packages/tgui/styles/layouts/PopupWindow.scss new file mode 100644 index 0000000000000..2346f2c5d35ab --- /dev/null +++ b/tgui/packages/tgui/styles/layouts/PopupWindow.scss @@ -0,0 +1,14 @@ +@use '../base.scss'; +@use '../functions.scss' as *; +@use './Layout.scss'; + +.PopupWindow { + color: base.$color-fg; + background-color: base.$color-bg; + background-image: linear-gradient(to bottom, base.$color-bg-start 0%, base.$color-bg-end 100%); + @include Layout.fancy-scrollbar(base.$color-bg, Layout.$scrollbar-color-multiplier); +} + +.PopupWindow * { + @include Layout.fancy-scrollbar(base.$color-bg, Layout.$scrollbar-color-multiplier); +} diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss index 33261dd80b85f..2eaa9bd4e6166 100644 --- a/tgui/packages/tgui/styles/main.scss +++ b/tgui/packages/tgui/styles/main.scss @@ -63,6 +63,7 @@ @include meta.load-css('./layouts/Layout.scss'); @include meta.load-css('./layouts/NtosHeader.scss'); @include meta.load-css('./layouts/NtosWindow.scss'); +@include meta.load-css('./layouts/PopupWindow.scss'); @include meta.load-css('./layouts/TitleBar.scss'); @include meta.load-css('./layouts/Window.scss'); diff --git a/tgui/packages/tgui/styles/themes/generic.scss b/tgui/packages/tgui/styles/themes/generic.scss index c16c715b164ea..65dee095f39d2 100644 --- a/tgui/packages/tgui/styles/themes/generic.scss +++ b/tgui/packages/tgui/styles/themes/generic.scss @@ -32,6 +32,7 @@ $accent: #4f56a5; @include meta.load-css('../layouts/Layout.scss'); @include meta.load-css('../layouts/Window.scss'); @include meta.load-css('../layouts/TitleBar.scss', $with: ('background-color': color.scale($generic, $lightness: -25%))); + @include meta.load-css('../layouts/PopupWindow.scss'); .Layout__content { background-image: none; From 63bb7608a0713d0cf1055974083404bcf2f4eeb0 Mon Sep 17 00:00:00 2001 From: Lucy Date: Sat, 17 Jun 2023 17:46:49 -0400 Subject: [PATCH 164/269] good riddance legacy toggles --- beestation.dme | 1 + code/__DEFINES/preferences.dm | 15 ++-- code/controllers/subsystem/job.dm | 8 +-- code/modules/admin/admin.dm | 2 +- code/modules/admin/verbs/randomverbs.dm | 2 +- .../antagonists/_common/antag_datum.dm | 2 +- .../preferences/entries/player/admin.dm | 18 +++++ .../preferences/entries/player/deadmin.dm | 39 +++++++++++ .../client/preferences/entries/player/ooc.dm | 11 +++ .../preferences/middleware/legacy_toggles.dm | 70 ------------------- code/modules/client/verbs/ooc.dm | 5 +- code/modules/jobs/job_types/ai.dm | 2 +- code/modules/jobs/job_types/captain.dm | 2 +- code/modules/jobs/job_types/chief_engineer.dm | 2 +- .../jobs/job_types/chief_medical_officer.dm | 2 +- code/modules/jobs/job_types/cyborg.dm | 2 +- code/modules/jobs/job_types/detective.dm | 2 +- .../jobs/job_types/head_of_personnel.dm | 2 +- .../jobs/job_types/head_of_security.dm | 2 +- .../jobs/job_types/research_director.dm | 2 +- .../jobs/job_types/security_officer.dm | 2 +- code/modules/jobs/job_types/warden.dm | 2 +- code/modules/mob/living/silicon/login.dm | 2 +- .../simple_animal/friendly/drone/_drone.dm | 2 +- code/modules/mob/login.dm | 4 +- .../features/game_preferences/admin.tsx | 16 ++++- .../features/game_preferences/deadmin.tsx | 36 ++++++++++ .../features/game_preferences/ooc.tsx | 9 ++- 28 files changed, 157 insertions(+), 107 deletions(-) create mode 100644 code/modules/client/preferences/entries/player/deadmin.dm delete mode 100644 code/modules/client/preferences/middleware/legacy_toggles.dm create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/deadmin.tsx diff --git a/beestation.dme b/beestation.dme index d168c21b10ac0..1cf1620f50cc3 100644 --- a/beestation.dme +++ b/beestation.dme @@ -2163,6 +2163,7 @@ #include "code\modules\client\preferences\entries\player\buttons_locked.dm" #include "code\modules\client\preferences\entries\player\chat.dm" #include "code\modules\client\preferences\entries\player\crew_objectives.dm" +#include "code\modules\client\preferences\entries\player\deadmin.dm" #include "code\modules\client\preferences\entries\player\fps.dm" #include "code\modules\client\preferences\entries\player\ghost.dm" #include "code\modules\client\preferences\entries\player\glasses.dm" diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm index 399506d74ca5d..b819e2a5cbad6 100644 --- a/code/__DEFINES/preferences.dm +++ b/code/__DEFINES/preferences.dm @@ -1,13 +1,8 @@ -// Legacy toggles -#define PREFTOGGLE_MEMBER_PUBLIC (1<<4) -#define PREFTOGGLE_ANNOUNCE_LOGIN (1<<10) -#define PREFTOGGLE_COMBOHUD_LIGHTING (1<<14) - -#define PREFTOGGLE_DEADMIN_ALWAYS (1<<15) -#define PREFTOGGLE_DEADMIN_ANTAGONIST (1<<16) -#define PREFTOGGLE_DEADMIN_POSITION_HEAD (1<<17) -#define PREFTOGGLE_DEADMIN_POSITION_SECURITY (1<<18) -#define PREFTOGGLE_DEADMIN_POSITION_SILICON (1<<19) +#define DEADMIN_ALWAYS (1<<15) +#define DEADMIN_ANTAGONIST (1<<16) +#define DEADMIN_POSITION_HEAD (1<<17) +#define DEADMIN_POSITION_SECURITY (1<<18) +#define DEADMIN_POSITION_SILICON (1<<19) // Preferences value defines diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index 261e05cc26aca..fe14b4eb00429 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -526,7 +526,7 @@ SUBSYSTEM_DEF(job) SSpersistence.antag_rep_change[M.client.ckey] += job.GetAntagRep() if(M.client.holder) - if(CONFIG_GET(flag/auto_deadmin_players) || (M.client.prefs?.toggles & PREFTOGGLE_DEADMIN_ALWAYS)) + if(CONFIG_GET(flag/auto_deadmin_players) || M.client?.prefs.read_player_preference(/datum/preference/toggle/deadmin_always)) M.client.holder.auto_deadmin() else handle_auto_deadmin_roles(M.client, rank) @@ -554,11 +554,11 @@ SUBSYSTEM_DEF(job) var/datum/job/job = GetJob(rank) if(!job) return - if((job.auto_deadmin_role_flags & PREFTOGGLE_DEADMIN_POSITION_HEAD) && (CONFIG_GET(flag/auto_deadmin_heads) || (C.prefs?.toggles & PREFTOGGLE_DEADMIN_POSITION_HEAD))) + if((job.auto_deadmin_role_flags & DEADMIN_POSITION_HEAD) && (CONFIG_GET(flag/auto_deadmin_heads) || C.prefs?.read_player_preference(/datum/preference/toggle/deadmin_position_head))) return C.holder.auto_deadmin() - else if((job.auto_deadmin_role_flags & PREFTOGGLE_DEADMIN_POSITION_SECURITY) && (CONFIG_GET(flag/auto_deadmin_security) || (C.prefs?.toggles & PREFTOGGLE_DEADMIN_POSITION_SECURITY))) + else if((job.auto_deadmin_role_flags & DEADMIN_POSITION_SECURITY) && (CONFIG_GET(flag/auto_deadmin_security) || C.prefs?.read_player_preference(/datum/preference/toggle/deadmin_position_security))) return C.holder.auto_deadmin() - else if((job.auto_deadmin_role_flags & PREFTOGGLE_DEADMIN_POSITION_SILICON) && (CONFIG_GET(flag/auto_deadmin_silicons) || (C.prefs?.toggles & PREFTOGGLE_DEADMIN_POSITION_SILICON))) //in the event there's ever psuedo-silicon roles added, ie synths. + else if((job.auto_deadmin_role_flags & DEADMIN_POSITION_SILICON) && (CONFIG_GET(flag/auto_deadmin_silicons) || C.prefs?.read_player_preference(/datum/preference/toggle/deadmin_position_silicon))) //in the event there's ever psuedo-silicon roles added, ie synths. return C.holder.auto_deadmin() /datum/controller/subsystem/job/proc/setup_officer_positions() diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 3f18a751f4429..e6981b1f75dd5 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -785,7 +785,7 @@ if(logout && CONFIG_GET(flag/announce_admin_logout)) string = pick( "Admin logout: [key_name(src)]") - else if(!logout && CONFIG_GET(flag/announce_admin_login) && (prefs.toggles & PREFTOGGLE_ANNOUNCE_LOGIN)) + else if(!logout && CONFIG_GET(flag/announce_admin_login) && prefs?.read_player_preference(/datum/preference/toggle/announce_login)) string = pick( "Admin login: [key_name(src)]") if(string) diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 3e78ec016ed9b..324b5707cd95a 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -881,7 +881,7 @@ Traitors and the like can also be revived with the previous role mostly intact. for(var/datum/atom_hud/antag/H in GLOB.huds) // add antag huds (adding_hud) ? H.add_hud_to(usr) : H.remove_hud_from(usr) - if(prefs.toggles & PREFTOGGLE_COMBOHUD_LIGHTING) + if(prefs?.read_player_preference(/datum/preference/toggle/combohud_lighting)) if(adding_hud) mob.lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE else diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index 4bdfd8cbec2de..e809091cba77a 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -107,7 +107,7 @@ GLOBAL_LIST(admin_antag_list) give_antag_moodies() if(is_banned(owner.current) && replace_banned) replace_banned_player() - else if(owner.current.client?.holder && (CONFIG_GET(flag/auto_deadmin_antagonists) || owner.current.client.prefs?.toggles & PREFTOGGLE_DEADMIN_ANTAGONIST)) + else if(owner.current.client?.holder && (CONFIG_GET(flag/auto_deadmin_antagonists) || owner.current.client.prefs?.read_player_preference(/datum/preference/toggle/deadmin_antagonist))) owner.current.client.holder.auto_deadmin() if(count_against_dynamic_roll_chance && owner.current.stat != DEAD && owner.current.client) owner.current.add_to_current_living_antags() diff --git a/code/modules/client/preferences/entries/player/admin.dm b/code/modules/client/preferences/entries/player/admin.dm index a5a73c4becc9a..9037723a2282e 100644 --- a/code/modules/client/preferences/entries/player/admin.dm +++ b/code/modules/client/preferences/entries/player/admin.dm @@ -11,3 +11,21 @@ return FALSE return is_admin(preferences.parent) && CONFIG_GET(flag/allow_admin_asaycolor) + +/datum/preference/toggle/announce_login + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + db_key = "announce_login" + preference_type = PREFERENCE_PLAYER + default_value = FALSE + +/datum/preference/toggle/announce_login/is_accessible(datum/preferences/preferences, ignore_page) + return ..() && is_admin(preferences.parent) + +/datum/preference/toggle/combohud_lighting + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + db_key = "combohud_lighting" + preference_type = PREFERENCE_PLAYER + default_value = FALSE + +/datum/preference/toggle/combohud_lighting/is_accessible(datum/preferences/preferences, ignore_page) + return ..() && is_admin(preferences.parent) diff --git a/code/modules/client/preferences/entries/player/deadmin.dm b/code/modules/client/preferences/entries/player/deadmin.dm new file mode 100644 index 0000000000000..33a5b0d883172 --- /dev/null +++ b/code/modules/client/preferences/entries/player/deadmin.dm @@ -0,0 +1,39 @@ +/datum/preference/toggle/deadmin_always + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + db_key = "deadmin_always" + preference_type = PREFERENCE_PLAYER + +/datum/preference/toggle/deadmin_always/is_accessible(datum/preferences/preferences, ignore_page) + return ..() && is_admin(preferences.parent) + +/datum/preference/toggle/deadmin_antagonist + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + db_key = "deadmin_antagonist" + preference_type = PREFERENCE_PLAYER + +/datum/preference/toggle/deadmin_antagonist/is_accessible(datum/preferences/preferences, ignore_page) + return ..() && is_admin(preferences.parent) && !preferences.read_player_preference(/datum/preference/toggle/deadmin_always) + +/datum/preference/toggle/deadmin_position_head + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + db_key = "deadmin_position_head" + preference_type = PREFERENCE_PLAYER + +/datum/preference/toggle/deadmin_position_head/is_accessible(datum/preferences/preferences, ignore_page) + return ..() && is_admin(preferences.parent) && !preferences.read_player_preference(/datum/preference/toggle/deadmin_always) + +/datum/preference/toggle/deadmin_position_security + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + db_key = "deadmin_position_security" + preference_type = PREFERENCE_PLAYER + +/datum/preference/toggle/deadmin_position_security/is_accessible(datum/preferences/preferences, ignore_page) + return ..() && is_admin(preferences.parent) && !preferences.read_player_preference(/datum/preference/toggle/deadmin_always) + +/datum/preference/toggle/deadmin_position_silicon + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + db_key = "deadmin_position_silicon" + preference_type = PREFERENCE_PLAYER + +/datum/preference/toggle/deadmin_position_silicon/is_accessible(datum/preferences/preferences, ignore_page) + return ..() && is_admin(preferences.parent) && !preferences.read_player_preference(/datum/preference/toggle/deadmin_always) diff --git a/code/modules/client/preferences/entries/player/ooc.dm b/code/modules/client/preferences/entries/player/ooc.dm index 61123be8592c9..86916265ffd32 100644 --- a/code/modules/client/preferences/entries/player/ooc.dm +++ b/code/modules/client/preferences/entries/player/ooc.dm @@ -12,3 +12,14 @@ return FALSE return is_admin(preferences.parent) || preferences.unlock_content + +/datum/preference/toggle/member_public + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + db_key = "member_public" + preference_type = PREFERENCE_PLAYER + +/datum/preference/toggle/member_public/create_informed_default_value(datum/preferences/preferences) + return preferences.unlock_content + +/datum/preference/toggle/member_public/is_accessible(datum/preferences/preferences, ignore_page) + return ..() && preferences.unlock_content diff --git a/code/modules/client/preferences/middleware/legacy_toggles.dm b/code/modules/client/preferences/middleware/legacy_toggles.dm deleted file mode 100644 index 4291b346ed8f9..0000000000000 --- a/code/modules/client/preferences/middleware/legacy_toggles.dm +++ /dev/null @@ -1,70 +0,0 @@ -/// In the before times, toggles were all stored in one bitfield. -/// In order to preserve this existing data (and code) without massive -/// migrations, this middleware attempts to handle this in a way -/// transparent to the preferences UI itself. -/// In the future, the existing toggles data should just be migrated to -/// individual `/datum/preference/toggle`s. -/datum/preference_middleware/legacy_toggles - // DO NOT ADD ANY NEW TOGGLES HERE! - // Use `/datum/preference/toggle` instead. - var/static/list/legacy_toggles = list( - "announce_login" = PREFTOGGLE_ANNOUNCE_LOGIN, - "combohud_lighting" = PREFTOGGLE_COMBOHUD_LIGHTING, - "deadmin_always" = PREFTOGGLE_DEADMIN_ALWAYS, - "deadmin_antagonist" = PREFTOGGLE_DEADMIN_ANTAGONIST, - "deadmin_position_head" = PREFTOGGLE_DEADMIN_POSITION_HEAD, - "deadmin_position_security" = PREFTOGGLE_DEADMIN_POSITION_SECURITY, - "deadmin_position_silicon" = PREFTOGGLE_DEADMIN_POSITION_SILICON, - "member_public" = PREFTOGGLE_MEMBER_PUBLIC, - ) - -/datum/preference_middleware/legacy_toggles/get_character_preferences(mob/user) - if (preferences.current_window != PREFERENCE_TAB_GAME_PREFERENCES) - return list() - - var/static/list/admin_only_legacy_toggles = list( - "announce_login", - "combohud_lighting", - "deadmin_always", - "deadmin_antagonist", - "deadmin_position_head", - "deadmin_position_security", - "deadmin_position_silicon", - ) - - var/static/list/deadmin_flags = list( - "deadmin_antagonist", - "deadmin_position_head", - "deadmin_position_security", - "deadmin_position_silicon", - ) - - var/list/new_game_preferences = list() - var/is_admin = is_admin(user.client) - - for (var/toggle_name in legacy_toggles) - if (!is_admin && (toggle_name in admin_only_legacy_toggles)) - continue - - if (is_admin && (toggle_name in deadmin_flags) && (preferences.toggles & PREFTOGGLE_DEADMIN_ALWAYS)) - continue - - if (toggle_name == "member_public" && !preferences.unlock_content) - continue - - new_game_preferences[toggle_name] = (preferences.toggles & legacy_toggles[toggle_name]) != 0 - - return list( - PREFERENCE_CATEGORY_GAME_PREFERENCES = new_game_preferences, - ) - -/datum/preference_middleware/legacy_toggles/pre_set_preference(mob/user, preference, value) - var/legacy_flag = legacy_toggles[preference] - if (!isnull(legacy_flag)) - if (value) - preferences.toggles |= legacy_flag - else - preferences.toggles &= ~legacy_flag - return TRUE - - return FALSE diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm index 14ba1ca2d5f1a..80b1e419daecf 100644 --- a/code/modules/client/verbs/ooc.dm +++ b/code/modules/client/verbs/ooc.dm @@ -77,9 +77,8 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") var/keyname = key var/ooccolor = prefs.read_player_preference(/datum/preference/color/ooc_color) - if(prefs.unlock_content) - if(prefs.toggles & PREFTOGGLE_MEMBER_PUBLIC) - keyname = "[icon2html('icons/member_content.dmi', world, "blag")][keyname]" + if(prefs.unlock_content && prefs.read_player_preference(/datum/preference/toggle/member_public)) + keyname = "[icon2html('icons/member_content.dmi', world, "blag")][keyname]" //Get client badges var/badge_data = badge_parse(get_badges()) //The linkify span classes and linkify=TRUE below make ooc text get clickable chat href links if you pass in something resembling a url diff --git a/code/modules/jobs/job_types/ai.dm b/code/modules/jobs/job_types/ai.dm index 1515d4adfa676..71c7b84ba1bed 100644 --- a/code/modules/jobs/job_types/ai.dm +++ b/code/modules/jobs/job_types/ai.dm @@ -4,7 +4,7 @@ description = "Follow your laws above all else, be the invisible eye that watches all." department_for_prefs = DEPT_BITFLAG_SILICON department_head_for_prefs = JOB_NAME_AI - auto_deadmin_role_flags = PREFTOGGLE_DEADMIN_POSITION_SILICON + auto_deadmin_role_flags = DEADMIN_POSITION_SILICON department_flag = ENGSEC faction = "Station" total_positions = 1 diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm index 3d458db6c66db..211788581f051 100755 --- a/code/modules/jobs/job_types/captain.dm +++ b/code/modules/jobs/job_types/captain.dm @@ -4,7 +4,7 @@ description = "Supreme leader of the station, oversee and appoint missing heads of staff, manage alert levels and contact CentCom if needed. Don't forget to secure the nuclear authentication disk." department_for_prefs = DEPT_BITFLAG_CAPTAIN department_head_for_prefs = JOB_NAME_CAPTAIN - auto_deadmin_role_flags = PREFTOGGLE_DEADMIN_POSITION_HEAD|PREFTOGGLE_DEADMIN_POSITION_SECURITY + auto_deadmin_role_flags = DEADMIN_POSITION_HEAD|DEADMIN_POSITION_SECURITY department_head = list("CentCom") supervisors = "Nanotrasen officials and Space law" faction = "Station" diff --git a/code/modules/jobs/job_types/chief_engineer.dm b/code/modules/jobs/job_types/chief_engineer.dm index b4e3ad1dbf5f9..f50fdd3ef6057 100644 --- a/code/modules/jobs/job_types/chief_engineer.dm +++ b/code/modules/jobs/job_types/chief_engineer.dm @@ -3,7 +3,7 @@ flag = CHIEF description = "Oversee the engineers and atmospheric technicians, keep a watchful eye on the station's engine, gravity generator, and telecomms. Send your staff to repair hull breaches and damaged equipment as necessary." department_for_prefs = DEPT_BITFLAG_ENG - auto_deadmin_role_flags = PREFTOGGLE_DEADMIN_POSITION_HEAD + auto_deadmin_role_flags = DEADMIN_POSITION_HEAD department_head = list(JOB_NAME_CAPTAIN) supervisors = "the captain" head_announce = list("Engineering") diff --git a/code/modules/jobs/job_types/chief_medical_officer.dm b/code/modules/jobs/job_types/chief_medical_officer.dm index aab99a6ceb8d9..d35e1e09e4cc8 100644 --- a/code/modules/jobs/job_types/chief_medical_officer.dm +++ b/code/modules/jobs/job_types/chief_medical_officer.dm @@ -7,7 +7,7 @@ department_for_prefs = DEPT_BITFLAG_MED department_head = list(JOB_NAME_CAPTAIN) supervisors = "the captain" - auto_deadmin_role_flags = PREFTOGGLE_DEADMIN_POSITION_HEAD + auto_deadmin_role_flags = DEADMIN_POSITION_HEAD head_announce = list(RADIO_CHANNEL_MEDICAL) faction = "Station" total_positions = 1 diff --git a/code/modules/jobs/job_types/cyborg.dm b/code/modules/jobs/job_types/cyborg.dm index 11c5be72a4c9c..3a46423433e18 100644 --- a/code/modules/jobs/job_types/cyborg.dm +++ b/code/modules/jobs/job_types/cyborg.dm @@ -4,7 +4,7 @@ description = "Follow your AI's interpretation of your laws above all else, or your own interpretation if not connected to an AI. Choose one of many modules with different tools, ask robotics for maintenance and upgrades." department_for_prefs = DEPT_BITFLAG_SILICON department_head_for_prefs = JOB_NAME_AI - auto_deadmin_role_flags = PREFTOGGLE_DEADMIN_POSITION_SILICON + auto_deadmin_role_flags = DEADMIN_POSITION_SILICON department_flag = ENGSEC faction = "Station" total_positions = 1 diff --git a/code/modules/jobs/job_types/detective.dm b/code/modules/jobs/job_types/detective.dm index 7bb6f7381455a..fcd0df2364cb4 100644 --- a/code/modules/jobs/job_types/detective.dm +++ b/code/modules/jobs/job_types/detective.dm @@ -3,7 +3,7 @@ flag = DETECTIVE description = "Investigate crimes, solve murder mysteries, report your findings to the rest of Security." department_for_prefs = DEPT_BITFLAG_SEC - auto_deadmin_role_flags = PREFTOGGLE_DEADMIN_POSITION_SECURITY + auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY department_head = list(JOB_NAME_HEADOFSECURITY) supervisors = "the head of security" faction = "Station" diff --git a/code/modules/jobs/job_types/head_of_personnel.dm b/code/modules/jobs/job_types/head_of_personnel.dm index 7c4311d041c6b..99ff2e847215d 100644 --- a/code/modules/jobs/job_types/head_of_personnel.dm +++ b/code/modules/jobs/job_types/head_of_personnel.dm @@ -4,7 +4,7 @@ description = "Second in command on the station, oversee the crew assigned to service and cargo positions, handle department transfer requests by consulting relevant heads. Protect Ian at all costs." department_for_prefs = DEPT_BITFLAG_CAPTAIN department_head_for_prefs = JOB_NAME_CAPTAIN - auto_deadmin_role_flags = PREFTOGGLE_DEADMIN_POSITION_HEAD + auto_deadmin_role_flags = DEADMIN_POSITION_HEAD department_head = list(JOB_NAME_CAPTAIN) supervisors = "the captain" head_announce = list(RADIO_CHANNEL_SUPPLY, RADIO_CHANNEL_SERVICE) diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm index b32686f414ff7..ae19aba09f1fe 100644 --- a/code/modules/jobs/job_types/head_of_security.dm +++ b/code/modules/jobs/job_types/head_of_security.dm @@ -3,7 +3,7 @@ flag = HOS description = "Oversee the members of security and ensure they follow Space Law. Deputize other crew members when the station is in need of additional protection." department_for_prefs = DEPT_BITFLAG_SEC - auto_deadmin_role_flags = PREFTOGGLE_DEADMIN_POSITION_HEAD|PREFTOGGLE_DEADMIN_POSITION_SECURITY + auto_deadmin_role_flags = DEADMIN_POSITION_HEAD|DEADMIN_POSITION_SECURITY department_head = list(JOB_NAME_CAPTAIN) supervisors = "the captain" head_announce = list(RADIO_CHANNEL_SECURITY) diff --git a/code/modules/jobs/job_types/research_director.dm b/code/modules/jobs/job_types/research_director.dm index a7cbecde700d7..c643ef2a38a74 100644 --- a/code/modules/jobs/job_types/research_director.dm +++ b/code/modules/jobs/job_types/research_director.dm @@ -3,7 +3,7 @@ flag = RD_JF description = "Oversee the scientists and roboticists and keep up with their research projects, take care of any issues with the station's AI that may arise, ensure research is being prioritized in accordance with the needs of the station." department_for_prefs = DEPT_BITFLAG_SCI - auto_deadmin_role_flags = PREFTOGGLE_DEADMIN_POSITION_HEAD + auto_deadmin_role_flags = DEADMIN_POSITION_HEAD department_head = list(JOB_NAME_CAPTAIN) supervisors = "the captain" head_announce = list("Science") diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm index f3a42a55a0866..a2c4dcb5f61af 100644 --- a/code/modules/jobs/job_types/security_officer.dm +++ b/code/modules/jobs/job_types/security_officer.dm @@ -3,7 +3,7 @@ flag = OFFICER description = "Follow Space Law, patrol the station, arrest criminals and bring them to the Brig." department_for_prefs = DEPT_BITFLAG_SEC - auto_deadmin_role_flags = PREFTOGGLE_DEADMIN_POSITION_SECURITY + auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY department_head = list(JOB_NAME_HEADOFSECURITY) supervisors = "the head of security, and the head of your assigned department (if applicable)" faction = "Station" diff --git a/code/modules/jobs/job_types/warden.dm b/code/modules/jobs/job_types/warden.dm index 00c069d43e24b..98e7d52226d8c 100644 --- a/code/modules/jobs/job_types/warden.dm +++ b/code/modules/jobs/job_types/warden.dm @@ -3,7 +3,7 @@ flag = WARDEN description = "Oversee prisoners in the brig and guard the armory. Hand out equipment when necessary and ensure it is returned after threats have been contained." department_for_prefs = DEPT_BITFLAG_SEC - auto_deadmin_role_flags = PREFTOGGLE_DEADMIN_POSITION_SECURITY + auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY department_head = list(JOB_NAME_HEADOFSECURITY) supervisors = "the head of security" faction = "Station" diff --git a/code/modules/mob/living/silicon/login.dm b/code/modules/mob/living/silicon/login.dm index 76b171063c4c9..541736da1a6a1 100644 --- a/code/modules/mob/living/silicon/login.dm +++ b/code/modules/mob/living/silicon/login.dm @@ -9,6 +9,6 @@ /mob/living/silicon/auto_deadmin_on_login() if(!client?.holder) return TRUE - if(CONFIG_GET(flag/auto_deadmin_silicons) || (client.prefs?.toggles & PREFTOGGLE_DEADMIN_POSITION_SILICON)) + if(CONFIG_GET(flag/auto_deadmin_silicons) || client.prefs?.read_player_preference(/datum/preference/toggle/deadmin_position_silicon)) return client.holder.auto_deadmin() return ..() diff --git a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm index 30f86c43f8dcf..9ab732c688f15 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm @@ -145,7 +145,7 @@ /mob/living/simple_animal/drone/auto_deadmin_on_login() if(!client?.holder) return TRUE - if(CONFIG_GET(flag/auto_deadmin_silicons) || (client.prefs?.toggles & PREFTOGGLE_DEADMIN_POSITION_SILICON)) + if(CONFIG_GET(flag/auto_deadmin_silicons) || client.prefs?.read_player_preference(/datum/preference/toggle/deadmin_position_silicon)) return client.holder.auto_deadmin() return ..() diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index e033865cf958c..a466896bedfb8 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -115,9 +115,9 @@ /mob/proc/auto_deadmin_on_login() //return true if they're not an admin at the end. if(!client?.holder) return TRUE - if(CONFIG_GET(flag/auto_deadmin_players) || (client.prefs?.toggles & PREFTOGGLE_DEADMIN_ALWAYS)) + if(CONFIG_GET(flag/auto_deadmin_players) || client?.prefs?.read_player_preference(/datum/preference/toggle/deadmin_always)) return client.holder.auto_deadmin() - if(mind.has_antag_datum(/datum/antagonist) && (CONFIG_GET(flag/auto_deadmin_antagonists) || client.prefs?.toggles & PREFTOGGLE_DEADMIN_ANTAGONIST)) + if(mind.has_antag_datum(/datum/antagonist) && (CONFIG_GET(flag/auto_deadmin_antagonists) || client.prefs?.read_player_preference(/datum/preference/toggle/deadmin_antagonist))) return client.holder.auto_deadmin() if(job) return SSjob.handle_auto_deadmin_roles(client, job) diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/admin.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/admin.tsx index d31139ed64b30..336ec901d53ec 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/admin.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/admin.tsx @@ -1,4 +1,4 @@ -import { FeatureColorInput, Feature, FeatureDropdownInput } from '../base'; +import { FeatureColorInput, Feature, FeatureToggle, CheckboxInput } from '../base'; export const asaycolor: Feature = { name: 'Admin chat color', @@ -6,3 +6,17 @@ export const asaycolor: Feature = { description: 'The color of your messages in Adminsay.', component: FeatureColorInput, }; + +export const announce_login: FeatureToggle = { + name: 'Announce Login', + category: 'ADMIN', + description: 'Whether you will announce whenever you login to fellow admins or not.', + component: CheckboxInput, +}; + +export const combohud_lighting: FeatureToggle = { + name: 'Combo HUD Lighting', + category: 'ADMIN', + description: 'Whether you see combo HUD lighting as fullbright or not.', + component: CheckboxInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/deadmin.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/deadmin.tsx new file mode 100644 index 0000000000000..036e9389296a1 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/deadmin.tsx @@ -0,0 +1,36 @@ +import { FeatureToggle, CheckboxInput } from '../base'; + +export const deadmin_always: FeatureToggle = { + name: 'Always Deadmin', + category: 'ADMIN', + description: 'Whether you will always deadmin when joining a round.', + component: CheckboxInput, +}; + +export const deadmin_antagonist: FeatureToggle = { + name: 'Deadmin As Antagonist', + category: 'ADMIN', + description: 'Whether you will always deadmin when joining a round as an antagonist.', + component: CheckboxInput, +}; + +export const deadmin_position_head: FeatureToggle = { + name: 'Deadmin As Head of Staff', + category: 'ADMIN', + description: 'Whether you will always deadmin when joining a round as a head of staff.', + component: CheckboxInput, +}; + +export const deadmin_position_security: FeatureToggle = { + name: 'Deadmin As Security', + category: 'ADMIN', + description: 'Whether you will always deadmin when joining a round as security.', + component: CheckboxInput, +}; + +export const deadmin_position_silicon: FeatureToggle = { + name: 'Deadmin As Silicon', + category: 'ADMIN', + description: 'Whether you will always deadmin when joining a round as a silicon.', + component: CheckboxInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ooc.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ooc.tsx index 3e0421de71241..a8ca03224a392 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ooc.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ooc.tsx @@ -1,4 +1,4 @@ -import { FeatureColorInput, Feature } from '../base'; +import { CheckboxInput, FeatureColorInput, FeatureToggle, Feature } from '../base'; export const ooccolor: Feature = { name: 'OOC color', @@ -6,3 +6,10 @@ export const ooccolor: Feature = { description: 'The color of your OOC messages.', component: FeatureColorInput, }; + +export const member_public: FeatureToggle = { + name: 'Show BYOND Membership', + category: 'CHAT', + description: 'Whether to show your BYOND membership in OOC or not.', + component: CheckboxInput, +}; From 166a374da7bc09ab11f63b41a0b8a88039361b75 Mon Sep 17 00:00:00 2001 From: Lucy Date: Sat, 17 Jun 2023 17:51:35 -0400 Subject: [PATCH 165/269] i dumb --- code/modules/client/preferences/entries/player/ooc.dm | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/code/modules/client/preferences/entries/player/ooc.dm b/code/modules/client/preferences/entries/player/ooc.dm index 86916265ffd32..c0b044b83d973 100644 --- a/code/modules/client/preferences/entries/player/ooc.dm +++ b/code/modules/client/preferences/entries/player/ooc.dm @@ -17,9 +17,7 @@ category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "member_public" preference_type = PREFERENCE_PLAYER - -/datum/preference/toggle/member_public/create_informed_default_value(datum/preferences/preferences) - return preferences.unlock_content + default_value = TRUE /datum/preference/toggle/member_public/is_accessible(datum/preferences/preferences, ignore_page) return ..() && preferences.unlock_content From bb0889b966e49a35bf431f3efe452e0697fadfd0 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Sat, 17 Jun 2023 19:23:51 -0400 Subject: [PATCH 166/269] Remove the last of legacy toggles for good, add deadmin forced preview --- .../preferences/entries/player/deadmin.dm | 30 ++++++++++ .../features/game_preferences/deadmin.tsx | 49 ++++++++++++---- .../game_preferences/legacy_toggles.tsx | 56 ------------------- 3 files changed, 68 insertions(+), 67 deletions(-) delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/legacy_toggles.tsx diff --git a/code/modules/client/preferences/entries/player/deadmin.dm b/code/modules/client/preferences/entries/player/deadmin.dm index 33a5b0d883172..dd1b985529f98 100644 --- a/code/modules/client/preferences/entries/player/deadmin.dm +++ b/code/modules/client/preferences/entries/player/deadmin.dm @@ -2,38 +2,68 @@ category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "deadmin_always" preference_type = PREFERENCE_PLAYER + default_value = FALSE /datum/preference/toggle/deadmin_always/is_accessible(datum/preferences/preferences, ignore_page) return ..() && is_admin(preferences.parent) +/datum/preference/toggle/deadmin_always/compile_constant_data() + return list( + "forced" = CONFIG_GET(flag/auto_deadmin_players), + ) + /datum/preference/toggle/deadmin_antagonist category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "deadmin_antagonist" preference_type = PREFERENCE_PLAYER + default_value = FALSE /datum/preference/toggle/deadmin_antagonist/is_accessible(datum/preferences/preferences, ignore_page) return ..() && is_admin(preferences.parent) && !preferences.read_player_preference(/datum/preference/toggle/deadmin_always) +/datum/preference/toggle/deadmin_antagonist/compile_constant_data() + return list( + "forced" = CONFIG_GET(flag/auto_deadmin_antagonists), + ) + /datum/preference/toggle/deadmin_position_head category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "deadmin_position_head" preference_type = PREFERENCE_PLAYER + default_value = FALSE /datum/preference/toggle/deadmin_position_head/is_accessible(datum/preferences/preferences, ignore_page) return ..() && is_admin(preferences.parent) && !preferences.read_player_preference(/datum/preference/toggle/deadmin_always) +/datum/preference/toggle/deadmin_position_head/compile_constant_data() + return list( + "forced" = CONFIG_GET(flag/auto_deadmin_heads), + ) + /datum/preference/toggle/deadmin_position_security category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "deadmin_position_security" preference_type = PREFERENCE_PLAYER + default_value = FALSE /datum/preference/toggle/deadmin_position_security/is_accessible(datum/preferences/preferences, ignore_page) return ..() && is_admin(preferences.parent) && !preferences.read_player_preference(/datum/preference/toggle/deadmin_always) +/datum/preference/toggle/deadmin_position_security/compile_constant_data() + return list( + "forced" = CONFIG_GET(flag/auto_deadmin_security), + ) + /datum/preference/toggle/deadmin_position_silicon category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "deadmin_position_silicon" preference_type = PREFERENCE_PLAYER + default_value = FALSE /datum/preference/toggle/deadmin_position_silicon/is_accessible(datum/preferences/preferences, ignore_page) return ..() && is_admin(preferences.parent) && !preferences.read_player_preference(/datum/preference/toggle/deadmin_always) + +/datum/preference/toggle/deadmin_position_silicon/compile_constant_data() + return list( + "forced" = CONFIG_GET(flag/auto_deadmin_silicons), + ) diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/deadmin.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/deadmin.tsx index 036e9389296a1..c826526ad97b4 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/deadmin.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/deadmin.tsx @@ -1,36 +1,63 @@ -import { FeatureToggle, CheckboxInput } from '../base'; +import { BooleanLike } from 'common/react'; +import { Button } from '../../../../../components'; +import { Feature, FeatureValueProps } from '../base'; -export const deadmin_always: FeatureToggle = { +type FeatureToggleDeadminServerData = { + forced: BooleanLike; +}; + +type FeatureToggleDeadmin = Feature; + +const DeadminCheckboxInput = (props: FeatureValueProps) => { + const forced = props.serverData?.forced; + return ( + diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx index c295d0bce27a8..f801ea66462e7 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/LoadoutPage.tsx @@ -1,13 +1,16 @@ -import { Box, Tabs, Button, Section, Stack, Flex, Table, Icon } from '../../components'; +import { Box, Tabs, Button, Tooltip, Stack, Flex, Table, Section, Icon, Input } from '../../components'; import { LoadoutGear, PreferencesMenuData } from './data'; import { useBackend, useLocalState } from '../../backend'; import { ServerPreferencesFetcher } from './ServerPreferencesFetcher'; import { CharacterPreview } from './CharacterPreview'; +import { createSearch } from 'common/string'; + +const isPurchased = (purchased_gear: string[], gear: LoadoutGear) => purchased_gear.includes(gear.id) && !gear.multi_purchase; export const LoadoutPage = (props, context) => { const { act, data } = useBackend(context); - const { purchased_gear = [], equipped_gear = [], character_preferences, metacurrency_balance = 0, is_donator = false } = data; - const jumpsuit_style = character_preferences.clothing.jumpsuit_style; + const { purchased_gear = [], metacurrency_balance = 0, is_donator = false } = data; + return ( { @@ -16,16 +19,19 @@ export const LoadoutPage = (props, context) => { } const { categories = [], metacurrency_name } = serverData.loadout; const [selectedCategory, setSelectedCategory] = useLocalState(context, 'category', categories[0].name); - const [onlyPurchased, setOnlyPurchased] = useLocalState(context, 'only_purchased', false); + let [searchText, setSearchText] = useLocalState(context, 'loadout_search', ''); + let search = createSearch(searchText, (gear: LoadoutGear) => { + return gear.display_name + ' ' + gear.skirt_display_name + ' ' + gear.allowed_roles?.join(' '); + }); let selectedCategoryObject = categories.filter((c) => c.name === selectedCategory)[0]; - - const isPurchased = (gear: LoadoutGear) => purchased_gear.includes(gear.id) && !gear.multi_purchase; let currency_text = metacurrency_balance.toLocaleString() + ' ' + metacurrency_name + 's'; + const showRoles = + !selectedCategoryObject || selectedCategoryObject.gear.filter((g) => g.allowed_roles?.length).length > 0; return ( - + { {currency_text} - + - + - - - {categories - .filter((c) => c.name !== 'Donator' || is_donator) - .map((category) => ( - setSelectedCategory(category.name)}> - {category.name} - - ))} - setOnlyPurchased(!onlyPurchased)}> - - Show Purchased Only - - + +
    + + setSearchText(value)} + /> +
    - + {!searchText?.length && ( + + + {categories + .filter((c) => c.name !== 'Donator' || is_donator) + .map((category) => ( + setSelectedCategory(category.name)}> + {category.name} + + ))} + setSelectedCategory('Purchased')}> + Purchased + + + + )} + { Name + {showRoles ? Roles : null} {selectedCategory !== 'Donator' && ( Cost @@ -83,63 +102,30 @@ export const LoadoutPage = (props, context) => { )} - {selectedCategoryObject && - selectedCategoryObject.gear - .filter((gear) => !onlyPurchased || isPurchased(gear)) + {!searchText?.length && selectedCategoryObject + ? selectedCategoryObject.gear.map((gear) => ( + + )) + : null} + {searchText.length || selectedCategory === 'Purchased' + ? categories + .flatMap((c) => c.gear) + .filter((gear) => (searchText.length ? search(gear) : isPurchased(purchased_gear, gear))) .map((gear) => ( - - - - - - {jumpsuit_style === 'Jumpskirt' && gear.skirt_display_name - ? gear.skirt_display_name - : gear.display_name} - - {selectedCategory !== 'Donator' && ( - - {gear.cost.toLocaleString()} - - )} - - -
    -
    - + + + + + - - + - {Object.entries(catalog.icons).map(([name, image], index) => { - return ( - - - - ); - })} + + + {name} + + + ); + })} - - - + + + ); }; @@ -217,6 +257,11 @@ const MainFeature = ( const { catalog, currentValue, isOpen, handleOpen, handleClose, handleSelect, randomization, setRandomization } = props; const supplementalFeature = catalog.supplemental_feature; + let [searchText, setSearchText] = useLocalState(context, catalog.name + '_choiced_search', ''); + const handleCloseInternal = () => { + handleClose(); + setSearchText(''); + }; return ( + ) @@ -241,7 +288,7 @@ const MainFeature = ( + + {catalog.name} + ); }; @@ -435,14 +493,17 @@ export const MainPage = ( )} - + - + { - act('rotate'); + handleRotate={(direction) => { + act('rotate', { direction: direction }); }} setGender={createSetPreference(act, 'gender')} showGender={currentSpeciesData ? !!currentSpeciesData.sexes : true} @@ -465,7 +526,7 @@ export const MainPage = ( - + {mainFeatures.map(([clothingKey, clothing]) => { const catalog = diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/SaveStatus.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/SaveStatus.tsx index 48d0210c6b095..b4c4cf3e9f6df 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/SaveStatus.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/SaveStatus.tsx @@ -44,7 +44,18 @@ export const SaveStatus = (props, context) => { } if (!save_in_progress && !save_sucess) { return ( - + + {innerBox} + + ); + } + if (save_in_progress) { + return ( + {innerBox} ); diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx index 3ea20e8f41af2..977a1a5a672ab 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx @@ -53,7 +53,7 @@ export const MultiNameInput = ( Close } - title="Alternate names"> + title="All Names"> {sortNameWithKeyEntries(Object.entries(namesIntoGroups)).map(([_, names], index, collection) => ( <> @@ -151,26 +151,34 @@ export const NameInput = ( onClick={() => { setLastNameBeforeEdit(props.name); }} - textAlign="center" width="100%" height="28px"> - - + + - + {(editing && ( { setLastNameBeforeEdit(null); }} @@ -181,17 +189,6 @@ export const NameInput = ( {props.name} )} - - {/* We only know other names when the server tells us */} @@ -206,10 +203,10 @@ export const NameInput = ( style={{ background: 'rgba(0, 0, 0, 0.7)', position: 'absolute', - right: '2px', + right: '5px', top: '50%', transform: 'translateY(-50%)', - width: '2%', + width: '20px', }} onClick={(event) => { props.openMultiNameInput(); @@ -220,10 +217,10 @@ export const NameInput = ( event.stopPropagation(); }}> diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx index 47889aa291c8d..a2d2a60718005 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx @@ -20,6 +20,11 @@ export const gradient_color: Feature = { component: FeatureColorInput, }; +export const feature_lizard_legs: FeatureChoiced = { + name: 'Leg Type', + component: FeatureDropdownInput, +}; + export const feature_mcolor: Feature = { name: 'Mutant color', component: FeatureColorInput, diff --git a/tgui/packages/tgui/styles/layouts/PopupWindow.scss b/tgui/packages/tgui/styles/layouts/PopupWindow.scss index 2346f2c5d35ab..acf9246f4ab3c 100644 --- a/tgui/packages/tgui/styles/layouts/PopupWindow.scss +++ b/tgui/packages/tgui/styles/layouts/PopupWindow.scss @@ -1,3 +1,4 @@ +@use 'sass:color'; @use '../base.scss'; @use '../functions.scss' as *; @use './Layout.scss'; @@ -7,6 +8,7 @@ background-color: base.$color-bg; background-image: linear-gradient(to bottom, base.$color-bg-start 0%, base.$color-bg-end 100%); @include Layout.fancy-scrollbar(base.$color-bg, Layout.$scrollbar-color-multiplier); + border: 1px solid color.adjust(base.$color-bg, $lightness: 7%); } .PopupWindow * { From c036548fa8c6e321b151adb1206f994b84ae3f8d Mon Sep 17 00:00:00 2001 From: itsmeow Date: Tue, 20 Jun 2023 20:40:38 -0400 Subject: [PATCH 174/269] A little bit of unique styling --- tgui/packages/tgui/index.js | 1 + .../CharacterPreferenceWindow.tsx | 2 +- .../PreferencesMenu/GamePreferenceWindow.tsx | 2 +- .../interfaces/PreferencesMenu/MainPage.tsx | 10 ++-- .../tgui/styles/themes/generic-yellow.scss | 49 +++++++++++++++++++ 5 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 tgui/packages/tgui/styles/themes/generic-yellow.scss diff --git a/tgui/packages/tgui/index.js b/tgui/packages/tgui/index.js index bfb803769114b..fb75304f1d241 100644 --- a/tgui/packages/tgui/index.js +++ b/tgui/packages/tgui/index.js @@ -35,6 +35,7 @@ import './styles/themes-ntos/clown-pink.scss'; import './styles/themes-ntos/clown-yellow.scss'; import './styles/themes-ntos/hackerman.scss'; import './styles/themes/generic.scss'; +import './styles/themes/generic-yellow.scss'; import './styles/themes/paper.scss'; import './styles/themes/retro.scss'; import './styles/themes/syndicate.scss'; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx index 470e292cae5f6..c9f2ec1f6b622 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx @@ -83,7 +83,7 @@ export const CharacterPreferenceWindow = (props, context) => { } return ( - }> + }> diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferenceWindow.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferenceWindow.tsx index fd3ccbc35eb30..d1592d1b1dca5 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferenceWindow.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferenceWindow.tsx @@ -36,7 +36,7 @@ export const GamePreferenceWindow = ( } return ( - }> + }> diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx index 2f597b0570f9c..545c2dd0d5223 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx @@ -31,19 +31,19 @@ const CharacterControls = (props: { - - {name} + + + {name} + ); diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx index b10a7d82c9ec0..2ded90d6c6664 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx @@ -26,6 +26,7 @@ const eyePresets = { export const eye_color: Feature = { name: 'Eye Color', + small_supplemental: false, predictable: false, component: (props: FeatureValueProps) => { const { handleSetValue, value, featureId, act } = props; @@ -40,6 +41,7 @@ export const eye_color: Feature = { allow_custom featureId={featureId} act={act} + maxWidth="100%" includeHex /> ); @@ -186,3 +188,13 @@ export const helmet_style: FeatureChoiced = { name: 'Helmet Style', component: FeatureDropdownInput, }; + +export const feature_ipc_antenna_color: Feature = { + name: 'Antenna Color', + component: FeatureColorInput, +}; + +export const feature_ipc_screen_color: Feature = { + name: 'Screen Color', + component: FeatureColorInput, +}; From e5c969c21045f24c68a433e8bd6de59b7e7b5c6e Mon Sep 17 00:00:00 2001 From: itsmeow Date: Thu, 22 Jun 2023 00:01:03 -0400 Subject: [PATCH 194/269] Make game preference look pretty as fuck, add searching, remove unused stuff, add subcategories --- beestation.dme | 1 - .../entries/player/broadcast_login_logout.dm | 5 -- .../client/preferences/entries/player/chat.dm | 5 -- .../preferences/entries/player/ghost.dm | 3 + .../tgui/components/CollapsibleSection.js | 13 ++- tgui/packages/tgui/components/Dropdown.tsx | 7 +- tgui/packages/tgui/components/Flex.tsx | 10 ++- tgui/packages/tgui/components/Stack.tsx | 4 +- .../PreferencesMenu/GamePreferenceWindow.tsx | 2 +- .../PreferencesMenu/GamePreferencesPage.tsx | 84 +++++++++++++++---- .../interfaces/PreferencesMenu/TabbedMenu.tsx | 72 ++++++++-------- .../preferences/features/base.tsx | 4 +- .../features/game_preferences/admin.tsx | 3 + .../game_preferences/ambient_occlusion.tsx | 5 +- .../game_preferences/auto_fit_viewport.tsx | 5 +- .../broadcast_login_logout.tsx | 11 --- .../game_preferences/buttons_locked.tsx | 3 +- .../features/game_preferences/chat.tsx | 38 +++++---- .../features/game_preferences/deadmin.tsx | 5 ++ .../features/game_preferences/fps.tsx | 8 +- .../features/game_preferences/ghost.tsx | 17 ++-- .../features/game_preferences/glasses.tsx | 5 +- .../game_preferences/item_outlines.tsx | 6 +- .../features/game_preferences/ooc.tsx | 5 +- .../features/game_preferences/parallax.tsx | 5 +- .../features/game_preferences/pixel_size.tsx | 24 ++++-- .../features/game_preferences/rattle.tsx | 6 +- .../features/game_preferences/roundend.tsx | 3 +- .../features/game_preferences/runechat.tsx | 17 ++-- .../game_preferences/scaling_method.tsx | 20 +++-- .../features/game_preferences/sound.tsx | 12 +++ .../features/game_preferences/tgui.tsx | 28 ++++--- .../features/game_preferences/tooltips.tsx | 6 +- .../features/game_preferences/ui_style.tsx | 5 +- .../game_preferences/window_flashing.tsx | 1 + 35 files changed, 297 insertions(+), 151 deletions(-) delete mode 100644 code/modules/client/preferences/entries/player/broadcast_login_logout.dm delete mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/broadcast_login_logout.tsx diff --git a/beestation.dme b/beestation.dme index 2c10b65a102ac..5f6b7c5d6ff4f 100644 --- a/beestation.dme +++ b/beestation.dme @@ -2158,7 +2158,6 @@ #include "code\modules\client\preferences\entries\player\admin.dm" #include "code\modules\client\preferences\entries\player\ambient_occlusion.dm" #include "code\modules\client\preferences\entries\player\auto_fit_viewport.dm" -#include "code\modules\client\preferences\entries\player\broadcast_login_logout.dm" #include "code\modules\client\preferences\entries\player\buttons_locked.dm" #include "code\modules\client\preferences\entries\player\chat.dm" #include "code\modules\client\preferences\entries\player\crew_objectives.dm" diff --git a/code/modules/client/preferences/entries/player/broadcast_login_logout.dm b/code/modules/client/preferences/entries/player/broadcast_login_logout.dm deleted file mode 100644 index ffea82ef3c3de..0000000000000 --- a/code/modules/client/preferences/entries/player/broadcast_login_logout.dm +++ /dev/null @@ -1,5 +0,0 @@ -/// Whether or not to announce when the player logs in or out. -/datum/preference/toggle/broadcast_login_logout - category = PREFERENCE_CATEGORY_GAME_PREFERENCES - db_key = "broadcast_login_logout" - preference_type = PREFERENCE_PLAYER diff --git a/code/modules/client/preferences/entries/player/chat.dm b/code/modules/client/preferences/entries/player/chat.dm index eb3d58c6948a6..259fc0c7d1c58 100644 --- a/code/modules/client/preferences/entries/player/chat.dm +++ b/code/modules/client/preferences/entries/player/chat.dm @@ -48,11 +48,6 @@ db_key = "chat_ghostwhisper" preference_type = PREFERENCE_PLAYER -/datum/preference/toggle/chat_login_logout - category = PREFERENCE_CATEGORY_GAME_PREFERENCES - db_key = "chat_login_logout" - preference_type = PREFERENCE_PLAYER - /datum/preference/toggle/chat_ooc category = PREFERENCE_CATEGORY_GAME_PREFERENCES db_key = "chat_ooc" diff --git a/code/modules/client/preferences/entries/player/ghost.dm b/code/modules/client/preferences/entries/player/ghost.dm index 2f988c0a344f5..36e7aee4cf9e9 100644 --- a/code/modules/client/preferences/entries/player/ghost.dm +++ b/code/modules/client/preferences/entries/player/ghost.dm @@ -103,6 +103,9 @@ ghost.ghost_orbit = value +/datum/preference/choiced/ghost_orbit/create_default_value() + return GHOST_ORBIT_CIRCLE + /// Determines how to show other ghosts /datum/preference/choiced/ghost_others db_key = "ghost_others" diff --git a/tgui/packages/tgui/components/CollapsibleSection.js b/tgui/packages/tgui/components/CollapsibleSection.js index fc5e98b4a538f..d5441e589e9d5 100644 --- a/tgui/packages/tgui/components/CollapsibleSection.js +++ b/tgui/packages/tgui/components/CollapsibleSection.js @@ -3,8 +3,17 @@ import { Section } from './Section'; import { Button } from './Button'; export const CollapsibleSection = (props, context) => { - const { children, sectionKey, color, buttons = [], forceOpen = false, showButton = !forceOpen, ...rest } = props; - const [isOpen, setOpen] = useLocalState(context, `open_collapsible_${sectionKey}`, true); + const { + children, + startOpen = true, + sectionKey, + color, + buttons = [], + forceOpen = false, + showButton = !forceOpen, + ...rest + } = props; + const [isOpen, setOpen] = useLocalState(context, `open_collapsible_${sectionKey}`, startOpen); return (
    ; }; export const computeFlexItemClassName = (props: FlexItemProps) => { @@ -83,7 +85,13 @@ export const computeFlexItemProps = (props: FlexItemProps) => { const FlexItem = (props) => { const { className, ...rest } = props; - return
    ; + return ( +
    + ); }; FlexItem.defaultHooks = pureComponentHooks; diff --git a/tgui/packages/tgui/components/Stack.tsx b/tgui/packages/tgui/components/Stack.tsx index 26feaaba8852c..218374605b051 100644 --- a/tgui/packages/tgui/components/Stack.tsx +++ b/tgui/packages/tgui/components/Stack.tsx @@ -33,9 +33,7 @@ export const Stack = (props: StackProps) => { ); }; -type StackItemProps = FlexProps & { - innerRef?: RefObject; -}; +type StackItemProps = FlexProps; const StackItem = (props: StackItemProps) => { const { className, innerRef, ...rest } = props; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferenceWindow.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferenceWindow.tsx index d1592d1b1dca5..faca332e4d263 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferenceWindow.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferenceWindow.tsx @@ -36,7 +36,7 @@ export const GamePreferenceWindow = ( } return ( - }> + }> diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx index a29fa45410300..2d2333ab5e469 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx @@ -1,11 +1,13 @@ import { binaryInsertWith, sortBy } from 'common/collections'; -import type { InfernoNode } from 'inferno'; +import { useLocalState } from '../../backend'; +import type { Inferno, InfernoNode } from 'inferno'; import { useBackend } from '../../backend'; -import { Box, Flex, Tooltip } from '../../components'; +import { Box, Flex, Tooltip, Input, Icon } from '../../components'; import { PreferencesMenuData } from './data'; import features from './preferences/features'; import { FeatureValueInput } from './preferences/features/base'; import { TabbedMenu } from './TabbedMenu'; +import { createSearch } from 'common/string'; type PreferenceChild = { name: string; @@ -14,12 +16,11 @@ type PreferenceChild = { const binaryInsertPreference = binaryInsertWith((child) => child.name); -const sortByName = sortBy<[string, PreferenceChild[]]>(([name]) => name); - export const GamePreferencesPage = (props, context) => { const { act, data } = useBackend(context); + let [searchText, setSearchText] = useLocalState(context, 'game_prefs_searchText', ''); - const gamePreferences: Record = {}; + const gamePreferences: Record> = {}; for (const [featureId, value] of Object.entries(data.character_preferences.game_preferences)) { const feature = features[featureId]; @@ -53,9 +54,10 @@ export const GamePreferencesPage = (props, context) => { } const child = ( - - {name} - + + + {name} + {(feature && ) || ( @@ -72,22 +74,76 @@ export const GamePreferencesPage = (props, context) => { }; const category = feature?.category || 'ERROR'; + const subcategory = feature?.subcategory || ''; + const curCategory = gamePreferences[category] || []; + gamePreferences[category] = curCategory; - gamePreferences[category] = binaryInsertPreference(gamePreferences[category] || [], entry); + gamePreferences[category][subcategory] = binaryInsertPreference(curCategory[subcategory] || [], entry); } + const sortByName = sortBy(([name]) => name); + const gamePreferenceEntries: [string, InfernoNode][] = sortByName(Object.entries(gamePreferences)).map( - ([category, preferences]) => { - return [category, preferences.map((entry) => entry.children)]; + ([category, subcategory]) => { + let subcategories = sortByName(Object.entries(subcategory)); + return [ + category, + <> + {subcategories.map(([subcategory, preferences], index) => ( + + {subcategory?.length ? ( + + + + + {subcategory} + + + + + + ) : null} + {preferences.map((preference) => preference.children)} + + ))} + , + ]; } ); + const sortByNameTyped = sortBy<[string, Record]>(([name]) => name); + + const search = createSearch(searchText, (preference: PreferenceChild) => preference.name); + const searchResult: null | [string, InfernoNode][] = + searchText?.length > 0 + ? [ + [ + 'Search Result', + sortByNameTyped(Object.entries(gamePreferences)) + .flatMap(([_, subcategory]) => Object.entries(subcategory)) + .flatMap(([_, preferences]) => preferences) + .filter(search) + .map((preference) => preference.children), + ], + ] + : null; + + const result: [string, InfernoNode][] = searchResult || gamePreferenceEntries; + return ( + }}> + + + + + + setSearchText(value)} /> + + + ); }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx index 4dba7157f97aa..3e292e22ef182 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/TabbedMenu.tsx @@ -1,7 +1,8 @@ import { Component, createRef, RefObject } from 'inferno'; import type { InfernoNode } from 'inferno'; -import { Button, Section, Stack } from '../../components'; +import { Button, Section, Stack, Flex } from '../../components'; import { FlexProps } from '../../components/Flex'; +import { CollapsibleSection } from 'tgui/components/CollapsibleSection'; type TabbedMenuProps = { categoryEntries: [string, InfernoNode][]; @@ -23,37 +24,40 @@ export class TabbedMenu extends Component { render() { return ( - - - {this.props.categoryEntries.map(([category]) => { - return ( - - - - ); - })} - - + currentSection.scrollTop = offsetTop; + }}> + {category} + + + ); + })} + + + )} { // Otherwise, TypeScript complains about invalid prop className: undefined, }}> - + {this.props.categoryEntries.map(([category, children]) => { return ( - -
    + + {children} -
    -
    + + ); })} -
    +
    ); diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx index e8c214439a462..0f5d58673aac7 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx @@ -8,6 +8,7 @@ import { Box, Button, Dropdown, Icon, Input, NumberInput, Stack, Flex, Tooltip } import { createSetPreference, PreferencesMenuData } from '../../data'; import { ServerPreferencesFetcher } from '../../ServerPreferencesFetcher'; import features from '.'; +import { DropdownOptionalProps, DropdownProps } from 'tgui/components/Dropdown'; export const sortChoices = sortBy<[string, InfernoNode]>(([name]) => name); @@ -15,6 +16,7 @@ export type Feature = name: string; component: FeatureValue; category?: string; + subcategory?: string; description?: string; predictable?: boolean; small_supplemental?: boolean; @@ -103,7 +105,7 @@ export const CheckboxInputInverse = (props: FeatureValueProps( // Map of value to display texts choices: Record, - dropdownProps?: Record + dropdownProps?: DropdownOptionalProps ): FeatureValue => { return (props: FeatureValueProps) => { return ( diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/admin.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/admin.tsx index 336ec901d53ec..67211ce275200 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/admin.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/admin.tsx @@ -3,6 +3,7 @@ import { FeatureColorInput, Feature, FeatureToggle, CheckboxInput } from '../bas export const asaycolor: Feature = { name: 'Admin chat color', category: 'ADMIN', + subcategory: 'Chat', description: 'The color of your messages in Adminsay.', component: FeatureColorInput, }; @@ -10,6 +11,7 @@ export const asaycolor: Feature = { export const announce_login: FeatureToggle = { name: 'Announce Login', category: 'ADMIN', + subcategory: 'Misc', description: 'Whether you will announce whenever you login to fellow admins or not.', component: CheckboxInput, }; @@ -17,6 +19,7 @@ export const announce_login: FeatureToggle = { export const combohud_lighting: FeatureToggle = { name: 'Combo HUD Lighting', category: 'ADMIN', + subcategory: 'Misc', description: 'Whether you see combo HUD lighting as fullbright or not.', component: CheckboxInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ambient_occlusion.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ambient_occlusion.tsx index 8886a5da9db95..12d677dbcc619 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ambient_occlusion.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ambient_occlusion.tsx @@ -2,7 +2,8 @@ import { CheckboxInput, FeatureToggle } from '../base'; export const ambientocclusion: FeatureToggle = { name: 'Enable ambient occlusion', - category: 'GAMEPLAY', - description: 'Enable ambient occlusion, light shadows around characters.', + category: 'GRAPHICS', + subcategory: 'Quality', + description: 'Adds soft shadows around the edges of objects.', component: CheckboxInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/auto_fit_viewport.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/auto_fit_viewport.tsx index 31609990f18c6..b7099190eb1df 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/auto_fit_viewport.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/auto_fit_viewport.tsx @@ -2,6 +2,9 @@ import { CheckboxInput, FeatureToggle } from '../base'; export const auto_fit_viewport: FeatureToggle = { name: 'Auto fit viewport', - category: 'UI', + category: 'GRAPHICS', + subcategory: 'Scaling', + description: + 'Automatically resize the map panel to chat panel ratio to fit the map size, removing black bars from the edges of the view.', component: CheckboxInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/broadcast_login_logout.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/broadcast_login_logout.tsx deleted file mode 100644 index f8252412fc994..0000000000000 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/broadcast_login_logout.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { multiline } from 'common/string'; -import { CheckboxInput, FeatureToggle } from '../base'; - -export const broadcast_login_logout: FeatureToggle = { - name: 'Broadcast login/logout', - category: 'GAMEPLAY', - description: multiline` - When enabled, disconnecting and reconnecting will announce to deadchat. - `, - component: CheckboxInput, -}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/buttons_locked.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/buttons_locked.tsx index d82bc89306ea2..62bd39ec412ad 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/buttons_locked.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/buttons_locked.tsx @@ -2,7 +2,8 @@ import { CheckboxInput, FeatureToggle } from '../base'; export const buttons_locked: FeatureToggle = { name: 'Lock action buttons', - category: 'GAMEPLAY', + category: 'UI', + subcategory: 'HUD', description: 'When enabled, action buttons will be locked in place.', component: CheckboxInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/chat.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/chat.tsx index 9e2cd62979eb6..11f8983eba0d0 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/chat.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/chat.tsx @@ -4,6 +4,7 @@ import { FeatureToggle, CheckboxInput } from '../base'; export const chat_bankcard: FeatureToggle = { name: 'Enable Income Updates', category: 'CHAT', + subcategory: 'IC', description: 'Receive notifications for your bank account.', component: CheckboxInput, }; @@ -11,19 +12,23 @@ export const chat_bankcard: FeatureToggle = { export const chat_dead: FeatureToggle = { name: 'Enable Deadchat', category: 'ADMIN', + subcategory: 'Chat', component: CheckboxInput, }; export const chat_followghostmindless: FeatureToggle = { - name: 'Enable Mindless Ghost Follow Messages', + name: '(F) Mindless', category: 'GHOST', - description: 'When enabled, (F) will be prefixed on mindless mobs in deadchat.', + subcategory: 'Chat', + description: + 'When enabled, (F) will be prefixed on mindless mobs in deadchat. When disabled, it will only be shown on mobs with minds.', component: CheckboxInput, }; export const chat_ghostears: FeatureToggle = { - name: 'Hear All Messages', + name: 'Speech', category: 'GHOST', + subcategory: 'Chat', description: multiline` When enabled, you will be able to hear all speech as a ghost. When disabled, you will only be able to hear nearby speech. @@ -32,36 +37,41 @@ export const chat_ghostears: FeatureToggle = { }; export const chat_ghostlaws: FeatureToggle = { - name: 'Enable Law Change Updates', + name: 'Law Changes', category: 'GHOST', + subcategory: 'Chat', description: 'When enabled, be notified of any new law changes as a ghost.', component: CheckboxInput, }; export const chat_ghostpda: FeatureToggle = { - name: 'Enable PDA Messages', + name: 'PDA Messages', category: 'GHOST', + subcategory: 'Chat', description: 'When enabled, be notified of any PDA messages as a ghost.', component: CheckboxInput, }; export const chat_ghostradio: FeatureToggle = { - name: 'Enable Radio', + name: 'Radio', category: 'GHOST', + subcategory: 'Chat', description: 'When enabled, be notified of any radio messages as a ghost.', component: CheckboxInput, }; export const chat_ghostsight: FeatureToggle = { - name: 'See All Emotes', + name: 'Emotes', category: 'GHOST', + subcategory: 'Chat', description: 'When enabled, see all emotes as a ghost.', component: CheckboxInput, }; export const chat_ghostwhisper: FeatureToggle = { - name: 'See All Whispers', + name: 'Whispers', category: 'GHOST', + subcategory: 'Chat', description: multiline` When enabled, you will be able to hear all whispers as a ghost. When disabled, you will only be able to hear nearby whispers. @@ -69,28 +79,24 @@ export const chat_ghostwhisper: FeatureToggle = { component: CheckboxInput, }; -export const chat_login_logout: FeatureToggle = { - name: 'See Login/Logout Messages', - category: 'GHOST', - description: 'When enabled, be notified when a player logs in or out.', - component: CheckboxInput, -}; - export const chat_ooc: FeatureToggle = { name: 'Enable OOC', category: 'CHAT', + subcategory: 'OOC', component: CheckboxInput, }; export const chat_prayer: FeatureToggle = { - name: 'Listen to prayers', + name: 'Recieve prayers', category: 'ADMIN', + subcategory: 'Chat', component: CheckboxInput, }; export const chat_pullr: FeatureToggle = { name: 'Enable Pull Request Notifications', category: 'CHAT', + subcategory: 'OOC', description: 'Be notified when a pull request is made, closed, or merged.', component: CheckboxInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/deadmin.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/deadmin.tsx index c826526ad97b4..152e19d26b36e 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/deadmin.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/deadmin.tsx @@ -30,6 +30,7 @@ const DeadminCheckboxInput = (props: FeatureValueProps) => { { if (value === recommened) { handleSetValue(-1); } else { - handleSetValue(serverData?.recommended_fps || 60); + handleSetValue(serverData?.recommended_fps || 40); } }} width="100%" + buttons options={[recommened, 'Custom']} /> @@ -48,6 +50,8 @@ const FpsInput = (props: FeatureValueProps) => { export const clientfps: Feature = { name: 'FPS', - category: 'GAMEPLAY', + category: 'GRAPHICS', + subcategory: 'Quality', component: FpsInput, + predictable: false, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx index 82ae5e302f36d..c7bfb741b4f6e 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx @@ -10,6 +10,7 @@ import { PreferencesMenuData } from '../../../data'; export const ghost_accs: FeatureChoiced = { name: 'Ghost accessories', category: 'GHOST', + subcategory: 'Appearance', description: 'Determines what adjustments your ghost will have.', component: FeatureDropdownInput, }; @@ -40,7 +41,7 @@ const GhostFormInput = (props: FeatureValueProps + @@ -72,27 +73,29 @@ const GhostFormInput = (props: FeatureValueProps ); }; export const ghost_form: FeatureChoiced = { - name: 'Ghosts form', - category: 'GHOST', + name: 'Ghost Appearance', + category: 'BYOND MEMBER', description: 'The appearance of your ghost. Requires BYOND membership.', component: GhostFormInput, }; export const ghost_hud: FeatureToggle = { name: 'Ghost HUD', - category: 'GHOST', + category: 'UI', + subcategory: 'HUD', description: 'Enable HUD buttons for ghosts.', component: CheckboxInput, }; export const ghost_orbit: FeatureChoiced = { - name: 'Ghost orbit', - category: 'GHOST', + name: 'Ghost Orbit Shape', + category: 'BYOND MEMBER', description: multiline` The shape in which your ghost will orbit. Requires BYOND membership. @@ -107,6 +110,7 @@ export const ghost_orbit: FeatureChoiced = { export const ghost_others: FeatureChoiced = { name: 'Ghosts of others', category: 'GHOST', + subcategory: 'Appearance', description: multiline` Do you want the ghosts of others to show up as their own setting, as their default sprites, or always as the default white ghost? @@ -117,6 +121,7 @@ export const ghost_others: FeatureChoiced = { export const inquisitive_ghost: FeatureToggle = { name: 'Ghost inquisitiveness', category: 'GHOST', + subcategory: 'Behavior', description: 'Clicking on something as a ghost will examine it.', component: CheckboxInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/glasses.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/glasses.tsx index bd36af4818546..8c6050eb3a2c6 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/glasses.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/glasses.tsx @@ -2,7 +2,8 @@ import { FeatureToggle, CheckboxInput } from '../base'; export const glasses_color: FeatureToggle = { name: 'Enable glasses tint', - category: 'GAMEPLAY', - description: 'Glasses will tint your client color to match their color.', + category: 'GRAPHICS', + subcategory: 'Misc', + description: "Glasses will tint your entire screen's color to match their color.", component: CheckboxInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/item_outlines.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/item_outlines.tsx index 543d9fe64d59d..b17ab3e94fdec 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/item_outlines.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/item_outlines.tsx @@ -2,14 +2,16 @@ import { CheckboxInput, FeatureToggle, FeatureColorInput, Feature } from '../bas export const itemoutline_pref: FeatureToggle = { name: 'Item outlines', - category: 'GAMEPLAY', + category: 'UI', + subcategory: 'HUD', description: 'When enabled, hovering over items will outline them.', component: CheckboxInput, }; export const outline_color: Feature = { name: 'Item outline color', - category: 'GAMEPLAY', + category: 'UI', + subcategory: 'HUD', description: 'The color of that hovered items will outline with.', component: FeatureColorInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ooc.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ooc.tsx index a8ca03224a392..ed8eeeebc7fb7 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ooc.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ooc.tsx @@ -1,15 +1,16 @@ import { CheckboxInput, FeatureColorInput, FeatureToggle, Feature } from '../base'; export const ooccolor: Feature = { - name: 'OOC color', + name: 'OOC Color', category: 'CHAT', + subcategory: 'OOC', description: 'The color of your OOC messages.', component: FeatureColorInput, }; export const member_public: FeatureToggle = { name: 'Show BYOND Membership', - category: 'CHAT', + category: 'BYOND MEMBER', description: 'Whether to show your BYOND membership in OOC or not.', component: CheckboxInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/parallax.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/parallax.tsx index a7b143a6d3d0d..2b9c98a72bab1 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/parallax.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/parallax.tsx @@ -1,7 +1,8 @@ import { Feature, FeatureDropdownInput } from '../base'; export const parallax: Feature = { - name: 'Parallax (fancy space)', - category: 'GAMEPLAY', + name: 'Space Parallax', + category: 'GRAPHICS', + subcategory: 'Quality', component: FeatureDropdownInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/pixel_size.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/pixel_size.tsx index 4afde6a8bb1b8..0bd4c0e8c92b2 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/pixel_size.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/pixel_size.tsx @@ -2,12 +2,20 @@ import { createDropdownInput, Feature } from '../base'; export const pixel_size: Feature = { name: 'Pixel Scaling', - category: 'UI', - component: createDropdownInput({ - 0: 'Stretch to fit', - 1: 'Pixel Perfect 1x', - 1.5: 'Pixel Perfect 1.5x', - 2: 'Pixel Perfect 2x', - 3: 'Pixel Perfect 3x', - }), + category: 'GRAPHICS', + subcategory: 'Scaling', + description: + 'The size of the game and its icons within the map window. Stretch to fit works with all screen sizes, but Pixel Perfect will produce cleaner scaling, if any fit your window.', + component: createDropdownInput( + { + 0: 'Stretch to fit', + 1: 'Pixel Perfect 1x', + 1.5: 'Pixel Perfect 1.5x', + 2: 'Pixel Perfect 2x', + 3: 'Pixel Perfect 3x', + }, + { + buttons: true, + } + ), }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/rattle.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/rattle.tsx index 4d9b206842464..2212c5ba3f8ba 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/rattle.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/rattle.tsx @@ -1,15 +1,17 @@ import { CheckboxInput, FeatureToggle } from '../base'; export const arrivals_rattle: FeatureToggle = { - name: 'Notify for new arrivals', + name: 'Arrivals', category: 'GHOST', + subcategory: 'Chat', description: 'When enabled, you will be notified as a ghost for new crew.', component: CheckboxInput, }; export const death_rattle: FeatureToggle = { - name: 'Notify for deaths', + name: 'Deaths', category: 'GHOST', + subcategory: 'Chat', description: 'When enabled, you will be notified as a ghost whenever someone dies.', component: CheckboxInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/roundend.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/roundend.tsx index 3f43a120a0597..d582498976e9f 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/roundend.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/roundend.tsx @@ -2,7 +2,8 @@ import { FeatureToggle, CheckboxInput } from '../base'; export const show_credits: FeatureToggle = { name: 'Show Credits', - category: 'GAMEPLAY', + category: 'UI', + subcategory: 'HUD', description: 'Enables scrolling credit screen at roundend.', component: CheckboxInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/runechat.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/runechat.tsx index 355142231ae28..42153b5b32c1b 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/runechat.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/runechat.tsx @@ -2,35 +2,40 @@ import { Feature, CheckboxInput, FeatureDropdownInput, FeatureNumberInput, Featu export const chat_on_map: FeatureToggle = { name: 'Enable Runechat', - category: 'RUNECHAT', + category: 'CHAT', + subcategory: 'Runechat', description: 'Chat messages will show above heads.', component: CheckboxInput, }; export const see_chat_non_mob: FeatureToggle = { name: 'Enable Runechat on objects', - category: 'RUNECHAT', + category: 'CHAT', + subcategory: 'Runechat', description: 'Chat messages will show above objects when they speak.', component: CheckboxInput, }; export const see_rc_emotes: FeatureToggle = { name: 'Enable Runechat emotes', - category: 'RUNECHAT', + category: 'CHAT', + subcategory: 'Runechat', description: 'Emotes will show above heads.', component: CheckboxInput, }; export const max_chat_length: FeatureNumeric = { - name: 'Max chat length', - category: 'RUNECHAT', + name: 'Max runechat length', + category: 'CHAT', + subcategory: 'Runechat', description: 'The maximum length a Runechat message will show as.', component: FeatureNumberInput, }; export const show_balloon_alerts: Feature = { name: 'Show balloon alerts', - category: 'RUNECHAT', + category: 'CHAT', + subcategory: 'Runechat', description: 'Show text above items when certain interactions are used.', component: FeatureDropdownInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/scaling_method.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/scaling_method.tsx index 68a4c9c87fa30..e6066699ba22d 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/scaling_method.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/scaling_method.tsx @@ -2,10 +2,18 @@ import { createDropdownInput, Feature } from '../base'; export const scaling_method: Feature = { name: 'Scaling method', - category: 'UI', - component: createDropdownInput({ - blur: 'Bilinear', - distort: 'Nearest Neighbor', - normal: 'Point Sampling', - }), + category: 'GRAPHICS', + subcategory: 'Scaling', + description: + 'The scaling algorithm used by BYOND to resize game objects. Point sampling looks best, followed by Nearest Neighbor.', + component: createDropdownInput( + { + blur: 'Bilinear', + distort: 'Nearest Neighbor', + normal: 'Point Sampling', + }, + { + buttons: true, + } + ), }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/sound.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/sound.tsx index 56ef88f8a691e..33e8b460f02b2 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/sound.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/sound.tsx @@ -3,18 +3,22 @@ import { FeatureToggle, CheckboxInput } from '../base'; export const sound_adminhelp: FeatureToggle = { name: 'Enable adminhelp sounds', category: 'ADMIN', + subcategory: 'Sound', component: CheckboxInput, }; export const sound_ambience: FeatureToggle = { name: 'Enable ambience', category: 'SOUND', + subcategory: 'Ambience', + description: 'When enabled, plays various sounds depending on the area of the station you are in.', component: CheckboxInput, }; export const sound_announcements: FeatureToggle = { name: 'Enable announcement sounds', category: 'SOUND', + subcategory: 'IC', description: 'When enabled, hear sounds for command reports, notices, etc.', component: CheckboxInput, }; @@ -22,6 +26,7 @@ export const sound_announcements: FeatureToggle = { export const sound_instruments: FeatureToggle = { name: 'Enable instruments', category: 'SOUND', + subcategory: 'IC', description: 'When enabled, be able hear instruments in game.', component: CheckboxInput, }; @@ -29,12 +34,14 @@ export const sound_instruments: FeatureToggle = { export const sound_lobby: FeatureToggle = { name: 'Enable lobby music', category: 'SOUND', + subcategory: 'Music', component: CheckboxInput, }; export const sound_midi: FeatureToggle = { name: 'Enable admin music', category: 'SOUND', + subcategory: 'Music', description: 'When enabled, admins will be able to play music to you.', component: CheckboxInput, }; @@ -42,12 +49,14 @@ export const sound_midi: FeatureToggle = { export const sound_prayers: FeatureToggle = { name: 'Enable prayer sounds', category: 'ADMIN', + subcategory: 'Sound', component: CheckboxInput, }; export const sound_adminalert: FeatureToggle = { name: 'Enable admin alert sounds', category: 'ADMIN', + subcategory: 'Sound', description: 'Enables sound on various admin notifications such as midround and event triggers.', component: CheckboxInput, }; @@ -55,12 +64,15 @@ export const sound_adminalert: FeatureToggle = { export const sound_ship_ambience: FeatureToggle = { name: 'Enable ship ambience', category: 'SOUND', + subcategory: 'Ambience', + description: "Plays a soft droning sound, like that of a ship's engine.", component: CheckboxInput, }; export const sound_soundtrack: FeatureToggle = { name: 'Enable soundtrack music', category: 'SOUND', + subcategory: 'Music', description: 'When enabled, hear automatic soundtrack music triggered during situations like nuclear countdowns or xenomorph invasions.', component: CheckboxInput, diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tgui.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tgui.tsx index 940b15eda4e29..cdaf85c08dd42 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tgui.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tgui.tsx @@ -3,6 +3,7 @@ import { CheckboxInput, FeatureToggle } from '../base'; export const tgui_fancy: FeatureToggle = { name: 'Enable fancy tgui', category: 'UI', + subcategory: 'TGUI', description: 'Makes tgui windows look better, at the cost of compatibility.', component: CheckboxInput, }; @@ -10,48 +11,55 @@ export const tgui_fancy: FeatureToggle = { export const tgui_lock: FeatureToggle = { name: 'Lock tgui to main monitor', category: 'UI', + subcategory: 'TGUI', description: 'Locks tgui windows to your main monitor.', component: CheckboxInput, }; export const tgui_say_show_prefix: FeatureToggle = { - name: 'Say: Show Prefix', + name: 'Keep Radio Prefix', category: 'UI', + subcategory: 'TGUI Say', description: 'If radio prefixes should remain in the chatbox after being typed.', component: CheckboxInput, }; export const tgui_input: FeatureToggle = { - name: 'Input: Enable TGUI', + name: 'Enable TGUI Input', category: 'UI', - description: 'Renders input boxes in TGUI.', + subcategory: 'TGUI Input', + description: 'Renders input boxes in TGUI. If this is disabled, legacy input boxes will be uesd.', component: CheckboxInput, }; export const tgui_input_large: FeatureToggle = { - name: 'Input: Larger buttons', + name: 'Large Buttons', category: 'UI', - description: 'Makes TGUI buttons less traditional, more functional.', + subcategory: 'TGUI Input', + description: 'Makes TGUI buttons fill the size of the box.', component: CheckboxInput, }; export const tgui_input_swapped: FeatureToggle = { - name: 'Input: Swap Submit/Cancel buttons', + name: 'Swap Submit/Cancel buttons', category: 'UI', - description: 'Switches the location of the Submit and Cancel buttons. On means Submit will be on the left.', + subcategory: 'TGUI Input', + description: 'Switches the location of the Submit and Cancel buttons. "On" means Submit will be on the left.', component: CheckboxInput, }; export const tgui_say: FeatureToggle = { - name: 'Say: Enable TGUI', + name: 'Enable TGUI Say', category: 'UI', - description: 'Renders the Say input in TGUI.', + subcategory: 'TGUI Say', + description: 'Renders the Say input in TGUI. If disabled, a legacy input box will be used.', component: CheckboxInput, }; export const tgui_say_light_mode: FeatureToggle = { - name: 'Say: Light mode', + name: 'Light Mode', category: 'UI', + subcategory: 'TGUI Say', description: 'Sets TGUI Say to use light mode.', component: CheckboxInput, }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tooltips.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tooltips.tsx index 62bfe8b4f5d30..3ce06c0bd999a 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tooltips.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/tooltips.tsx @@ -3,7 +3,8 @@ import { CheckboxInput, Feature, FeatureNumberInput, FeatureToggle } from '../ba export const enable_tips: FeatureToggle = { name: 'Enable tooltips', - category: 'TOOLTIPS', + category: 'UI', + subcategory: 'Tooltips', description: multiline` Do you want to see tooltips when hovering over items? `, @@ -12,7 +13,8 @@ export const enable_tips: FeatureToggle = { export const tip_delay: Feature = { name: 'Tooltip delay (in milliseconds)', - category: 'TOOLTIPS', + category: 'UI', + subcategory: 'Tooltips', description: multiline` How long should it take to see a tooltip when hovering over items? `, diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ui_style.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ui_style.tsx index 186d4c20d2ebc..013ea7f984b84 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ui_style.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ui_style.tsx @@ -36,6 +36,7 @@ const UIStyleInput = (props: FeatureValueProps Date: Thu, 22 Jun 2023 00:08:18 -0400 Subject: [PATCH 195/269] Fix preferred map not showing Default properly --- code/controllers/subsystem/mapping.dm | 2 +- code/controllers/subsystem/vote.dm | 2 +- .../client/preferences/entries/player/preferred_map.dm | 10 +++++----- .../features/game_preferences/preferred_map.tsx | 8 +++++--- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index c432d1e687662..947564bead401 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -344,7 +344,7 @@ GLOBAL_LIST_EMPTY(the_station_areas) if(pmv) for (var/client/c in GLOB.clients) var/vote = c.prefs.read_player_preference(/datum/preference/choiced/preferred_map) - if (!vote) + if (!vote || vote == "Default") if (global.config.defaultmap) mapvotes[global.config.defaultmap.map_name] += 1 continue diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm index ba5679e0a45fd..97cb73e7ffa27 100644 --- a/code/controllers/subsystem/vote.dm +++ b/code/controllers/subsystem/vote.dm @@ -69,7 +69,7 @@ SUBSYSTEM_DEF(vote) for (var/non_voter_ckey in non_voters) var/client/C = non_voters[non_voter_ckey] var/preferred_map = C.prefs.read_player_preference(/datum/preference/choiced/preferred_map) - if(preferred_map) + if(preferred_map && preferred_map != "Default") choices[preferred_map] += 1 greatest_votes = max(greatest_votes, choices[preferred_map]) else if(global.config.defaultmap) diff --git a/code/modules/client/preferences/entries/player/preferred_map.dm b/code/modules/client/preferences/entries/player/preferred_map.dm index 9bdcf15bc84e6..a6fa091a02223 100644 --- a/code/modules/client/preferences/entries/player/preferred_map.dm +++ b/code/modules/client/preferences/entries/player/preferred_map.dm @@ -6,7 +6,7 @@ /datum/preference/choiced/preferred_map/init_possible_values() var/list/maps = list() - maps += "" + maps += "Default" for (var/map in config.maplist) var/datum/map_config/map_config = config.maplist[map] @@ -18,7 +18,7 @@ return maps /datum/preference/choiced/preferred_map/create_default_value() - return "" + return "Default" /datum/preference/choiced/preferred_map/compile_constant_data() var/list/data = ..() @@ -26,12 +26,12 @@ var/display_names = list() if (config.defaultmap) - display_names[""] = "Default ([config.defaultmap.map_name])" + display_names["Default"] = "Default ([config.defaultmap.map_name])" else - display_names[""] = "Default" + display_names["Default"] = "Default" for (var/choice in get_choices()) - if (choice == "") + if (choice == "Default") continue var/datum/map_config/map_config = config.maplist[choice] diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/preferred_map.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/preferred_map.tsx index 8d3c8b1742699..faa2b77274d3e 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/preferred_map.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/preferred_map.tsx @@ -1,7 +1,7 @@ import { multiline } from 'common/string'; -import { Feature, FeatureDropdownInput } from '../base'; +import { FeatureChoicedServerData, FeatureValueProps, FeatureChoiced, FeatureDropdownInput } from '../base'; -export const preferred_map: Feature = { +export const preferred_map: FeatureChoiced = { name: 'Preferred map', category: 'GAMEPLAY', description: multiline` @@ -9,5 +9,7 @@ export const preferred_map: Feature = { This does not affect the map vote, only random rotation when a vote is not held. `, - component: FeatureDropdownInput, + component: (props: FeatureValueProps) => { + return ; + }, }; From 9d8f1d4b6cab3cf1b8abdd95bab667772641f7ef Mon Sep 17 00:00:00 2001 From: itsmeow Date: Thu, 22 Jun 2023 00:22:08 -0400 Subject: [PATCH 196/269] Add categories to search results --- .../PreferencesMenu/GamePreferencesPage.tsx | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx index 2d2333ab5e469..372b6cd23b219 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx @@ -120,10 +120,27 @@ export const GamePreferencesPage = (props, context) => { [ 'Search Result', sortByNameTyped(Object.entries(gamePreferences)) - .flatMap(([_, subcategory]) => Object.entries(subcategory)) - .flatMap(([_, preferences]) => preferences) - .filter(search) - .map((preference) => preference.children), + .flatMap(([category, categoryObj]) => + Object.entries(categoryObj).map<[string, PreferenceChild[]]>(([k, v]) => [category + (k ? ' > ' + k : ''), v]) + ) + .filter(([_, preferences]) => preferences.some(search)) + .map(([subcategory, preferences], index) => ( + + {subcategory?.length ? ( + + + + + {subcategory} + + + + + + ) : null} + {preferences.filter(search).map((preference) => preference.children)} + + )), ], ] : null; From e71b6a138d5ef71e358c48ff5e598f8d441104dc Mon Sep 17 00:00:00 2001 From: itsmeow Date: Thu, 22 Jun 2023 00:27:34 -0400 Subject: [PATCH 197/269] Add some more blue eye presets --- .../features/character_preferences/species_features.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx index 2ded90d6c6664..63ab5355bc5f3 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx @@ -4,6 +4,8 @@ const eyePresets = { // these need to be short color (3 byte) compatible '#aaccff': 'Baby Blue', '#0099bb': 'Blue-Green', + '#3399ff': 'Light Blue', + '#0066ff': 'Blue', '#337788': 'Teal Blue', '#005577': 'Dark Cerulean Blue', '#889988': 'Artichoke Green', From c028a10740307b77b235fed01ec64518e421e0db Mon Sep 17 00:00:00 2001 From: itsmeow Date: Fri, 23 Jun 2023 00:34:06 -0400 Subject: [PATCH 198/269] Rename body_type to body_model --- SQL/beestation_schema.sql | 2 +- beestation.dme | 2 +- .../character/{body_type.dm => body_model.dm} | 12 ++++++------ code/modules/client/preferences/preference_entry.dm | 4 ++-- .../features/character_preferences/body_type.tsx | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) rename code/modules/client/preferences/entries/character/{body_type.dm => body_model.dm} (71%) diff --git a/SQL/beestation_schema.sql b/SQL/beestation_schema.sql index 52621d0450362..90c619a7e5bf3 100644 --- a/SQL/beestation_schema.sql +++ b/SQL/beestation_schema.sql @@ -97,7 +97,7 @@ CREATE TABLE IF NOT EXISTS `SS13_characters` ( `name_is_always_random` TINYINT(1), `body_is_always_random` TINYINT(1), `gender` VARCHAR(16) COLLATE 'utf8mb4_general_ci', - `body_type` VARCHAR(16) COLLATE 'utf8mb4_general_ci', + `body_model` VARCHAR(16) COLLATE 'utf8mb4_general_ci', `body_size` VARCHAR(16) COLLATE 'utf8mb4_general_ci', `age` TINYINT(3) UNSIGNED, `hair_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci', diff --git a/beestation.dme b/beestation.dme index 5f6b7c5d6ff4f..a976a229bcb3f 100644 --- a/beestation.dme +++ b/beestation.dme @@ -2136,7 +2136,7 @@ #include "code\modules\client\preferences\preferences.dm" #include "code\modules\client\preferences\entries\character\age.dm" #include "code\modules\client\preferences\entries\character\ai_core_display.dm" -#include "code\modules\client\preferences\entries\character\body_type.dm" +#include "code\modules\client\preferences\entries\character\body_model.dm" #include "code\modules\client\preferences\entries\character\clothing.dm" #include "code\modules\client\preferences\entries\character\gender.dm" #include "code\modules\client\preferences\entries\character\names.dm" diff --git a/code/modules/client/preferences/entries/character/body_type.dm b/code/modules/client/preferences/entries/character/body_model.dm similarity index 71% rename from code/modules/client/preferences/entries/character/body_type.dm rename to code/modules/client/preferences/entries/character/body_model.dm index b317f86586798..e48b0925f2284 100644 --- a/code/modules/client/preferences/entries/character/body_type.dm +++ b/code/modules/client/preferences/entries/character/body_model.dm @@ -1,19 +1,19 @@ -/datum/preference/choiced/body_type +/datum/preference/choiced/body_model category = PREFERENCE_CATEGORY_SECONDARY_FEATURES - priority = PREFERENCE_PRIORITY_BODY_TYPE - db_key = "body_type" + priority = PREFERENCE_PRIORITY_BODY_MODEL + db_key = "body_model" preference_type = PREFERENCE_CHARACTER -/datum/preference/choiced/body_type/init_possible_values() +/datum/preference/choiced/body_model/init_possible_values() return list(MALE, FEMALE) -/datum/preference/choiced/body_type/apply_to_human(mob/living/carbon/human/target, value) +/datum/preference/choiced/body_model/apply_to_human(mob/living/carbon/human/target, value) if (target.gender != MALE && target.gender != FEMALE) target.dna.features["body_model"] = value else target.dna.features["body_model"] = target.gender -/datum/preference/choiced/body_type/is_accessible(datum/preferences/preferences, ignore_page = FALSE) +/datum/preference/choiced/body_model/is_accessible(datum/preferences/preferences, ignore_page = FALSE) if (!..()) return FALSE diff --git a/code/modules/client/preferences/preference_entry.dm b/code/modules/client/preferences/preference_entry.dm index 39e5756d6e6a7..05a66e30aba2d 100644 --- a/code/modules/client/preferences/preference_entry.dm +++ b/code/modules/client/preferences/preference_entry.dm @@ -8,9 +8,9 @@ /// The priority at which gender is determined, needed for proper randomization. #define PREFERENCE_PRIORITY_GENDER 3 -/// The priority at which body type is decided, applied after gender so we can +/// The priority at which body model is decided, applied after gender so we can /// make sure they're non-binary. -#define PREFERENCE_PRIORITY_BODY_TYPE 4 +#define PREFERENCE_PRIORITY_BODY_MODEL 4 /// The priority at which names are decided, needed for proper randomization. #define PREFERENCE_PRIORITY_NAMES 5 diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/body_type.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/body_type.tsx index a297a0e04394c..001eaa8194955 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/body_type.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/body_type.tsx @@ -1,6 +1,6 @@ import { FeatureChoiced, FeatureDropdownInput } from '../base'; -export const body_type: FeatureChoiced = { +export const body_model: FeatureChoiced = { name: 'Body Type', component: FeatureDropdownInput, }; From ca7aa94832bc0fdf938e26700a81ba007f076d22 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Fri, 23 Jun 2023 00:36:35 -0400 Subject: [PATCH 199/269] Revert "Remove felinid ears/tail pref" This reverts commit d59876d38019196d201869f95df1abd1021d3452. --- SQL/beestation_schema.sql | 2 ++ beestation.dme | 1 + .../character/species_features/felinid.dm | 34 +++++++++++++++++++ .../species_features.tsx | 10 ++++++ 4 files changed, 47 insertions(+) create mode 100644 code/modules/client/preferences/entries/character/species_features/felinid.dm diff --git a/SQL/beestation_schema.sql b/SQL/beestation_schema.sql index 90c619a7e5bf3..bf46b97bdb054 100644 --- a/SQL/beestation_schema.sql +++ b/SQL/beestation_schema.sql @@ -137,6 +137,8 @@ CREATE TABLE IF NOT EXISTS `SS13_characters` ( `feature_lizard_spines` VARCHAR(64) COLLATE 'utf8mb4_general_ci', `feature_lizard_tail` VARCHAR(64) COLLATE 'utf8mb4_general_ci', `feature_mcolor` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `feature_human_tail` VARCHAR(64) COLLATE 'utf8mb4_general_ci', + `feature_human_ears` VARCHAR(64) COLLATE 'utf8mb4_general_ci', `helmet_style` VARCHAR(64) COLLATE 'utf8mb4_general_ci', `preferred_ai_core_display` VARCHAR(64) COLLATE 'utf8mb4_general_ci', `preferred_security_department` VARCHAR(32) COLLATE 'utf8mb4_general_ci', diff --git a/beestation.dme b/beestation.dme index a976a229bcb3f..d93782f4f586b 100644 --- a/beestation.dme +++ b/beestation.dme @@ -2150,6 +2150,7 @@ #include "code\modules\client\preferences\entries\character\species_features\apid.dm" #include "code\modules\client\preferences\entries\character\species_features\basic.dm" #include "code\modules\client\preferences\entries\character\species_features\ethereal.dm" +#include "code\modules\client\preferences\entries\character\species_features\felinid.dm" #include "code\modules\client\preferences\entries\character\species_features\ipc.dm" #include "code\modules\client\preferences\entries\character\species_features\lizard.dm" #include "code\modules\client\preferences\entries\character\species_features\moth.dm" diff --git a/code/modules/client/preferences/entries/character/species_features/felinid.dm b/code/modules/client/preferences/entries/character/species_features/felinid.dm new file mode 100644 index 0000000000000..bd2e340a48e1f --- /dev/null +++ b/code/modules/client/preferences/entries/character/species_features/felinid.dm @@ -0,0 +1,34 @@ +/datum/preference/choiced/tail_human + db_key = "feature_human_tail" + preference_type = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + can_randomize = FALSE + relevant_mutant_bodypart = "tail_human" + +/datum/preference/choiced/tail_human/init_possible_values() + return assoc_to_keys(GLOB.tails_roundstart_list_human) + +/datum/preference/choiced/tail_human/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["tail_human"] = value + +/datum/preference/choiced/tail_human/create_default_value() + var/datum/sprite_accessory/tails/human/cat/tail = /datum/sprite_accessory/tails/human/cat + return initial(tail.name) + +/datum/preference/choiced/ears + db_key = "feature_human_ears" + preference_type = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + can_randomize = FALSE + relevant_mutant_bodypart = "ears" + +/datum/preference/choiced/ears/init_possible_values() + return assoc_to_keys(GLOB.ears_list) + +/datum/preference/choiced/ears/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["ears"] = value + +/datum/preference/choiced/ears/create_default_value() + var/datum/sprite_accessory/ears/cat/ears = /datum/sprite_accessory/ears/cat + return initial(ears.name) + diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx index 63ab5355bc5f3..47edb0dade0ff 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx @@ -200,3 +200,13 @@ export const feature_ipc_screen_color: Feature = { name: 'Screen Color', component: FeatureColorInput, }; + +export const feature_human_tail: FeatureChoiced = { + name: 'Tail', + component: FeatureDropdownInput, +}; + +export const feature_human_ears: FeatureChoiced = { + name: 'Ears', + component: FeatureDropdownInput, +}; From 12417ca111d4ae075f6a1a7ea9dadd8262d3d867 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Fri, 23 Jun 2023 00:51:47 -0400 Subject: [PATCH 200/269] Update ss13_preferences schema --- SQL/beestation_schema.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SQL/beestation_schema.sql b/SQL/beestation_schema.sql index bf46b97bdb054..0303230ea40c9 100644 --- a/SQL/beestation_schema.sql +++ b/SQL/beestation_schema.sql @@ -440,7 +440,7 @@ CREATE TABLE IF NOT EXISTS `SS13_poll_vote` ( DROP TABLE IF EXISTS `SS13_preferences`; CREATE TABLE `SS13_preferences` ( `ckey` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci', - `preference_tag` INT(11) NOT NULL, + `preference_tag` VARCHAR(255) NOT NULL COLLATE 'utf8mb4_general_ci', `preference_value` MEDIUMTEXT NULL COLLATE 'utf8mb4_general_ci', UNIQUE INDEX `prefbinding` (`ckey`, `preference_tag`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; From 58958a0f8915725acc8a10a504141ecbddffc800 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Fri, 23 Jun 2023 00:52:11 -0400 Subject: [PATCH 201/269] Antenna -> Antennae for apids display name. Code later --- .../preferences/entries/character/species_features/apid.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/client/preferences/entries/character/species_features/apid.dm b/code/modules/client/preferences/entries/character/species_features/apid.dm index 0abf7d15ab05a..be6c91bfd3726 100644 --- a/code/modules/client/preferences/entries/character/species_features/apid.dm +++ b/code/modules/client/preferences/entries/character/species_features/apid.dm @@ -33,7 +33,7 @@ db_key = "feature_apid_antenna" preference_type = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_FEATURES - main_feature_name = "Antenna Style" + main_feature_name = "Antennae Style" should_generate_icons = TRUE relevant_mutant_bodypart = "apid_antenna" From 89a90377b27c82fbe74eb5f009ad2222810e4366 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Fri, 23 Jun 2023 00:59:02 -0400 Subject: [PATCH 202/269] Add flypeople insect_type preference --- beestation.dme | 1 + .../entries/character/species_features/fly.dm | 11 +++++++++++ .../living/carbon/human/species_types/flypeople.dm | 2 +- .../character_preferences/species_features.tsx | 5 +++++ 4 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 code/modules/client/preferences/entries/character/species_features/fly.dm diff --git a/beestation.dme b/beestation.dme index d93782f4f586b..dae9e90006d5e 100644 --- a/beestation.dme +++ b/beestation.dme @@ -2151,6 +2151,7 @@ #include "code\modules\client\preferences\entries\character\species_features\basic.dm" #include "code\modules\client\preferences\entries\character\species_features\ethereal.dm" #include "code\modules\client\preferences\entries\character\species_features\felinid.dm" +#include "code\modules\client\preferences\entries\character\species_features\fly.dm" #include "code\modules\client\preferences\entries\character\species_features\ipc.dm" #include "code\modules\client\preferences\entries\character\species_features\lizard.dm" #include "code\modules\client\preferences\entries\character\species_features\moth.dm" diff --git a/code/modules/client/preferences/entries/character/species_features/fly.dm b/code/modules/client/preferences/entries/character/species_features/fly.dm new file mode 100644 index 0000000000000..a471a6732a617 --- /dev/null +++ b/code/modules/client/preferences/entries/character/species_features/fly.dm @@ -0,0 +1,11 @@ +/datum/preference/choiced/insect_type + db_key = "feature_insect_type" + preference_type = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + relevant_mutant_bodypart = "insect_type" + +/datum/preference/choiced/insect_type/init_possible_values() + return assoc_to_keys(GLOB.insect_type_list) + +/datum/preference/choiced/insect_type/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["insect_type"] = value diff --git a/code/modules/mob/living/carbon/human/species_types/flypeople.dm b/code/modules/mob/living/carbon/human/species_types/flypeople.dm index 2cfaa44d614c9..78af4c52160f5 100644 --- a/code/modules/mob/living/carbon/human/species_types/flypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/flypeople.dm @@ -10,7 +10,7 @@ mutantstomach = /obj/item/organ/stomach/fly meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/fly mutant_bodyparts = list("insect_type") - default_features = list("insect_type" = "housefly", "body_size" = "Normal") + default_features = list("insect_type" = "fly", "body_size" = "Normal") burnmod = 1.4 brutemod = 1.4 speedmod = 0.7 diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx index 47edb0dade0ff..b9e82571e714e 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/species_features.tsx @@ -210,3 +210,8 @@ export const feature_human_ears: FeatureChoiced = { name: 'Ears', component: FeatureDropdownInput, }; + +export const feature_insect_type: FeatureChoiced = { + name: 'Insect Type', + component: FeatureDropdownInput, +}; From eb476fdfb7134e21844012f8d479b45f0c3b203d Mon Sep 17 00:00:00 2001 From: itsmeow Date: Fri, 23 Jun 2023 01:06:13 -0400 Subject: [PATCH 203/269] Fix fly insect type pref not even working --- .../mob/dead/new_player/sprite_accessories.dm | 2 ++ code/modules/mob/living/carbon/human/dummy.dm | 1 + .../carbon/human/species_types/flypeople.dm | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/code/modules/mob/dead/new_player/sprite_accessories.dm b/code/modules/mob/dead/new_player/sprite_accessories.dm index 503ce44639039..ab0f6af9a3c04 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories.dm @@ -2457,10 +2457,12 @@ /datum/sprite_accessory/insect_type/fly name = "Common Fly" limbs_id = "fly" + gender_specific = FALSE /datum/sprite_accessory/insect_type/bee name = "Hoverfly" limbs_id = "bee" + gender_specific = TRUE /datum/sprite_accessory/ipc_chassis/mcgreyscale name = "Morpheus Cyberkinetics (Custom)" diff --git a/code/modules/mob/living/carbon/human/dummy.dm b/code/modules/mob/living/carbon/human/dummy.dm index 5600c9d2975a3..f39277d3697c4 100644 --- a/code/modules/mob/living/carbon/human/dummy.dm +++ b/code/modules/mob/living/carbon/human/dummy.dm @@ -48,6 +48,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy) dna.features["apid_stripes"] = "thick" dna.features["apid_headstripes"] = "thick" dna.features["apid_antenna"] = "curled" + dna.features["insect_type"] = "fly" dna.features["ipc_screen"] = "BSOD" dna.features["ipc_antenna"] = "None" dna.features["ipc_chassis"] = "Morpheus Cyberkinetics (Custom)" diff --git a/code/modules/mob/living/carbon/human/species_types/flypeople.dm b/code/modules/mob/living/carbon/human/species_types/flypeople.dm index 78af4c52160f5..65bca74b4973b 100644 --- a/code/modules/mob/living/carbon/human/species_types/flypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/flypeople.dm @@ -40,6 +40,25 @@ return TRUE return ..() +/datum/species/fly/replace_body(mob/living/carbon/C, datum/species/new_species) + ..() + + var/datum/sprite_accessory/insect_type/type_selection = GLOB.insect_type_list[C.dna.features["insect_type"]] + if(!istype(type_selection)) + return + + for(var/obj/item/bodypart/BP as() in C.bodyparts) //Override bodypart data as necessary + BP.uses_mutcolor = !!type_selection.color_src + if(BP.uses_mutcolor) + BP.should_draw_greyscale = TRUE + BP.species_color = C.dna?.features["mcolor"] + // Hardcoded bullshit that will probably break. Woo shitcode. Bee insect_type has dimorphic parts while flies do not. + BP.is_dimorphic = type_selection.gender_specific && (istype(BP, /obj/item/bodypart/head) || istype(BP, /obj/item/bodypart/chest)) + + BP.limb_id = type_selection.limbs_id + BP.name = "\improper[type_selection.name] [parse_zone(BP.body_zone)]" + BP.update_limb() + /datum/species/fly/check_species_weakness(obj/item/weapon, mob/living/attacker) if(istype(weapon, /obj/item/melee/flyswatter)) return 29 //Flyswatters deal 30x damage to flypeople. From 07cc443e993d9c36d07c500a64162caccf71cd8b Mon Sep 17 00:00:00 2001 From: itsmeow Date: Fri, 23 Jun 2023 01:18:02 -0400 Subject: [PATCH 204/269] Fix tsc --- tgui/packages/tgui/components/Icon.tsx | 2 +- tgui/packages/tgui/interfaces/PaiInterface.tsx | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tgui/packages/tgui/components/Icon.tsx b/tgui/packages/tgui/components/Icon.tsx index 2d2c737074d79..b1a3bc6165d9c 100644 --- a/tgui/packages/tgui/components/Icon.tsx +++ b/tgui/packages/tgui/components/Icon.tsx @@ -16,7 +16,7 @@ type IconPropsUnique = { spin?: boolean; className?: string; rotation?: number; - style?: string | CSSProperties; + style?: string | Record; }; export type IconProps = IconPropsUnique & BoxProps; diff --git a/tgui/packages/tgui/interfaces/PaiInterface.tsx b/tgui/packages/tgui/interfaces/PaiInterface.tsx index 4c9d6d1075d30..b4d3acb86e140 100644 --- a/tgui/packages/tgui/interfaces/PaiInterface.tsx +++ b/tgui/packages/tgui/interfaces/PaiInterface.tsx @@ -1,3 +1,4 @@ +import { BooleanLike } from 'common/react'; import { useBackend, useSharedState } from '../backend'; import { Box, Button, LabeledList, Icon, NoticeBox, ProgressBar, Section, Stack, Table, Tabs, Tooltip } from '../components'; import { Window } from '../layouts'; @@ -13,7 +14,7 @@ type PaiInterfaceData = { master: Master; ram: number; records: Records; - refresh_spam: number; + refresh_spam: BooleanLike; }; type Available = { @@ -348,7 +349,7 @@ const RecordsDisplay = (props, context) => { From 3c387cc51a87490537f7cf2a857dd1e9e010b2ab Mon Sep 17 00:00:00 2001 From: itsmeow Date: Fri, 23 Jun 2023 02:05:05 -0400 Subject: [PATCH 205/269] Fix human ears/tail prefs not fuckin working (OH MY GOD FUCK ORGANS CODE) --- code/datums/diseases/advance/symptoms/clockwork.dm | 10 ++++++++-- code/game/objects/items/body_egg.dm | 4 ++-- .../antagonists/abductor/equipment/gland.dm | 10 +++++----- code/modules/antagonists/slaughter/slaughter.dm | 4 ++-- code/modules/mining/equipment/regenerative_core.dm | 4 ++-- code/modules/mob/living/brain/brain_item.dm | 4 ++-- code/modules/mob/living/carbon/alien/organs.dm | 12 ++++++------ .../living/carbon/human/species_types/felinid.dm | 10 +++++----- .../carbon/human/species_types/shadowpeople.dm | 8 ++++---- code/modules/mob/living/carbon/monkey/monkey.dm | 2 +- code/modules/surgery/organs/appendix.dm | 4 ++-- code/modules/surgery/organs/augments_arms.dm | 4 ++-- code/modules/surgery/organs/augments_chest.dm | 4 ++-- code/modules/surgery/organs/augments_eyes.dm | 4 ++-- code/modules/surgery/organs/augments_internal.dm | 8 ++++---- code/modules/surgery/organs/ears.dm | 14 ++++++++++---- code/modules/surgery/organs/eyes.dm | 10 +++++----- code/modules/surgery/organs/heart.dm | 6 +++--- code/modules/surgery/organs/organ_internal.dm | 6 +++--- code/modules/surgery/organs/stomach.dm | 10 +++++----- code/modules/surgery/organs/tails.dm | 13 ++++++++++--- code/modules/surgery/organs/tongue.dm | 2 +- code/modules/surgery/organs/wings.dm | 6 +++--- code/modules/zombie/organs.dm | 4 ++-- 24 files changed, 91 insertions(+), 72 deletions(-) diff --git a/code/datums/diseases/advance/symptoms/clockwork.dm b/code/datums/diseases/advance/symptoms/clockwork.dm index 2ca29bf06d2db..bbc63452af826 100644 --- a/code/datums/diseases/advance/symptoms/clockwork.dm +++ b/code/datums/diseases/advance/symptoms/clockwork.dm @@ -334,16 +334,22 @@ organ_flags = ORGAN_SYNTHETIC status = ORGAN_ROBOTIC -/obj/item/organ/tail/clockwork/Insert(mob/living/carbon/human/H, special = 0, drop_if_replaced = TRUE) +/obj/item/organ/tail/clockwork/Insert(mob/living/carbon/human/H, special = 0, drop_if_replaced = TRUE, pref_load = FALSE) ..() + if(pref_load && istype(H)) + H.update_body() + return if(istype(H)) if(!("tail_human" in H.dna.species.mutant_bodyparts)) H.dna.features["tail_human"] = tail_type H.dna.species.mutant_bodyparts |= "tail_human" H.update_body() -/obj/item/organ/tail/clockwork/Remove(mob/living/carbon/human/H, special = 0) +/obj/item/organ/tail/clockwork/Remove(mob/living/carbon/human/H, special = 0, pref_load = FALSE) ..() + if(pref_load && istype(H)) + H.update_body() + return if(istype(H)) H.dna.species.mutant_bodyparts -= "tail_human" H.update_body() diff --git a/code/game/objects/items/body_egg.dm b/code/game/objects/items/body_egg.dm index a51259a88bbec..19a17837f61c4 100644 --- a/code/game/objects/items/body_egg.dm +++ b/code/game/objects/items/body_egg.dm @@ -14,7 +14,7 @@ src.Insert(loc) return ..() -/obj/item/organ/body_egg/Insert(var/mob/living/carbon/M, special = 0) +/obj/item/organ/body_egg/Insert(var/mob/living/carbon/M, special = 0, pref_load = FALSE) ..() ADD_TRAIT(owner, TRAIT_XENO_HOST, TRAIT_GENERIC) ADD_TRAIT(owner, TRAIT_XENO_IMMUNE, "xeno immune") @@ -22,7 +22,7 @@ owner.med_hud_set_status() INVOKE_ASYNC(src, PROC_REF(AddInfectionImages), owner) -/obj/item/organ/body_egg/Remove(var/mob/living/carbon/M, special = 0) +/obj/item/organ/body_egg/Remove(var/mob/living/carbon/M, special = 0, pref_load = FALSE) if(owner) REMOVE_TRAIT(owner, TRAIT_XENO_HOST, TRAIT_GENERIC) REMOVE_TRAIT(owner, TRAIT_XENO_IMMUNE, "xeno immune") diff --git a/code/modules/antagonists/abductor/equipment/gland.dm b/code/modules/antagonists/abductor/equipment/gland.dm index 7a4a1f1ac856b..6f9badfb740cb 100644 --- a/code/modules/antagonists/abductor/equipment/gland.dm +++ b/code/modules/antagonists/abductor/equipment/gland.dm @@ -71,7 +71,7 @@ owner.clear_alert("mind_control") active_mind_control = FALSE -/obj/item/organ/heart/gland/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/heart/gland/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) active = 0 if(initial(uses) == 1) uses = initial(uses) @@ -131,12 +131,12 @@ mind_control_uses = 1 mind_control_duration = 2400 -/obj/item/organ/heart/gland/slime/Insert(mob/living/carbon/M, special = 0) +/obj/item/organ/heart/gland/slime/Insert(mob/living/carbon/M, special = 0, pref_load = FALSE) ..() owner.faction |= "slime" owner.grant_language(/datum/language/slime, TRUE, TRUE, LANGUAGE_GLAND) -/obj/item/organ/heart/gland/slime/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/heart/gland/slime/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) ..() owner.faction -= "slime" owner.remove_language(/datum/language/slime, TRUE, TRUE, LANGUAGE_GLAND) @@ -298,11 +298,11 @@ mind_control_uses = 2 mind_control_duration = 900 -/obj/item/organ/heart/gland/electric/Insert(mob/living/carbon/M, special = 0) +/obj/item/organ/heart/gland/electric/Insert(mob/living/carbon/M, special = 0, pref_load = FALSE) ..() ADD_TRAIT(owner, TRAIT_SHOCKIMMUNE, ORGAN_TRAIT) -/obj/item/organ/heart/gland/electric/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/heart/gland/electric/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) REMOVE_TRAIT(owner, TRAIT_SHOCKIMMUNE, ORGAN_TRAIT) ..() diff --git a/code/modules/antagonists/slaughter/slaughter.dm b/code/modules/antagonists/slaughter/slaughter.dm index 13a1d60c98416..fc7269fbf6afb 100644 --- a/code/modules/antagonists/slaughter/slaughter.dm +++ b/code/modules/antagonists/slaughter/slaughter.dm @@ -97,12 +97,12 @@ user.temporarilyRemoveItemFromInventory(src, TRUE) src.Insert(user) //Consuming the heart literally replaces your heart with a demon heart. H A R D C O R E -/obj/item/organ/heart/demon/Insert(mob/living/carbon/M, special = 0) +/obj/item/organ/heart/demon/Insert(mob/living/carbon/M, special = 0, pref_load = FALSE) ..() if(M.mind) M.mind.AddSpell(new /obj/effect/proc_holder/spell/bloodcrawl(null)) -/obj/item/organ/heart/demon/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/heart/demon/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) ..() if(M.mind) M.mind.RemoveSpell(/obj/effect/proc_holder/spell/bloodcrawl) diff --git a/code/modules/mining/equipment/regenerative_core.dm b/code/modules/mining/equipment/regenerative_core.dm index 2fb07c0fd390f..108dc6d6e6ac6 100644 --- a/code/modules/mining/equipment/regenerative_core.dm +++ b/code/modules/mining/equipment/regenerative_core.dm @@ -109,13 +109,13 @@ if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) applyto(user, user) -/obj/item/organ/regenerative_core/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE) +/obj/item/organ/regenerative_core/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE, pref_load = FALSE) . = ..() if(!preserved && !inert) preserved(TRUE) owner.visible_message("[src] stabilizes as it's inserted.") -/obj/item/organ/regenerative_core/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/regenerative_core/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) if(!inert && !special) owner.visible_message("[src] rapidly decays as it's removed.") go_inert() diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index 4a62877b93bd1..e9be3a75242ab 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -28,7 +28,7 @@ investigate_flags = ADMIN_INVESTIGATE_TARGET -/obj/item/organ/brain/Insert(mob/living/carbon/C, special = 0,no_id_transfer = FALSE) +/obj/item/organ/brain/Insert(mob/living/carbon/C, special = 0,no_id_transfer = FALSE, pref_load = FALSE) ..() name = "brain" @@ -59,7 +59,7 @@ //Update the body's icon so it doesnt appear debrained anymore C.update_hair() -/obj/item/organ/brain/Remove(mob/living/carbon/C, special = 0, no_id_transfer = FALSE) +/obj/item/organ/brain/Remove(mob/living/carbon/C, special = 0, no_id_transfer = FALSE, pref_load = FALSE) ..() for(var/X in traumas) var/datum/brain_trauma/BT = X diff --git a/code/modules/mob/living/carbon/alien/organs.dm b/code/modules/mob/living/carbon/alien/organs.dm index 3e1541526109c..12d13bd80e821 100644 --- a/code/modules/mob/living/carbon/alien/organs.dm +++ b/code/modules/mob/living/carbon/alien/organs.dm @@ -14,12 +14,12 @@ QDEL_LIST(alien_powers) return ..() -/obj/item/organ/alien/Insert(mob/living/carbon/M, special = 0) +/obj/item/organ/alien/Insert(mob/living/carbon/M, special = 0, pref_load = FALSE) . = ..() for(var/obj/effect/proc_holder/alien/P in alien_powers) M.AddAbility(P) -/obj/item/organ/alien/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/alien/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) for(var/obj/effect/proc_holder/alien/P in alien_powers) M.RemoveAbility(P) return ..() @@ -82,14 +82,14 @@ else owner.adjustPlasma(plasma_rate * 0.1) -/obj/item/organ/alien/plasmavessel/Insert(mob/living/carbon/M, special = 0) +/obj/item/organ/alien/plasmavessel/Insert(mob/living/carbon/M, special = 0, pref_load = FALSE) . = ..() if(!isalien(M)) return var/mob/living/carbon/alien/A = M A.updatePlasmaDisplay() -/obj/item/organ/alien/plasmavessel/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/alien/plasmavessel/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) . = ..() if(!isalien(M)) return @@ -107,12 +107,12 @@ alien_powers = list(/obj/effect/proc_holder/alien/whisper) var/recent_queen_death = 0 //Indicates if the queen died recently, aliens are heavily weakened while this is active. -/obj/item/organ/alien/hivenode/Insert(mob/living/carbon/M, special = 0) +/obj/item/organ/alien/hivenode/Insert(mob/living/carbon/M, special = 0, pref_load = FALSE) M.faction |= FACTION_ALIEN ADD_TRAIT(M, TRAIT_XENO_IMMUNE, "xeno immune") return ..() -/obj/item/organ/alien/hivenode/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/alien/hivenode/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) M.faction -= FACTION_ALIEN REMOVE_TRAIT(M, TRAIT_XENO_IMMUNE, "xeno immune") return ..() diff --git a/code/modules/mob/living/carbon/human/species_types/felinid.dm b/code/modules/mob/living/carbon/human/species_types/felinid.dm index 8d16289fe08b5..5b0dcf55c7453 100644 --- a/code/modules/mob/living/carbon/human/species_types/felinid.dm +++ b/code/modules/mob/living/carbon/human/species_types/felinid.dm @@ -40,12 +40,12 @@ H.dna.features["ears"] = "Cat" if(H.dna.features["ears"] == "Cat") var/obj/item/organ/ears/cat/ears = new - ears.Insert(H, drop_if_replaced = FALSE) + ears.Insert(H, drop_if_replaced = FALSE, pref_load = pref_load) else mutantears = /obj/item/organ/ears if(H.dna.features["tail_human"] == "Cat") var/obj/item/organ/tail/cat/tail = new - tail.Insert(H, drop_if_replaced = FALSE) + tail.Insert(H, drop_if_replaced = FALSE, pref_load = pref_load) else mutanttail = null return ..() @@ -64,7 +64,7 @@ if(!new_ears) // Go with default ears new_ears = new /obj/item/organ/ears - new_ears.Insert(H, drop_if_replaced = FALSE) + new_ears.Insert(H, drop_if_replaced = FALSE, pref_load = pref_load) if(tail) var/obj/item/organ/tail/new_tail @@ -74,9 +74,9 @@ if(new_species.mutanttail) new_tail = new new_species.mutanttail if(new_tail) - new_tail.Insert(H, drop_if_replaced = FALSE) + new_tail.Insert(H, drop_if_replaced = FALSE, pref_load = pref_load) else - tail.Remove(H) + tail.Remove(H, pref_load = pref_load) /datum/species/human/felinid/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/M) if(istype(chem, /datum/reagent/consumable/cocoa)) diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm index e55a2021e9afa..e41b2325c4894 100644 --- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm @@ -128,7 +128,7 @@ icon_state = "brain-x-d" var/obj/effect/proc_holder/spell/targeted/shadowwalk/shadowwalk -/obj/item/organ/brain/nightmare/Insert(mob/living/carbon/M, special = 0) +/obj/item/organ/brain/nightmare/Insert(mob/living/carbon/M, special = 0, pref_load = FALSE) ..() if(M.dna.species.id != "nightmare") M.set_species(/datum/species/shadow/nightmare) @@ -138,7 +138,7 @@ shadowwalk = SW -/obj/item/organ/brain/nightmare/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/brain/nightmare/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) if(shadowwalk) M.RemoveSpell(shadowwalk) ..() @@ -168,13 +168,13 @@ user.temporarilyRemoveItemFromInventory(src, TRUE) Insert(user) -/obj/item/organ/heart/nightmare/Insert(mob/living/carbon/M, special = 0) +/obj/item/organ/heart/nightmare/Insert(mob/living/carbon/M, special = 0, pref_load = FALSE) ..() if(special != HEART_SPECIAL_SHADOWIFY) blade = new/obj/item/light_eater M.put_in_hands(blade) -/obj/item/organ/heart/nightmare/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/heart/nightmare/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) respawn_progress = 0 if(blade && special != HEART_SPECIAL_SHADOWIFY) M.visible_message("\The [blade] disintegrates!") diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm index 95dea65a9a7f5..65d210482c280 100644 --- a/code/modules/mob/living/carbon/monkey/monkey.dm +++ b/code/modules/mob/living/carbon/monkey/monkey.dm @@ -247,7 +247,7 @@ GLOBAL_LIST_INIT(strippable_monkey_items, create_strippable_list(list( /obj/item/organ/brain/tumor name = "teratoma brain" -/obj/item/organ/brain/tumor/Remove(mob/living/carbon/C, special, no_id_transfer) +/obj/item/organ/brain/tumor/Remove(mob/living/carbon/C, special, no_id_transfer, pref_load = FALSE) . = ..() //Removing it deletes it if(!QDELETED(src)) diff --git a/code/modules/surgery/organs/appendix.dm b/code/modules/surgery/organs/appendix.dm index 89937e7b5fc49..0645e4bc6c101 100644 --- a/code/modules/surgery/organs/appendix.dm +++ b/code/modules/surgery/organs/appendix.dm @@ -28,14 +28,14 @@ if(M) M.adjustToxLoss(4, TRUE, TRUE) //forced to ensure people don't use it to gain tox as slime person -/obj/item/organ/appendix/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/appendix/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) for(var/datum/disease/appendicitis/A in M.diseases) A.cure() inflamed = TRUE update_icon() ..() -/obj/item/organ/appendix/Insert(mob/living/carbon/M, special = 0) +/obj/item/organ/appendix/Insert(mob/living/carbon/M, special = 0, pref_load = FALSE) ..() if(inflamed) M.ForceContractDisease(new /datum/disease/appendicitis(), FALSE, TRUE) diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm index 99796f0b99972..a2f133b383260 100644 --- a/code/modules/surgery/organs/augments_arms.dm +++ b/code/modules/surgery/organs/augments_arms.dm @@ -112,14 +112,14 @@ to_chat(user, "You modify [src] to be installed on the [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm.") update_icon() -/obj/item/organ/cyberimp/arm/Insert(mob/living/carbon/user, special = FALSE, drop_if_replaced = TRUE) +/obj/item/organ/cyberimp/arm/Insert(mob/living/carbon/user, special = FALSE, drop_if_replaced = TRUE, pref_load = FALSE) . = ..() var/side = zone == BODY_ZONE_R_ARM ? 2 : 1 register_hand(user, owner.hand_bodyparts[side]) RegisterSignal(user, COMSIG_KB_MOB_DROPITEM_DOWN, PROC_REF(dropkey)) //We're nodrop, but we'll watch for the drop hotkey anyway and then stow if possible. RegisterSignal(user, COMSIG_CARBON_POST_ATTACH_LIMB, PROC_REF(limb_attached)) -/obj/item/organ/cyberimp/arm/Remove(mob/living/carbon/user, special = 0) +/obj/item/organ/cyberimp/arm/Remove(mob/living/carbon/user, special = 0, pref_load = FALSE) Retract() unregister_hand(user) UnregisterSignal(user, list(COMSIG_KB_MOB_DROPITEM_DOWN, COMSIG_CARBON_POST_ATTACH_LIMB)) diff --git a/code/modules/surgery/organs/augments_chest.dm b/code/modules/surgery/organs/augments_chest.dm index a39f9ba0d074e..ccdf67ba41fc2 100644 --- a/code/modules/surgery/organs/augments_chest.dm +++ b/code/modules/surgery/organs/augments_chest.dm @@ -130,13 +130,13 @@ var/on = FALSE var/datum/effect_system/trail_follow/ion/ion_trail -/obj/item/organ/cyberimp/chest/thrusters/Insert(mob/living/carbon/M, special = 0) +/obj/item/organ/cyberimp/chest/thrusters/Insert(mob/living/carbon/M, special = 0, pref_load = FALSE) . = ..() if(!ion_trail) ion_trail = new ion_trail.set_up(M) -/obj/item/organ/cyberimp/chest/thrusters/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/cyberimp/chest/thrusters/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) if(on) toggle(silent = TRUE) ..() diff --git a/code/modules/surgery/organs/augments_eyes.dm b/code/modules/surgery/organs/augments_eyes.dm index 6397ce9a5211c..720a84d551c05 100644 --- a/code/modules/surgery/organs/augments_eyes.dm +++ b/code/modules/surgery/organs/augments_eyes.dm @@ -15,7 +15,7 @@ var/HUD_type var/HUD_trait -/obj/item/organ/cyberimp/eyes/hud/Insert(var/mob/living/carbon/M, var/special = 0, drop_if_replaced = FALSE) +/obj/item/organ/cyberimp/eyes/hud/Insert(var/mob/living/carbon/M, var/special = 0, drop_if_replaced = FALSE, pref_load = FALSE) ..() if(HUD_type) var/datum/atom_hud/H = GLOB.huds[HUD_type] @@ -23,7 +23,7 @@ if(HUD_trait) ADD_TRAIT(M, HUD_trait, ORGAN_TRAIT) -/obj/item/organ/cyberimp/eyes/hud/Remove(var/mob/living/carbon/M, var/special = 0) +/obj/item/organ/cyberimp/eyes/hud/Remove(var/mob/living/carbon/M, var/special = 0, pref_load = FALSE) if(HUD_type) var/datum/atom_hud/H = GLOB.huds[HUD_type] H.remove_hud_from(M) diff --git a/code/modules/surgery/organs/augments_internal.dm b/code/modules/surgery/organs/augments_internal.dm index 747a6ea285c5c..a914d0277e885 100644 --- a/code/modules/surgery/organs/augments_internal.dm +++ b/code/modules/surgery/organs/augments_internal.dm @@ -89,7 +89,7 @@ stored_items = list() -/obj/item/organ/cyberimp/brain/anti_drop/Remove(var/mob/living/carbon/M, special = 0) +/obj/item/organ/cyberimp/brain/anti_drop/Remove(var/mob/living/carbon/M, special = 0, pref_load = FALSE) if(active) ui_action_click() ..() @@ -109,7 +109,7 @@ var/stun_cap_amount = 40 -/obj/item/organ/cyberimp/brain/anti_stun/Remove(mob/living/carbon/M, special = FALSE) +/obj/item/organ/cyberimp/brain/anti_stun/Remove(mob/living/carbon/M, special = FALSE, pref_load = FALSE) . = ..() UnregisterSignal(M, signalCache) @@ -226,13 +226,13 @@ /datum/surgery/organ_extraction ) -/obj/item/organ/cyberimp/brain/linkedsurgery/perfect/Insert(mob/living/carbon/user, special, drop_if_replaced) +/obj/item/organ/cyberimp/brain/linkedsurgery/perfect/Insert(mob/living/carbon/user, special, drop_if_replaced, pref_load = FALSE) . = ..() to_chat(user, "Detailed, forbidden medical knowledge begins to fill your brain... You feel as if you're a perfect surgeon now!") ADD_TRAIT(user, TRAIT_PERFECT_SURGEON, ORGAN_TRAIT) update_surgery() -/obj/item/organ/cyberimp/brain/linkedsurgery/perfect/Remove(mob/living/carbon/user, special) +/obj/item/organ/cyberimp/brain/linkedsurgery/perfect/Remove(mob/living/carbon/user, special, pref_load = FALSE) . = ..() if(!QDELETED(user)) to_chat(user, "You feel your perfect surgical knowledge leaving your mind!") diff --git a/code/modules/surgery/organs/ears.dm b/code/modules/surgery/organs/ears.dm index da07a1205ca7f..ced7fd9d08f14 100644 --- a/code/modules/surgery/organs/ears.dm +++ b/code/modules/surgery/organs/ears.dm @@ -97,16 +97,22 @@ icon_state = "kitty" bang_protect = -2 -/obj/item/organ/ears/cat/Insert(mob/living/carbon/human/H, special = 0, drop_if_replaced = TRUE) +/obj/item/organ/ears/cat/Insert(mob/living/carbon/human/H, special = 0, drop_if_replaced = TRUE, pref_load = FALSE) ..() + if(pref_load) + H.update_body() + return if(istype(H)) color = H.hair_color H.dna.species.mutant_bodyparts |= "ears" H.dna.features["ears"] = "Cat" H.update_body() -/obj/item/organ/ears/cat/Remove(mob/living/carbon/human/H, special = 0) +/obj/item/organ/ears/cat/Remove(mob/living/carbon/human/H, special = 0, pref_load = FALSE) ..() + if(pref_load && istype(H)) + H.update_body() + return if(istype(H)) color = H.hair_color H.dna.features["ears"] = "None" @@ -118,13 +124,13 @@ desc = "The source of a penguin's happy feet." var/datum/component/waddle -/obj/item/organ/ears/penguin/Insert(mob/living/carbon/human/H, special = 0, drop_if_replaced = TRUE) +/obj/item/organ/ears/penguin/Insert(mob/living/carbon/human/H, special = 0, drop_if_replaced = TRUE, pref_load = FALSE) . = ..() if(istype(H)) to_chat(H, "You suddenly feel like you've lost your balance.") waddle = H.AddComponent(/datum/component/waddling) -/obj/item/organ/ears/penguin/Remove(mob/living/carbon/human/H, special = 0) +/obj/item/organ/ears/penguin/Remove(mob/living/carbon/human/H, special = 0, pref_load = FALSE) . = ..() if(istype(H)) to_chat(H, "Your sense of balance comes back to you.") diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm index 908eda8acd7b3..e78103e21c40b 100644 --- a/code/modules/surgery/organs/eyes.dm +++ b/code/modules/surgery/organs/eyes.dm @@ -31,7 +31,7 @@ var/no_glasses var/damaged = FALSE //damaged indicates that our eyes are undergoing some level of negative effect -/obj/item/organ/eyes/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = FALSE, initialising) +/obj/item/organ/eyes/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = FALSE, initialising, pref_load = FALSE) . = ..() if(ishuman(owner)) var/mob/living/carbon/human/HMN = owner @@ -48,7 +48,7 @@ if(M.has_dna() && ishuman(M)) M.dna.species.handle_body(M) //updates eye icon -/obj/item/organ/eyes/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/eyes/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) ..() if(ishuman(M) && eye_color) var/mob/living/carbon/human/HMN = M @@ -174,7 +174,7 @@ /obj/item/organ/eyes/robotic/flashlight/emp_act(severity) return -/obj/item/organ/eyes/robotic/flashlight/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = FALSE) +/obj/item/organ/eyes/robotic/flashlight/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = FALSE, pref_load = FALSE) ..() if(!eye) eye = new /obj/item/flashlight/eyelight() @@ -184,7 +184,7 @@ M.become_blind("flashlight_eyes") -/obj/item/organ/eyes/robotic/flashlight/Remove(var/mob/living/carbon/M, var/special = 0) +/obj/item/organ/eyes/robotic/flashlight/Remove(var/mob/living/carbon/M, var/special = 0, pref_load = FALSE) eye.on = FALSE eye.update_brightness(M) eye.forceMove(src) @@ -226,7 +226,7 @@ terminate_effects() . = ..() -/obj/item/organ/eyes/robotic/glow/Remove(mob/living/carbon/M, special = FALSE) +/obj/item/organ/eyes/robotic/glow/Remove(mob/living/carbon/M, special = FALSE, pref_load = FALSE) terminate_effects() . = ..() diff --git a/code/modules/surgery/organs/heart.dm b/code/modules/surgery/organs/heart.dm index 4888219fce4c6..97afce1b052a4 100644 --- a/code/modules/surgery/organs/heart.dm +++ b/code/modules/surgery/organs/heart.dm @@ -27,7 +27,7 @@ else icon_state = "[icon_base]-off" -/obj/item/organ/heart/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/heart/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) ..() if(!special) addtimer(CALLBACK(src, PROC_REF(stop_if_unowned)), 120) @@ -129,12 +129,12 @@ else last_pump = world.time //lets be extra fair *sigh* -/obj/item/organ/heart/cursed/Insert(mob/living/carbon/M, special = 0) +/obj/item/organ/heart/cursed/Insert(mob/living/carbon/M, special = 0, pref_load = FALSE) ..() if(owner) to_chat(owner, "Your heart has been replaced with a cursed one, you have to pump this one manually otherwise you'll die!") -/obj/item/organ/heart/cursed/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/heart/cursed/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) ..() M.remove_client_colour(/datum/client_colour/cursed_heart_blood) diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index 17fbc526fb992..15c675893dbf9 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -42,13 +42,13 @@ INITIALIZE_IMMEDIATE(/obj/item/organ) AddComponent(/datum/component/edible, initial_reagents = food_reagents, foodtypes = RAW | MEAT | GORE, \ pre_eat = CALLBACK(src, PROC_REF(pre_eat)), on_compost = CALLBACK(src, PROC_REF(pre_compost)) , after_eat = CALLBACK(src, PROC_REF(on_eat_from))) -/obj/item/organ/proc/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE) +/obj/item/organ/proc/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE, pref_load = FALSE) if(!iscarbon(M) || owner == M) return var/obj/item/organ/replaced = M.getorganslot(slot) if(replaced) - replaced.Remove(M, special = 1) + replaced.Remove(M, special = 1, pref_load = pref_load) if(drop_if_replaced) replaced.forceMove(get_turf(M)) else @@ -66,7 +66,7 @@ INITIALIZE_IMMEDIATE(/obj/item/organ) STOP_PROCESSING(SSobj, src) //Special is for instant replacement like autosurgeons -/obj/item/organ/proc/Remove(mob/living/carbon/M, special = FALSE) +/obj/item/organ/proc/Remove(mob/living/carbon/M, special = FALSE, pref_load = FALSE) owner = null if(M) M.internal_organs -= src diff --git a/code/modules/surgery/organs/stomach.dm b/code/modules/surgery/organs/stomach.dm index fc7218cf825ed..e4b19ac68016a 100755 --- a/code/modules/surgery/organs/stomach.dm +++ b/code/modules/surgery/organs/stomach.dm @@ -77,7 +77,7 @@ H.throw_alert("disgust", /atom/movable/screen/alert/disgusted) SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "disgust", /datum/mood_event/disgusted) -/obj/item/organ/stomach/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/stomach/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) var/mob/living/carbon/human/H = owner if(istype(H)) H.clear_alert("disgust") @@ -101,12 +101,12 @@ var/max_charge = 5000 //same as upgraded+ cell var/charge = 5000 -/obj/item/organ/stomach/battery/Insert(mob/living/carbon/M, special = 0) +/obj/item/organ/stomach/battery/Insert(mob/living/carbon/M, special = 0, pref_load = FALSE) . = ..() RegisterSignal(owner, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, PROC_REF(charge)) update_nutrition() -/obj/item/organ/stomach/battery/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/stomach/battery/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) UnregisterSignal(owner, COMSIG_PROCESS_BORGCHARGER_OCCUPANT) if(!HAS_TRAIT(owner, TRAIT_NOHUNGER) && HAS_TRAIT(owner, TRAIT_POWERHUNGRY)) owner.nutrition = 0 @@ -173,11 +173,11 @@ max_charge = 2500 //same as upgraded cell charge = 2500 -/obj/item/organ/stomach/battery/ethereal/Insert(mob/living/carbon/M, special = 0) +/obj/item/organ/stomach/battery/ethereal/Insert(mob/living/carbon/M, special = 0, pref_load = FALSE) RegisterSignal(owner, COMSIG_LIVING_ELECTROCUTE_ACT, PROC_REF(on_electrocute)) return ..() -/obj/item/organ/stomach/battery/ethereal/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/stomach/battery/ethereal/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) UnregisterSignal(owner, COMSIG_LIVING_ELECTROCUTE_ACT) return ..() diff --git a/code/modules/surgery/organs/tails.dm b/code/modules/surgery/organs/tails.dm index d9440688d80ac..63c51a2b67131 100644 --- a/code/modules/surgery/organs/tails.dm +++ b/code/modules/surgery/organs/tails.dm @@ -22,16 +22,23 @@ desc = "A severed cat tail. Who's wagging now?" tail_type = "Cat" -/obj/item/organ/tail/cat/Insert(mob/living/carbon/human/H, special = 0, drop_if_replaced = TRUE) +/obj/item/organ/tail/cat/Insert(mob/living/carbon/human/H, special = 0, drop_if_replaced = TRUE, pref_load = FALSE) ..() + if(pref_load && istype(H)) + H.update_body() + return if(istype(H)) if(!("tail_human" in H.dna.species.mutant_bodyparts)) H.dna.species.mutant_bodyparts |= "tail_human" H.dna.features["tail_human"] = tail_type H.update_body() -/obj/item/organ/tail/cat/Remove(mob/living/carbon/human/H, special = 0) +/obj/item/organ/tail/cat/Remove(mob/living/carbon/human/H, special = 0, pref_load = FALSE) ..() + if(pref_load && istype(H)) + color = H.hair_color + H.update_body() + return if(istype(H)) H.dna.features["tail_human"] = "None" H.dna.species.mutant_bodyparts -= "tail_human" @@ -77,7 +84,7 @@ H.dna.species.mutant_bodyparts |= "spines" H.update_body() -/obj/item/organ/tail/lizard/Remove(mob/living/carbon/human/H, special = 0) +/obj/item/organ/tail/lizard/Remove(mob/living/carbon/human/H, special = 0, pref_load = FALSE) ..() if(istype(H)) H.dna.species.mutant_bodyparts -= "tail_lizard" diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm index 06702d881c842..23117abd0d67a 100644 --- a/code/modules/surgery/organs/tongue.dm +++ b/code/modules/surgery/organs/tongue.dm @@ -48,7 +48,7 @@ M.UnregisterSignal(M, COMSIG_MOB_SAY) return ..() -/obj/item/organ/tongue/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/tongue/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) UnregisterSignal(M, COMSIG_MOB_SAY, PROC_REF(handle_speech)) M.RegisterSignal(M, COMSIG_MOB_SAY, TYPE_PROC_REF(/mob/living/carbon, handle_tongueless_speech)) return ..() diff --git a/code/modules/surgery/organs/wings.dm b/code/modules/surgery/organs/wings.dm index d62e84c031473..b5cc85dc7e22c 100644 --- a/code/modules/surgery/organs/wings.dm +++ b/code/modules/surgery/organs/wings.dm @@ -37,7 +37,7 @@ if(H.movement_type & FLYING) H.dna.species.toggle_flight(H) -/obj/item/organ/wings/Remove(mob/living/carbon/human/H, special = 0) +/obj/item/organ/wings/Remove(mob/living/carbon/human/H, special = 0, pref_load = FALSE) ..() if(istype(H)) H.dna.species.mutant_bodyparts -= basewings @@ -123,7 +123,7 @@ wing_type = "Plain" canopen = TRUE -/obj/item/organ/wings/moth/Remove(mob/living/carbon/human/H, special) +/obj/item/organ/wings/moth/Remove(mob/living/carbon/human/H, special, pref_load = FALSE) flight_level = initial(flight_level) return ..() @@ -172,7 +172,7 @@ wing_type = "Bee" var/jumpdist = 3 -/obj/item/organ/wings/bee/Remove(mob/living/carbon/human/H, special) +/obj/item/organ/wings/bee/Remove(mob/living/carbon/human/H, special, pref_load = FALSE) jumpdist = initial(jumpdist) return ..() diff --git a/code/modules/zombie/organs.dm b/code/modules/zombie/organs.dm index b077b9854ff6a..b307752791795 100644 --- a/code/modules/zombie/organs.dm +++ b/code/modules/zombie/organs.dm @@ -23,11 +23,11 @@ GLOB.zombie_infection_list -= src . = ..() -/obj/item/organ/zombie_infection/Insert(var/mob/living/carbon/M, special = 0) +/obj/item/organ/zombie_infection/Insert(var/mob/living/carbon/M, special = 0, pref_load = FALSE) . = ..() START_PROCESSING(SSobj, src) -/obj/item/organ/zombie_infection/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/zombie_infection/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE) . = ..() STOP_PROCESSING(SSobj, src) if(iszombie(M) && old_species && !QDELETED(M) && !special) From 9179d4181511ed7283fe59863162c856c66e658d Mon Sep 17 00:00:00 2001 From: itsmeow Date: Fri, 23 Jun 2023 02:08:14 -0400 Subject: [PATCH 206/269] Fix tsc again --- tgui/packages/tgui/components/Dropdown.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tgui/packages/tgui/components/Dropdown.tsx b/tgui/packages/tgui/components/Dropdown.tsx index 01821d98dce55..53f2bdc53cae2 100644 --- a/tgui/packages/tgui/components/Dropdown.tsx +++ b/tgui/packages/tgui/components/Dropdown.tsx @@ -345,7 +345,11 @@ export class Dropdown extends Component { - @@ -53,14 +75,73 @@ const AntagSelection = ( {props.antagonists.map((antagonist) => { const isBanned = antagonist.ban_key && data.antag_bans && data.antag_bans.indexOf(antagonist.ban_key) !== -1; + const hoursLeft = + (antagonist.path && + data.antag_living_playtime_hours_left && + data.antag_living_playtime_hours_left[antagonist.path]) || + 0; + + let full_description = `${ + isBanned ? `You are banned from ${antagonist.name}.${antagonist.description || hoursLeft > 0 ? '\n' : ''}` : '' + }${ + hoursLeft > 0 + ? `You require ${hoursLeft} more hour${ + hoursLeft !== 1 ? 's' : '' + } of living playtime in order to play this role.${antagonist.description ? '\n' : ''}` + : '' + }${antagonist.description}`; + if (!full_description.length) { + full_description = 'No description found.'; + } return ( 0 + ? 'banned' + : isSelectedGlobal(antagonist.path) + ? antagonist.per_character && !isSelectedCharacter(antagonist.path) + ? 'banned' + : 'on' + : 'off' + }`, ])} key={antagonist.path}> + {antagonist.per_character && !(isBanned || hoursLeft > 0) ? ( + + { + if (isBanned) { + return; + } + + if (isSelectedCharacter(antagonist.path)) { + disableAntagsCharacter([antagonist.path]); + } else { + enableAntagsCharacter([antagonist.path]); + } + }}> + + Per-Character Toggle + + This can only be disabled per-character. You cannot force a globally disabled antagonist + 'on' for a specific character. +
    + }> + C + + + + ) : null} ( -
    - {text} - {index !== values.length - 1 && } -
    - )) - } + content={(full_description || 'No description found.').split('\n').map((text, index, values) => ( +
    + {text} + {index !== values.length - 1 && } +
    + ))} position="bottom"> { if (isBanned) { return; } - if (isSelected(antagonist.path)) { - disableAntags([antagonist.path]); + if (isSelectedGlobal(antagonist.path)) { + disableAntagsGlobal([antagonist.path]); } else { - enableAntags([antagonist.path]); + enableAntagsGlobal([antagonist.path]); } }}> {isBanned && } + + {hoursLeft > 0 && ( + + {hoursLeft} +
    + hours left +
    + )}
    @@ -127,7 +212,11 @@ export const AntagsPage = (_, context) => { const { antagonists = [], categories = [] } = serverData.antags; return ( - + antag.path)} + /> {searchText !== '' ? ( ) : ( @@ -146,11 +235,41 @@ export const AntagsPage = (_, context) => { ); }; -const SearchBar = ({ searchText, setSearchText }) => { +const SearchBar = ({ searchText, setSearchText, allAntags }, context) => { + const { act } = useBackend(context); + const enableAntagsGlobal = (antags: string[]) => { + act('set_antags', { + antags, + toggled: true, + character: false, + }); + }; + + const disableAntagsGlobal = (antags: string[]) => { + act('set_antags', { + antags, + toggled: false, + character: false, + }); + }; return (
    - - setSearchText(value)} /> + + + + setSearchText(value)} /> + + + + + + + +
    ); }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts index 6b9d8cf9afe99..b87cfd8c1e22a 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts @@ -114,6 +114,7 @@ export type AntagonistData = { name: string; description: string; category: string; + per_character: BooleanLike; path: string; icon_path: string; ban_key?: string; @@ -198,7 +199,9 @@ export type PreferencesMenuData = { is_donator: BooleanLike; antag_bans?: string[]; - enabled_antags: string[]; + antag_living_playtime_hours_left?: Record; + enabled_global: string[]; + enabled_character: string[]; active_slot: number; max_slot: number; diff --git a/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss b/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss index 5e0b060f78e95..87d16a43e6357 100644 --- a/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss +++ b/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss @@ -53,6 +53,62 @@ $department_map: ( padding-bottom: $antagonist_bottom_padding; padding-right: 20px; + &__per_character { + &--off { + .antagonist-icon-parent-per-character { + .antagonist-icon { + border-color: darken(colors.$red, 10%); + &:hover { + transition: border-color 0.1s ease-out; + border-color: darken(colors.$red, 5%); + } + } + } + } + &--on { + .antagonist-icon-parent-per-character { + .antagonist-icon { + border-color: darken(colors.$grey, 10%); + &:hover { + transition: border-color 0.1s ease-out; + border-color: darken(colors.$grey, 5%); + } + } + } + } + + .antagonist-icon-parent-per-character { + z-index: 1; + opacity: 0.9; + overflow: visible; + position: relative; + height: 0; + width: 0; + padding: 0; + margin: 0; + left: 76px; + top: 82px; + + .antagonist-icon { + border-style: solid; + border-radius: 50%; + border-width: 4px; + background-color: #222; + + box-sizing: content-box; + + height: 32px; + width: 32px; + text-align: center; + font-size: 20px; + vertical-align: middle; + line-height: 32px; + -ms-user-select: none; + user-select: none; + } + } + } + .antagonist-icon-parent { border-style: solid; border-radius: 50%; @@ -99,10 +155,9 @@ $department_map: ( &--banned { .antagonist-icon-parent { border-color: colors.$grey; - } - - .antagonist-icon { - opacity: 0.5; + .antagonist-icon { + opacity: 0.5; + } } } @@ -120,9 +175,14 @@ $department_map: ( opacity: 0.8; } - .antagonist-days-left { + .antagonist-time-left { text-align: center; text-shadow: 1px 1px 1px #222; + font-size: 1.2rem; + .antagonist-time-left-hours { + font-size: 1.5rem; + font-weight: bold; + } width: 100%; From 1a196e0938848e01efb08ee60ad797abf1575ce9 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Tue, 25 Jul 2023 21:44:37 -0400 Subject: [PATCH 232/269] More tweaks to antag prefs --- .../interfaces/PreferencesMenu/AntagsPage.tsx | 179 +++++++++++------- .../styles/interfaces/PreferencesMenu.scss | 35 +++- 2 files changed, 142 insertions(+), 72 deletions(-) diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/AntagsPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/AntagsPage.tsx index dc6e354f8b518..96a36ceeafe92 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/AntagsPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/AntagsPage.tsx @@ -63,13 +63,36 @@ const AntagSelection = ( title={props.name} buttons={ <> - + +
    - }> - C - - - - ) : null} + {antagonist.per_character ? ( + + { + if (isSelectedCharacter(antagonist.path)) { + disableAntagsCharacter([antagonist.path]); + } else { + enableAntagsCharacter([antagonist.path]); + } + }}> + + Per-Character Toggle + + This can only be disabled per-character. You cannot force a globally disabled antagonist + 'on' for a specific character. + + }> + C + + + + ) : null} (
    @@ -165,10 +182,6 @@ const AntagSelection = ( { - if (isBanned) { - return; - } - if (isSelectedGlobal(antagonist.path)) { disableAntagsGlobal([antagonist.path]); } else { @@ -177,11 +190,18 @@ const AntagSelection = ( }}> - {isBanned && } + {isBanned && ( + <> + + Banned + + + + )} {hoursLeft > 0 && ( - - {hoursLeft} + + {hoursLeft}
    hours left
    @@ -237,19 +257,19 @@ export const AntagsPage = (_, context) => { const SearchBar = ({ searchText, setSearchText, allAntags }, context) => { const { act } = useBackend(context); - const enableAntagsGlobal = (antags: string[]) => { + const enableAntags = (character: boolean) => { act('set_antags', { - antags, + antags: allAntags, toggled: true, - character: false, + character, }); }; - const disableAntagsGlobal = (antags: string[]) => { + const disableAntags = (character: boolean) => { act('set_antags', { - antags, + antags: allAntags, toggled: false, - character: false, + character, }); }; return ( @@ -257,16 +277,43 @@ const SearchBar = ({ searchText, setSearchText, allAntags }, context) => { - setSearchText(value)} /> + setSearchText(value)} /> - - - + + + + diff --git a/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss b/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss index 87d16a43e6357..c488484da82f4 100644 --- a/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss +++ b/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss @@ -86,8 +86,8 @@ $department_map: ( width: 0; padding: 0; margin: 0; - left: 76px; - top: 82px; + left: 74px; + bottom: -64px; .antagonist-icon { border-style: solid; @@ -142,6 +142,16 @@ $department_map: ( } } } + + &--banned { + .antagonist-icon-parent { + border-color: colors.$grey; + color: color.adjust(colors.$red, $lightness: 20%); + .antagonist-icon { + opacity: 0.5; + } + } + } } &--on { @@ -150,11 +160,22 @@ $department_map: ( .antagonist-icon-parent { border-color: colors.$green; } + + &--banned { + .antagonist-icon-parent { + border-color: colors.$grey; + color: color.adjust(colors.$green, $lightness: 40%); + .antagonist-icon { + opacity: 0.5; + } + } + } } - &--banned { + &--grey { .antagonist-icon-parent { border-color: colors.$grey; + color: inherit; .antagonist-icon { opacity: 0.5; } @@ -175,11 +196,13 @@ $department_map: ( opacity: 0.8; } - .antagonist-time-left { + .antagonist-overlay-text { text-align: center; - text-shadow: 1px 1px 1px #222; + text-shadow: 1px 1px 3px 2px #222; font-size: 1.2rem; - .antagonist-time-left-hours { + z-index: 1; + + .antagonist-overlay-text-hours { font-size: 1.5rem; font-weight: bold; } From d2010ccf11a4809a612a59ef6cbe41d95d69201e Mon Sep 17 00:00:00 2001 From: itsmeow Date: Tue, 25 Jul 2023 21:54:49 -0400 Subject: [PATCH 233/269] Prettier jobs menu --- .../interfaces/PreferencesMenu/JobsPage.tsx | 1 + .../styles/interfaces/PreferencesMenu.scss | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx index e30d066a44e1f..0ee2532a74498 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx @@ -78,6 +78,7 @@ const PriorityButtons = (props: { createSetPriority: CreateSetPriority; isOverfl 'align-items': 'center', 'justify-content': 'flex-end', 'height': '100%', + 'border': '1px solid rgba(0, 0, 0, 0.4)', }}> {isOverflow ? ( <> diff --git a/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss b/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss index c488484da82f4..553aefcea68b6 100644 --- a/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss +++ b/tgui/packages/tgui/styles/interfaces/PreferencesMenu.scss @@ -270,36 +270,37 @@ $department_map: ( &__priority { color: black; - border-left: 1px solid rgba(0, 0, 0, 0.3); + border-left: 1px solid #222; border-right: none; border-top: none; border-bottom: none; border-radius: 0 !important; + text-shadow: 0 0 1px black; &--off { background-color: lighten(colors.$grey, 10%) !important; border-color: lighten(colors.$grey, 20%); border-left: none; } &--low { - background-color: red !important; - border-color: lighten(red, 20%); + background-color: colors.$red !important; color: black !important; + text-shadow: none; } &--medium { - background-color: yellow !important; - border-color: lighten(yellow, 20%); + background-color: colors.$yellow !important; color: black !important; + text-shadow: none; } &--high { - background-color: lime !important; - border-color: lighten(lime, 20%); - color: black !important ; + background-color: lighten(colors.$green, 10%) !important; + color: black !important; + text-shadow: none; } &--disabled { background-color: #444 !important; - border-color: #222; color: white !important; - transition: linear 0.25s background-color; + transition: ease-out 0.25s background-color; + text-shadow: 0 0 1px black; &:hover { background-color: #666 !important; } From be130c560ba61ffc0c999d9d0adfa7354b9cd44c Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 26 Jul 2023 11:56:11 -0400 Subject: [PATCH 234/269] Forgot to commit this --- code/__DEFINES/role_preferences.dm | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index 69de0a2d6018e..c3f2304bd7f51 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -180,11 +180,13 @@ GLOBAL_LIST_INIT(other_bannable_roles, list( CRASH("Invalid role_preference_key [role_preference_key] passed to role_preference_enabled!") if(!src.prefs) return FALSE - var/list/source = src.prefs.role_preferences_global var/datum/role_preference/pref = role_preference_key + // If this is per character, check if it's disabled. Otherwise continue and check the global value if(initial(pref.per_character)) - source = src.prefs.role_preferences - var/role_preference_value = source["[role_preference_key]"] + var/role_preference_value = src.prefs.role_preferences["[role_preference_key]"] + if(isnum(role_preference_value) && !role_preference_value) // explicitly disabled and not null + return FALSE + var/role_preference_value = src.prefs.role_preferences_global["[role_preference_key]"] if(isnum(role_preference_value) && !role_preference_value) // explicitly disabled and not null return FALSE return TRUE From 36b11a96ed48e601468f0e41f093e9b21b7a7d32 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 26 Jul 2023 15:26:43 -0400 Subject: [PATCH 235/269] Changelog cleanup --- SQL/database_changelog.txt | 517 +--------------------------- SQL/prefs_migration_2023-07-26.sql | 519 +++++++++++++++++++++++++++++ 2 files changed, 520 insertions(+), 516 deletions(-) create mode 100644 SQL/prefs_migration_2023-07-26.sql diff --git a/SQL/database_changelog.txt b/SQL/database_changelog.txt index fdc25b269feb6..ef0ba49342229 100644 --- a/SQL/database_changelog.txt +++ b/SQL/database_changelog.txt @@ -14,523 +14,8 @@ In any query remember to add a prefix to the table names if you use one. Version 7.0, 24 July 2023, by itsmeow Datumized preferences (TGUI Prefs) -DO NOT RUN WITHOUT TAKING A FULL BACKUP. -DO NOT RUN MORE THAN ONCE. -DO NOT RUN COLUMN REMOVALS UNTIL DATA IS VERIFIED. -- Allows nulls for `ss13_characters` columns (required for partial INSERT INTO ON DUPLICATE KEY UPDATE) -- Alters some datatypes to fit new data. -- Turns `ss13_characters`.`features` and `ss13_characters`.`custom_names` JSON objects into individual columns, removes the original columns. -- Updates `ss13_preferences`.`preference_tag` to text. -- Updates various `ss13_preferences` values to new ones. -- Flips `ss13_preferences`.`key_bindings` JSON structure. - -/* CHARACTER PREFERENCES */ - -/* Add new columns and allow nulls on existing columns. Tweak a few datatypes to allow for new data. */ - -ALTER TABLE `SS13_characters` - MODIFY COLUMN `species` VARCHAR(32) COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `real_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, - ADD COLUMN IF NOT EXISTS `human_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `real_name`, - ADD COLUMN IF NOT EXISTS `mime_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `human_name`, - ADD COLUMN IF NOT EXISTS `clown_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `mime_name`, - ADD COLUMN IF NOT EXISTS `cyborg_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `clown_name`, - ADD COLUMN IF NOT EXISTS `ai_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `cyborg_name`, - ADD COLUMN IF NOT EXISTS `religion_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `ai_name`, - ADD COLUMN IF NOT EXISTS `deity_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `religion_name`, - ADD COLUMN IF NOT EXISTS `bible_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `deity_name`, - MODIFY COLUMN `name_is_always_random` TINYINT(1) NULL, - MODIFY COLUMN `body_is_always_random` TINYINT(1) NULL, - MODIFY COLUMN `gender` VARCHAR(16) COLLATE 'utf8mb4_general_ci' NULL, - ADD COLUMN IF NOT EXISTS `body_model` VARCHAR(16) COLLATE 'utf8mb4_general_ci' NULL AFTER `gender`, - ADD COLUMN IF NOT EXISTS `body_size` VARCHAR(16) COLLATE 'utf8mb4_general_ci' NULL AFTER `body_model`, - MODIFY COLUMN `age` TINYINT(3) UNSIGNED NULL, - MODIFY COLUMN `hair_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `gradient_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `facial_hair_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `eye_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `skin_tone` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `hair_style_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `gradient_style` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `facial_style_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `underwear` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `underwear_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `undershirt` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `socks` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `backbag` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `jumpsuit_style` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `uplink_loc` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, - ADD COLUMN IF NOT EXISTS `pda_theme` VARCHAR(32) COLLATE 'utf8mb4_general_ci' NULL AFTER `uplink_loc`, - ADD COLUMN IF NOT EXISTS `pda_classic_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci' NULL AFTER `pda_theme`, - ADD COLUMN IF NOT EXISTS `feature_apid_stripes` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `pda_classic_color`, - ADD COLUMN IF NOT EXISTS `feature_apid_antenna` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_apid_stripes`, - ADD COLUMN IF NOT EXISTS `feature_apid_headstripes` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_apid_antenna`, - ADD COLUMN IF NOT EXISTS `feature_moth_antennae` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_apid_headstripes`, - ADD COLUMN IF NOT EXISTS `feature_moth_markings` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_moth_antennae`, - ADD COLUMN IF NOT EXISTS `feature_moth_wings` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_moth_markings`, - ADD COLUMN IF NOT EXISTS `feature_ethcolor` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_moth_wings`, - ADD COLUMN IF NOT EXISTS `feature_insect_type` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_ethcolor`, - ADD COLUMN IF NOT EXISTS `feature_ipc_screen` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_insect_type`, - ADD COLUMN IF NOT EXISTS `feature_ipc_antenna` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_ipc_screen`, - ADD COLUMN IF NOT EXISTS `feature_ipc_chassis` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_ipc_antenna`, - ADD COLUMN IF NOT EXISTS `feature_ipc_screen_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_ipc_chassis`, - ADD COLUMN IF NOT EXISTS `feature_ipc_antenna_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_ipc_screen_color`, - ADD COLUMN IF NOT EXISTS `feature_lizard_body_markings` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_ipc_antenna_color`, - ADD COLUMN IF NOT EXISTS `feature_lizard_frills` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_lizard_body_markings`, - ADD COLUMN IF NOT EXISTS `feature_lizard_horns` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_lizard_frills`, - ADD COLUMN IF NOT EXISTS `feature_lizard_legs` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_lizard_horns`, - ADD COLUMN IF NOT EXISTS `feature_lizard_snout` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_lizard_legs`, - ADD COLUMN IF NOT EXISTS `feature_lizard_spines` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_lizard_snout`, - ADD COLUMN IF NOT EXISTS `feature_lizard_tail` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_lizard_spines`, - ADD COLUMN IF NOT EXISTS `feature_mcolor` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_lizard_tail`, - ADD COLUMN IF NOT EXISTS `feature_human_tail` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_mcolor`, - ADD COLUMN IF NOT EXISTS `feature_human_ears` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_human_tail`, - MODIFY COLUMN `helmet_style` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `preferred_ai_core_display` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `preferred_security_department` VARCHAR(32) COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `joblessrole` TINYINT(4) UNSIGNED NULL, - MODIFY COLUMN `job_preferences` MEDIUMTEXT COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `all_quirks` MEDIUMTEXT COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `equipped_gear` MEDIUMTEXT COLLATE 'utf8mb4_general_ci' NULL; - -/* Flatten features JSON into its own columns */ - -UPDATE ss13_characters t1, JSON_TABLE( - t1.features, '$' COLUMNS( - body_size_old TEXT PATH '$.body_size', - mcolor_old TEXT PATH '$.mcolor', - ethcolor_old TEXT PATH '$.ethcolor', - tail_lizard_old TEXT PATH '$.lizard_tail', - snout_old TEXT PATH '$.snout', - horns_old TEXT PATH '$.horns', - frills_old TEXT PATH '$.frills', - spines_old TEXT PATH '$.spines', - body_markings_old TEXT PATH '$.body_markings', - moth_wings_old TEXT PATH '$.moth_wings', - ipc_screen_old TEXT PATH '$.ipc_screen', - ipc_antenna_old TEXT PATH '$.ipc_antenna', - ipc_chassis_old TEXT PATH '$.ipc_chassis', - insect_type_old TEXT PATH '$.insect_type', - tail_human_old TEXT PATH '$.tail_human', - ears_old TEXT PATH '$.ears', - body_model_old TEXT PATH '$.body_model', - feature_lizard_legs_old TEXT PATH '$.feature_lizard_legs', - moth_antennae_old TEXT PATH '$.moth_antennae', - moth_markings_old TEXT PATH '$.moth_markings', - apid_antenna_old TEXT PATH '$.apid_antenna', - apid_stripes_old TEXT PATH '$.apid_stripes', - apid_headstripes_old TEXT PATH '$.apid_headstripes' - ) -) AS jt -SET body_size = jt.body_size_old, - feature_mcolor = jt.mcolor_old, - feature_ethcolor = jt.ethcolor_old, - feature_lizard_tail = jt.tail_lizard_old, - feature_lizard_snout = jt.snout_old, - feature_lizard_horns = jt.horns_old, - feature_lizard_frills = jt.frills_old, - feature_lizard_spines = jt.spines_old, - feature_lizard_body_markings = jt.body_markings_old, - feature_moth_wings = jt.moth_wings_old, - feature_ipc_screen = jt.ipc_screen_old, - feature_ipc_antenna = jt.ipc_antenna_old, - feature_ipc_chassis = jt.ipc_chassis_old, - feature_insect_type = jt.insect_type_old, - feature_human_tail = jt.tail_human_old, - feature_human_ears = jt.ears_old, - body_model = jt.body_model_old, - feature_lizard_legs = jt.feature_lizard_legs_old, - feature_moth_antennae = jt.moth_antennae_old, - feature_moth_markings = jt.moth_markings_old, - feature_apid_antenna = jt.apid_antenna_old, - feature_apid_stripes = jt.apid_stripes_old, - feature_apid_headstripes = jt.apid_headstripes_old; - -/* Flatten custom_names JSON into its own columns */ - -UPDATE ss13_characters t1, JSON_TABLE( - t1.custom_names, '$' COLUMNS( - human_name_old TEXT PATH '$.human_name', - mime_name_old TEXT PATH '$.mime_name', - clown_name_old TEXT PATH '$.clown_name', - cyborg_name_old TEXT PATH '$.cyborg_name', - ai_name_old TEXT PATH '$.ai_name', - religion_name_old TEXT PATH '$.religion_name', - deity_name_old TEXT PATH '$.deity_name', - bible_name_old TEXT PATH '$.bible_name' - ) -) AS jt -SET human_name = jt.human_name_old, - mime_name = jt.mime_name_old, - clown_name = jt.clown_name_old, - cyborg_name = jt.cyborg_name_old, - ai_name = jt.ai_name_old, - religion_name = jt.religion_name_old, - deity_name = jt.deity_name_old, - bible_name = jt.bible_name_old; - -/* Delete unused data (features and custom_names, which are now flattened) */ - -ALTER TABLE `SS13_characters` DROP COLUMN IF EXISTS `features`; -ALTER TABLE `SS13_characters` DROP COLUMN IF EXISTS `custom_names`; - -/* PLAYER PREFERENCES */ - -/* Convert tags to strings */ - -ALTER TABLE `SS13_preferences` MODIFY COLUMN `preference_tag` VARCHAR(255) NOT NULL COLLATE 'utf8mb4_general_ci'; - -UPDATE `SS13_preferences` - SET `preference_tag` = CASE - WHEN `preference_tag` = '5' THEN 'last_changelog' - WHEN `preference_tag` = '6' THEN 'ui_style' - WHEN `preference_tag` = '7' THEN 'outline_color' - WHEN `preference_tag` = '8' THEN 'show_balloon_alerts' - WHEN `preference_tag` = '9' THEN 'default_slot' - WHEN `preference_tag` = '11' THEN 'ghost_form' - WHEN `preference_tag` = '12' THEN 'ghost_orbit' - WHEN `preference_tag` = '13' THEN 'ghost_accs' - WHEN `preference_tag` = '14' THEN 'ghost_others' - WHEN `preference_tag` = '15' THEN 'preferred_map' - WHEN `preference_tag` = '16' THEN 'ignoring' - WHEN `preference_tag` = '17' THEN 'clientfps' - WHEN `preference_tag` = '18' THEN 'parallax' - WHEN `preference_tag` = '19' THEN 'pixel_size' - WHEN `preference_tag` = '20' THEN 'scaling_method' - WHEN `preference_tag` = '21' THEN 'tip_delay' - WHEN `preference_tag` = '24' THEN 'key_bindings' - WHEN `preference_tag` = '25' THEN 'purchased_gear' - WHEN `preference_tag` = '26' THEN 'be_special' - WHEN `preference_tag` = '27' THEN 'pai_name' - WHEN `preference_tag` = '28' THEN 'pai_description' - WHEN `preference_tag` = '29' THEN 'pai_comment' - ELSE `preference_tag` - END; - -/* Convert to new values */ -START TRANSACTION; - -UPDATE `SS13_preferences` SET `preference_value` = CASE - WHEN `preference_value` = '1' THEN 'Default sprites' - WHEN `preference_value` = '50' THEN 'Only directional sprites' - WHEN `preference_value` = '100' THEN 'Full accessories' - ELSE `preference_value` - END WHERE `preference_tag` = 'ghost_accs'; - -UPDATE `SS13_preferences` SET `preference_value` = CASE - WHEN `preference_value` = '1' THEN 'White ghosts' - WHEN `preference_value` = '50' THEN 'Default sprites' - WHEN `preference_value` = '100' THEN 'Their sprites' - ELSE `preference_value` - END WHERE `preference_tag` = 'ghost_others'; - -UPDATE `SS13_preferences` SET `preference_value` = '-1' WHERE `preference_tag` = 'clientfps' AND `preference_value` = '40'; - -UPDATE `SS13_preferences` SET `preference_value` = CASE - WHEN `preference_value` = '-1' THEN 'Insane' - WHEN `preference_value` = '0' THEN 'High' - WHEN `preference_value` = '1' THEN 'Medium' - WHEN `preference_value` = '2' THEN 'Low' - WHEN `preference_value` = '3' THEN 'Disabled' - ELSE `preference_value` - END WHERE `preference_tag` = 'parallax'; - -/* Convert key_bindings to new format (flipping JSON structure) */ - -UPDATE - `ss13_preferences` -SET - `preference_value` = ( - SELECT - JSON_OBJECTAGG(`binding`, `keys`) - FROM - ( - SELECT - `binding`, - JSON_ARRAYAGG(`key`) AS `keys` - FROM - ( - SELECT - DISTINCT `binding_list`.`binding`, - `keys`.`key` AS `key` - FROM - JSON_TABLE( - ( - SELECT - JSON_EXTRACT(`preference_value`, '$.*[*]') AS t1 - FROM - `ss13_preferences` - WHERE - `ckey` = `ckey` - AND `preference_tag` = 'key_bindings' - ), - "$[*]" COLUMNS( - `binding` VARCHAR(32) PATH "$" - ) - ) `binding_list` - INNER JOIN ( - SELECT - DISTINCT `key` - FROM - JSON_TABLE( - ( - SELECT - JSON_KEYS(`preference_value`, '$') AS t1 - FROM - `ss13_preferences` - WHERE - `ckey` = `ckey` - AND `preference_tag` = 'key_bindings' - ), - "$[*]" COLUMNS( - `key` VARCHAR(32) PATH "$" - ) - ) `keys_inner` - ) `keys` - WHERE - ( - `key` IN ( - SELECT - SUBSTRING( - SUBSTRING_INDEX(`bindings`.`bind_key`, '[', 1), - 3 - ) - FROM - JSON_TABLE( - ( - SELECT - IF( - JSON_EXISTS( - JSON_SEARCH( - `preference_value`, 'all', `binding` - ), - "$[1]" - ), - JSON_SEARCH( - `preference_value`, 'all', `binding` - ), - JSON_ARRAY( - JSON_SEARCH( - `preference_value`, 'all', `binding` - ) - ) - ) - FROM - `ss13_preferences` - WHERE - `ckey` = `ckey` - AND `preference_tag` = 'key_bindings' - ), - "$[*]" COLUMNS( - `bind_key` VARCHAR(32) PATH "$" - ) - ) `bindings` - ) - ) - ) `full_binds` - GROUP BY - `binding` - ) `outer_full_binds` - ) -WHERE - `preference_tag` = 'key_bindings'; - -/* Finish value conversions */ - -COMMIT; - -/* Convert toggles */ - -START TRANSACTION; - -/* toggles 1 */ - -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'sound_adminhelp',IF(`preference_value` & (1<<0) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'sound_midi',IF(`preference_value` & (1<<1) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'sound_ambience',IF(`preference_value` & (1<<2) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'sound_lobby',IF(`preference_value` & (1<<3) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'member_public',IF(`preference_value` & (1<<4) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'intent_style',IF(`preference_value` & (1<<5) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'sound_instruments',IF(`preference_value` & (1<<7) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'sound_ship_ambience',IF(`preference_value` & (1<<8) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'sound_prayers',IF(`preference_value` & (1<<9) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'announce_login',IF(`preference_value` & (1<<10) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'sound_announcements',IF(`preference_value` & (1<<11) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'death_rattle',IF(`preference_value` & (1<<12) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'arrivals_rattle',IF(`preference_value` & (1<<13) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'combohud_lighting',IF(`preference_value` & (1<<14) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'deadmin_always',IF(`preference_value` & (1<<15) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'deadmin_antagonist',IF(`preference_value` & (1<<16) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'deadmin_position_head',IF(`preference_value` & (1<<17) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'deadmin_position_security',IF(`preference_value` & (1<<18) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'deadmin_position_silicon',IF(`preference_value` & (1<<19) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'itemoutline_pref',IF(`preference_value` & (1<<20) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'chat_on_map',IF(`preference_value` & (1<<21) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'see_chat_non_mob',IF(`preference_value` & (1<<22) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'see_rc_emotes',IF(`preference_value` & (1<<23) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' -); - -/* toggles 2 */ - -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'tgui_fancy',IF(`preference_value` & (1<<0) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'tgui_lock',IF(`preference_value` & (1<<1) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'buttons_locked',IF(`preference_value` & (1<<2) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'windowflashing',IF(`preference_value` & (1<<3) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'crew_objectives',IF(`preference_value` & (1<<4) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'ghost_hud',IF(`preference_value` & (1<<5) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'inquisitive_ghost',IF(`preference_value` & (1<<6) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'glasses_color',IF(`preference_value` & (1<<7) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'ambientocclusion',IF(`preference_value` & (1<<8) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'auto_fit_viewport',IF(`preference_value` & (1<<9) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'enable_tips',IF(`preference_value` & (1<<10) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'show_credits',IF(`preference_value` & (1<<11) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'hotkeys',IF(`preference_value` & (1<<12) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'sound_soundtrack',IF(`preference_value` & (1<<13) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'tgui_input',IF(`preference_value` & (1<<14) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'tgui_input_large',IF(`preference_value` & (1<<15) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'tgui_input_swapped',IF(`preference_value` & (1<<16) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'tgui_say',IF(`preference_value` & (1<<17) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'tgui_say_light_mode',IF(`preference_value` & (1<<18) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'tgui_say_show_prefix',IF(`preference_value` & (1<<19) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'sound_adminalert',IF(`preference_value` & (1<<20) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' -); - -/* chat toggles */ - -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'chat_ooc',IF(`preference_value` & (1<<0) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'chat_dead',IF(`preference_value` & (1<<1) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'chat_ghostears',IF(`preference_value` & (1<<2) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'chat_ghostsight',IF(`preference_value` & (1<<3) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'chat_prayer',IF(`preference_value` & (1<<4) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'chat_radio',IF(`preference_value` & (1<<5) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'chat_pullr',IF(`preference_value` & (1<<6) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'chat_ghostwhisper',IF(`preference_value` & (1<<7) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'chat_ghostpda',IF(`preference_value` & (1<<8) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'chat_ghostradio',IF(`preference_value` & (1<<9) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'chat_bankcard',IF(`preference_value` & (1<<10) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'chat_ghostlaws',IF(`preference_value` & (1<<11) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' -); -INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( - SELECT `ckey`,'chat_ghostfollowmindless',IF(`preference_value` & (1<<12) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' -); - -/* Finish toggle conversions */ - -COMMIT; - -/* Delete unused data (toggles and old PDA preferences, moved to character) */ - -DELETE FROM `SS13_preferences` WHERE `preference_tag` IN ('1', '2', '10', '22', '23'); +See prefs_migartion_2023-07-26.sql ----------------------------------------------------- diff --git a/SQL/prefs_migration_2023-07-26.sql b/SQL/prefs_migration_2023-07-26.sql new file mode 100644 index 0000000000000..83c9ff6722bdd --- /dev/null +++ b/SQL/prefs_migration_2023-07-26.sql @@ -0,0 +1,519 @@ +/* +DO NOT RUN WITHOUT TAKING A FULL BACKUP. +DO NOT RUN MORE THAN ONCE. +DO NOT RUN COLUMN REMOVALS UNTIL DATA IS VERIFIED. +- Allows nulls for `ss13_characters` columns (required for partial INSERT INTO ON DUPLICATE KEY UPDATE) +- Alters some datatypes to fit new data. +- Turns `ss13_characters`.`features` and `ss13_characters`.`custom_names` JSON objects into individual columns, removes the original columns. +- Updates `ss13_preferences`.`preference_tag` to text. +- Updates various `ss13_preferences` values to new ones. +- Flips `ss13_preferences`.`key_bindings` JSON structure. +*/ + +/* CHARACTER PREFERENCES */ + +/* Add new columns and allow nulls on existing columns. Tweak a few datatypes to allow for new data. */ + +ALTER TABLE `SS13_characters` + MODIFY COLUMN `species` VARCHAR(32) COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `real_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, + ADD COLUMN IF NOT EXISTS `human_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `real_name`, + ADD COLUMN IF NOT EXISTS `mime_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `human_name`, + ADD COLUMN IF NOT EXISTS `clown_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `mime_name`, + ADD COLUMN IF NOT EXISTS `cyborg_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `clown_name`, + ADD COLUMN IF NOT EXISTS `ai_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `cyborg_name`, + ADD COLUMN IF NOT EXISTS `religion_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `ai_name`, + ADD COLUMN IF NOT EXISTS `deity_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `religion_name`, + ADD COLUMN IF NOT EXISTS `bible_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `deity_name`, + MODIFY COLUMN `name_is_always_random` TINYINT(1) NULL, + MODIFY COLUMN `body_is_always_random` TINYINT(1) NULL, + MODIFY COLUMN `gender` VARCHAR(16) COLLATE 'utf8mb4_general_ci' NULL, + ADD COLUMN IF NOT EXISTS `body_model` VARCHAR(16) COLLATE 'utf8mb4_general_ci' NULL AFTER `gender`, + ADD COLUMN IF NOT EXISTS `body_size` VARCHAR(16) COLLATE 'utf8mb4_general_ci' NULL AFTER `body_model`, + MODIFY COLUMN `age` TINYINT(3) UNSIGNED NULL, + MODIFY COLUMN `hair_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `gradient_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `facial_hair_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `eye_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `skin_tone` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `hair_style_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `gradient_style` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `facial_style_name` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `underwear` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `underwear_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `undershirt` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `socks` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `backbag` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `jumpsuit_style` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `uplink_loc` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, + ADD COLUMN IF NOT EXISTS `pda_theme` VARCHAR(32) COLLATE 'utf8mb4_general_ci' NULL AFTER `uplink_loc`, + ADD COLUMN IF NOT EXISTS `pda_classic_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci' NULL AFTER `pda_theme`, + ADD COLUMN IF NOT EXISTS `feature_apid_stripes` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `pda_classic_color`, + ADD COLUMN IF NOT EXISTS `feature_apid_antenna` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_apid_stripes`, + ADD COLUMN IF NOT EXISTS `feature_apid_headstripes` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_apid_antenna`, + ADD COLUMN IF NOT EXISTS `feature_moth_antennae` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_apid_headstripes`, + ADD COLUMN IF NOT EXISTS `feature_moth_markings` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_moth_antennae`, + ADD COLUMN IF NOT EXISTS `feature_moth_wings` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_moth_markings`, + ADD COLUMN IF NOT EXISTS `feature_ethcolor` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_moth_wings`, + ADD COLUMN IF NOT EXISTS `feature_insect_type` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_ethcolor`, + ADD COLUMN IF NOT EXISTS `feature_ipc_screen` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_insect_type`, + ADD COLUMN IF NOT EXISTS `feature_ipc_antenna` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_ipc_screen`, + ADD COLUMN IF NOT EXISTS `feature_ipc_chassis` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_ipc_antenna`, + ADD COLUMN IF NOT EXISTS `feature_ipc_screen_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_ipc_chassis`, + ADD COLUMN IF NOT EXISTS `feature_ipc_antenna_color` VARCHAR(8) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_ipc_screen_color`, + ADD COLUMN IF NOT EXISTS `feature_lizard_body_markings` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_ipc_antenna_color`, + ADD COLUMN IF NOT EXISTS `feature_lizard_frills` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_lizard_body_markings`, + ADD COLUMN IF NOT EXISTS `feature_lizard_horns` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_lizard_frills`, + ADD COLUMN IF NOT EXISTS `feature_lizard_legs` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_lizard_horns`, + ADD COLUMN IF NOT EXISTS `feature_lizard_snout` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_lizard_legs`, + ADD COLUMN IF NOT EXISTS `feature_lizard_spines` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_lizard_snout`, + ADD COLUMN IF NOT EXISTS `feature_lizard_tail` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_lizard_spines`, + ADD COLUMN IF NOT EXISTS `feature_mcolor` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_lizard_tail`, + ADD COLUMN IF NOT EXISTS `feature_human_tail` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_mcolor`, + ADD COLUMN IF NOT EXISTS `feature_human_ears` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL AFTER `feature_human_tail`, + MODIFY COLUMN `helmet_style` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `preferred_ai_core_display` VARCHAR(64) COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `preferred_security_department` VARCHAR(32) COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `joblessrole` TINYINT(4) UNSIGNED NULL, + MODIFY COLUMN `job_preferences` MEDIUMTEXT COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `all_quirks` MEDIUMTEXT COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `equipped_gear` MEDIUMTEXT COLLATE 'utf8mb4_general_ci' NULL; + +/* Flatten features JSON into its own columns */ + +UPDATE ss13_characters t1, JSON_TABLE( + t1.features, '$' COLUMNS( + body_size_old TEXT PATH '$.body_size', + mcolor_old TEXT PATH '$.mcolor', + ethcolor_old TEXT PATH '$.ethcolor', + tail_lizard_old TEXT PATH '$.lizard_tail', + snout_old TEXT PATH '$.snout', + horns_old TEXT PATH '$.horns', + frills_old TEXT PATH '$.frills', + spines_old TEXT PATH '$.spines', + body_markings_old TEXT PATH '$.body_markings', + moth_wings_old TEXT PATH '$.moth_wings', + ipc_screen_old TEXT PATH '$.ipc_screen', + ipc_antenna_old TEXT PATH '$.ipc_antenna', + ipc_chassis_old TEXT PATH '$.ipc_chassis', + insect_type_old TEXT PATH '$.insect_type', + tail_human_old TEXT PATH '$.tail_human', + ears_old TEXT PATH '$.ears', + body_model_old TEXT PATH '$.body_model', + feature_lizard_legs_old TEXT PATH '$.feature_lizard_legs', + moth_antennae_old TEXT PATH '$.moth_antennae', + moth_markings_old TEXT PATH '$.moth_markings', + apid_antenna_old TEXT PATH '$.apid_antenna', + apid_stripes_old TEXT PATH '$.apid_stripes', + apid_headstripes_old TEXT PATH '$.apid_headstripes' + ) +) AS jt +SET body_size = jt.body_size_old, + feature_mcolor = jt.mcolor_old, + feature_ethcolor = jt.ethcolor_old, + feature_lizard_tail = jt.tail_lizard_old, + feature_lizard_snout = jt.snout_old, + feature_lizard_horns = jt.horns_old, + feature_lizard_frills = jt.frills_old, + feature_lizard_spines = jt.spines_old, + feature_lizard_body_markings = jt.body_markings_old, + feature_moth_wings = jt.moth_wings_old, + feature_ipc_screen = jt.ipc_screen_old, + feature_ipc_antenna = jt.ipc_antenna_old, + feature_ipc_chassis = jt.ipc_chassis_old, + feature_insect_type = jt.insect_type_old, + feature_human_tail = jt.tail_human_old, + feature_human_ears = jt.ears_old, + body_model = jt.body_model_old, + feature_lizard_legs = jt.feature_lizard_legs_old, + feature_moth_antennae = jt.moth_antennae_old, + feature_moth_markings = jt.moth_markings_old, + feature_apid_antenna = jt.apid_antenna_old, + feature_apid_stripes = jt.apid_stripes_old, + feature_apid_headstripes = jt.apid_headstripes_old; + +/* Flatten custom_names JSON into its own columns */ + +UPDATE ss13_characters t1, JSON_TABLE( + t1.custom_names, '$' COLUMNS( + human_name_old TEXT PATH '$.human_name', + mime_name_old TEXT PATH '$.mime_name', + clown_name_old TEXT PATH '$.clown_name', + cyborg_name_old TEXT PATH '$.cyborg_name', + ai_name_old TEXT PATH '$.ai_name', + religion_name_old TEXT PATH '$.religion_name', + deity_name_old TEXT PATH '$.deity_name', + bible_name_old TEXT PATH '$.bible_name' + ) +) AS jt +SET human_name = jt.human_name_old, + mime_name = jt.mime_name_old, + clown_name = jt.clown_name_old, + cyborg_name = jt.cyborg_name_old, + ai_name = jt.ai_name_old, + religion_name = jt.religion_name_old, + deity_name = jt.deity_name_old, + bible_name = jt.bible_name_old; + +/* Delete unused data (features and custom_names, which are now flattened) */ + +ALTER TABLE `SS13_characters` DROP COLUMN IF EXISTS `features`; +ALTER TABLE `SS13_characters` DROP COLUMN IF EXISTS `custom_names`; + +/* PLAYER PREFERENCES */ + +/* Convert tags to strings */ + +ALTER TABLE `SS13_preferences` MODIFY COLUMN `preference_tag` VARCHAR(255) NOT NULL COLLATE 'utf8mb4_general_ci'; + +UPDATE `SS13_preferences` + SET `preference_tag` = CASE + WHEN `preference_tag` = '5' THEN 'last_changelog' + WHEN `preference_tag` = '6' THEN 'ui_style' + WHEN `preference_tag` = '7' THEN 'outline_color' + WHEN `preference_tag` = '8' THEN 'show_balloon_alerts' + WHEN `preference_tag` = '9' THEN 'default_slot' + WHEN `preference_tag` = '11' THEN 'ghost_form' + WHEN `preference_tag` = '12' THEN 'ghost_orbit' + WHEN `preference_tag` = '13' THEN 'ghost_accs' + WHEN `preference_tag` = '14' THEN 'ghost_others' + WHEN `preference_tag` = '15' THEN 'preferred_map' + WHEN `preference_tag` = '16' THEN 'ignoring' + WHEN `preference_tag` = '17' THEN 'clientfps' + WHEN `preference_tag` = '18' THEN 'parallax' + WHEN `preference_tag` = '19' THEN 'pixel_size' + WHEN `preference_tag` = '20' THEN 'scaling_method' + WHEN `preference_tag` = '21' THEN 'tip_delay' + WHEN `preference_tag` = '24' THEN 'key_bindings' + WHEN `preference_tag` = '25' THEN 'purchased_gear' + WHEN `preference_tag` = '26' THEN 'be_special' + WHEN `preference_tag` = '27' THEN 'pai_name' + WHEN `preference_tag` = '28' THEN 'pai_description' + WHEN `preference_tag` = '29' THEN 'pai_comment' + ELSE `preference_tag` + END; + +/* Convert to new values */ + +START TRANSACTION; + +UPDATE `SS13_preferences` SET `preference_value` = CASE + WHEN `preference_value` = '1' THEN 'Default sprites' + WHEN `preference_value` = '50' THEN 'Only directional sprites' + WHEN `preference_value` = '100' THEN 'Full accessories' + ELSE `preference_value` + END WHERE `preference_tag` = 'ghost_accs'; + +UPDATE `SS13_preferences` SET `preference_value` = CASE + WHEN `preference_value` = '1' THEN 'White ghosts' + WHEN `preference_value` = '50' THEN 'Default sprites' + WHEN `preference_value` = '100' THEN 'Their sprites' + ELSE `preference_value` + END WHERE `preference_tag` = 'ghost_others'; + +UPDATE `SS13_preferences` SET `preference_value` = '-1' WHERE `preference_tag` = 'clientfps' AND `preference_value` = '40'; + +UPDATE `SS13_preferences` SET `preference_value` = CASE + WHEN `preference_value` = '-1' THEN 'Insane' + WHEN `preference_value` = '0' THEN 'High' + WHEN `preference_value` = '1' THEN 'Medium' + WHEN `preference_value` = '2' THEN 'Low' + WHEN `preference_value` = '3' THEN 'Disabled' + ELSE `preference_value` + END WHERE `preference_tag` = 'parallax'; + +/* Convert key_bindings to new format (flipping JSON structure) */ + +UPDATE + `ss13_preferences` +SET + `preference_value` = ( + SELECT + JSON_OBJECTAGG(`binding`, `keys`) + FROM + ( + SELECT + `binding`, + JSON_ARRAYAGG(`key`) AS `keys` + FROM + ( + SELECT + DISTINCT `binding_list`.`binding`, + `keys`.`key` AS `key` + FROM + JSON_TABLE( + ( + SELECT + JSON_EXTRACT(`preference_value`, '$.*[*]') AS t1 + FROM + `ss13_preferences` + WHERE + `ckey` = `ckey` + AND `preference_tag` = 'key_bindings' + ), + "$[*]" COLUMNS( + `binding` VARCHAR(32) PATH "$" + ) + ) `binding_list` + INNER JOIN ( + SELECT + DISTINCT `key` + FROM + JSON_TABLE( + ( + SELECT + JSON_KEYS(`preference_value`, '$') AS t1 + FROM + `ss13_preferences` + WHERE + `ckey` = `ckey` + AND `preference_tag` = 'key_bindings' + ), + "$[*]" COLUMNS( + `key` VARCHAR(32) PATH "$" + ) + ) `keys_inner` + ) `keys` + WHERE + ( + `key` IN ( + SELECT + SUBSTRING( + SUBSTRING_INDEX(`bindings`.`bind_key`, '[', 1), + 3 + ) + FROM + JSON_TABLE( + ( + SELECT + IF( + JSON_EXISTS( + JSON_SEARCH( + `preference_value`, 'all', `binding` + ), + "$[1]" + ), + JSON_SEARCH( + `preference_value`, 'all', `binding` + ), + JSON_ARRAY( + JSON_SEARCH( + `preference_value`, 'all', `binding` + ) + ) + ) + FROM + `ss13_preferences` + WHERE + `ckey` = `ckey` + AND `preference_tag` = 'key_bindings' + ), + "$[*]" COLUMNS( + `bind_key` VARCHAR(32) PATH "$" + ) + ) `bindings` + ) + ) + ) `full_binds` + GROUP BY + `binding` + ) `outer_full_binds` + ) +WHERE + `preference_tag` = 'key_bindings'; + +/* Finish value conversions */ + +COMMIT; + +/* Convert toggles */ + +START TRANSACTION; + +/* toggles 1 */ + +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'sound_adminhelp',IF(`preference_value` & (1<<0) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'sound_midi',IF(`preference_value` & (1<<1) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'sound_ambience',IF(`preference_value` & (1<<2) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'sound_lobby',IF(`preference_value` & (1<<3) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'member_public',IF(`preference_value` & (1<<4) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'intent_style',IF(`preference_value` & (1<<5) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'sound_instruments',IF(`preference_value` & (1<<7) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'sound_ship_ambience',IF(`preference_value` & (1<<8) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'sound_prayers',IF(`preference_value` & (1<<9) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'announce_login',IF(`preference_value` & (1<<10) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'sound_announcements',IF(`preference_value` & (1<<11) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'death_rattle',IF(`preference_value` & (1<<12) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'arrivals_rattle',IF(`preference_value` & (1<<13) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'combohud_lighting',IF(`preference_value` & (1<<14) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'deadmin_always',IF(`preference_value` & (1<<15) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'deadmin_antagonist',IF(`preference_value` & (1<<16) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'deadmin_position_head',IF(`preference_value` & (1<<17) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'deadmin_position_security',IF(`preference_value` & (1<<18) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'deadmin_position_silicon',IF(`preference_value` & (1<<19) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'itemoutline_pref',IF(`preference_value` & (1<<20) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'chat_on_map',IF(`preference_value` & (1<<21) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'see_chat_non_mob',IF(`preference_value` & (1<<22) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'see_rc_emotes',IF(`preference_value` & (1<<23) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '1' +); + +/* toggles 2 */ + +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'tgui_fancy',IF(`preference_value` & (1<<0) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'tgui_lock',IF(`preference_value` & (1<<1) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'buttons_locked',IF(`preference_value` & (1<<2) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'windowflashing',IF(`preference_value` & (1<<3) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'crew_objectives',IF(`preference_value` & (1<<4) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'ghost_hud',IF(`preference_value` & (1<<5) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'inquisitive_ghost',IF(`preference_value` & (1<<6) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'glasses_color',IF(`preference_value` & (1<<7) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'ambientocclusion',IF(`preference_value` & (1<<8) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'auto_fit_viewport',IF(`preference_value` & (1<<9) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'enable_tips',IF(`preference_value` & (1<<10) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'show_credits',IF(`preference_value` & (1<<11) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'hotkeys',IF(`preference_value` & (1<<12) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'sound_soundtrack',IF(`preference_value` & (1<<13) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'tgui_input',IF(`preference_value` & (1<<14) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'tgui_input_large',IF(`preference_value` & (1<<15) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'tgui_input_swapped',IF(`preference_value` & (1<<16) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'tgui_say',IF(`preference_value` & (1<<17) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'tgui_say_light_mode',IF(`preference_value` & (1<<18) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'tgui_say_show_prefix',IF(`preference_value` & (1<<19) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'sound_adminalert',IF(`preference_value` & (1<<20) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '2' +); + +/* chat toggles */ + +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'chat_ooc',IF(`preference_value` & (1<<0) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'chat_dead',IF(`preference_value` & (1<<1) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'chat_ghostears',IF(`preference_value` & (1<<2) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'chat_ghostsight',IF(`preference_value` & (1<<3) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'chat_prayer',IF(`preference_value` & (1<<4) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'chat_radio',IF(`preference_value` & (1<<5) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'chat_pullr',IF(`preference_value` & (1<<6) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'chat_ghostwhisper',IF(`preference_value` & (1<<7) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'chat_ghostpda',IF(`preference_value` & (1<<8) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'chat_ghostradio',IF(`preference_value` & (1<<9) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'chat_bankcard',IF(`preference_value` & (1<<10) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'chat_ghostlaws',IF(`preference_value` & (1<<11) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' +); +INSERT IGNORE INTO `ss13_preferences` (`ckey`, `preference_tag`, `preference_value`) ( + SELECT `ckey`,'chat_ghostfollowmindless',IF(`preference_value` & (1<<12) > 0, 1, 0) AS `preference_value` FROM `ss13_preferences` WHERE `preference_tag` = '10' +); + +/* Finish toggle conversions */ + +COMMIT; + +/* Delete unused data (toggles and old PDA preferences, moved to character) */ + +DELETE FROM `SS13_preferences` WHERE `preference_tag` IN ('1', '2', '10', '22', '23'); From 5d05a322cb67c68be77a508d6e190f4d59dab9cd Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 26 Jul 2023 15:56:52 -0400 Subject: [PATCH 236/269] Fix queries --- SQL/prefs_migration_2023-07-26.sql | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/SQL/prefs_migration_2023-07-26.sql b/SQL/prefs_migration_2023-07-26.sql index 83c9ff6722bdd..33008198b4600 100644 --- a/SQL/prefs_migration_2023-07-26.sql +++ b/SQL/prefs_migration_2023-07-26.sql @@ -1,4 +1,7 @@ /* + +Tested on MariaDB 10.11 + DO NOT RUN WITHOUT TAKING A FULL BACKUP. DO NOT RUN MORE THAN ONCE. DO NOT RUN COLUMN REMOVALS UNTIL DATA IS VERIFIED. @@ -77,7 +80,9 @@ ALTER TABLE `SS13_characters` MODIFY COLUMN `joblessrole` TINYINT(4) UNSIGNED NULL, MODIFY COLUMN `job_preferences` MEDIUMTEXT COLLATE 'utf8mb4_general_ci' NULL, MODIFY COLUMN `all_quirks` MEDIUMTEXT COLLATE 'utf8mb4_general_ci' NULL, - MODIFY COLUMN `equipped_gear` MEDIUMTEXT COLLATE 'utf8mb4_general_ci' NULL; + MODIFY COLUMN `equipped_gear` MEDIUMTEXT COLLATE 'utf8mb4_general_ci' NULL, + MODIFY COLUMN `role_preferences` MEDIUMTEXT COLLATE 'utf8mb4_general_ci' NULL, + ADD COLUMN IF NOT EXISTS `randomise` MEDIUMTEXT COLLATE 'utf8mb4_general_ci' NULL AFTER `role_preferences`; /* Flatten features JSON into its own columns */ From 5970f497b22342892a7b7f812457f7304e2e402d Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 26 Jul 2023 16:31:50 -0400 Subject: [PATCH 237/269] Allow global per-character prefs to exist --- .../client/preferences/serialization/preferences_database.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/client/preferences/serialization/preferences_database.dm b/code/modules/client/preferences/serialization/preferences_database.dm index a9e5ca29fedb9..6507455490310 100644 --- a/code/modules/client/preferences/serialization/preferences_database.dm +++ b/code/modules/client/preferences/serialization/preferences_database.dm @@ -96,7 +96,7 @@ for(var/preference in role_preferences_global) var/path = text2path(preference) var/datum/role_preference/entry = GLOB.role_preference_entries[path] - if(istype(entry) && !entry.per_character) + if(istype(entry)) continue role_preferences_global -= preference From e6e8e7342e87f77f78c0d6edc7766ea855a1ffcb Mon Sep 17 00:00:00 2001 From: itsmeow Date: Wed, 26 Jul 2023 17:12:55 -0400 Subject: [PATCH 238/269] Add a button to swap from character<->game prefs --- code/__DEFINES/preferences.dm | 3 --- .../modules/client/preferences/preferences.dm | 21 +++++++++---------- .../CharacterPreferenceWindow.tsx | 17 ++++++++++++++- .../PreferencesMenu/GamePreferenceWindow.tsx | 19 +++++++++++++++-- 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm index 0e16bf6ba21c4..e6e3357ec6601 100644 --- a/code/__DEFINES/preferences.dm +++ b/code/__DEFINES/preferences.dm @@ -103,9 +103,6 @@ GLOBAL_LIST_INIT(helmet_styles, list( /// Open the game preferences window #define PREFERENCE_TAB_GAME_PREFERENCES 1 -/// Open the keybindings window -#define PREFERENCE_TAB_KEYBINDINGS 2 - /// These will be shown in the character sidebar, but at the bottom. #define PREFERENCE_CATEGORY_FEATURES "features" diff --git a/code/modules/client/preferences/preferences.dm b/code/modules/client/preferences/preferences.dm index 4b98a632f6356..9681eff81039e 100644 --- a/code/modules/client/preferences/preferences.dm +++ b/code/modules/client/preferences/preferences.dm @@ -306,6 +306,16 @@ GLOBAL_LIST_EMPTY(preferences_datums) return FALSE return TRUE + if("open_game_preferences") + current_window = PREFERENCE_TAB_GAME_PREFERENCES + update_static_data(usr) + ui_interact(usr) + return TRUE + if("open_character_preferences") + current_window = PREFERENCE_TAB_CHARACTER_PREFERENCES + update_static_data(usr) + ui_interact(usr) + return TRUE for (var/datum/preference_middleware/preference_middleware as anything in middleware) var/delegation = preference_middleware.action_delegations[action] @@ -320,17 +330,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) save_preferences() character_preview_view.unregister_from_client(user.client) -/datum/preferences/Topic(href, list/href_list) - . = ..() - if (.) - return - - if (href_list["open_keybindings"]) - current_window = PREFERENCE_TAB_KEYBINDINGS - update_static_data(usr) - ui_interact(usr) - return TRUE - /datum/preferences/proc/compile_character_preferences(mob/user) var/list/preferences = list() diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx index c9f2ec1f6b622..37b68b040fbf1 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx @@ -83,7 +83,22 @@ export const CharacterPreferenceWindow = (props, context) => { } return ( - }> + +