From ec22fccc6a822af9a24f88841ff9848bba5d50ca Mon Sep 17 00:00:00 2001 From: SabreML <57483089+SabreML@users.noreply.github.com> Date: Mon, 26 Jan 2026 23:39:22 +0000 Subject: [PATCH] TGUI Faction Select (#21737) ## Description Hello! First PR here so I figured I'd do a big one ~~and stress out the maintainers as quickly as possible~~. :) While going through the process of creating a character the 'faction select' UI stuck out to me as *functional*, but difficult to parse visually and pretty dated looking by modern SS13 standards. (Not too bad, but you know what I mean) The new version is realistically *bit* too flashy for what it is (a single-use interface hidden away in the character creation menu), but it was very good practise for me to get back into DM/TGUI programming so it works out either way. I'm very happy to implement any feedback or suggested changes that could help improve it. ## Actual Description This PR replaces the old HTML 'faction select' interface with a new TGUI one, and also cleans up and fixes some adjacent code I bumped into while working on it. A couple of notes about the current version: * The 'Citizenship Check' line is currently non-functional and will always show a checkmark. I know a lot of the corporations have specific backgrounds/origins they won't hire people from, but I wasn't sure where exactly that's enforced in code, if at all. (I noticed `/datum/faction` has a `blacklisted_citizenship_types` variable, but it's currently unused.) * De-adminning or changing species doesn't immediately update the UI to reflect the changes, you have to click on something new to trigger an update manually. This doesn't have any actual consequences since I made sure to sanitise everything, but it is a visual thing. ## Screenshots
Collapsed so it doesn't take up so much room: ### Before xziPCYXbkn ### After NT ### Unathi attempting to join Zeng-Hu (Button disabled) Unathi fail ### Faction Pages
Stellar Corporate Conglomerate SCC
Nanotrasen NT
Hephaestus Industries HI
Idris Incorporated II
Orion Express OE
Private Military Contracting Group PMCG
Zavodskoi Interstellar ZI
Zeng-Hu Pharmaceuticals ZHP
Independent Independent

## Changelog - rscadd: "Revamped the 'Faction Select' UI in the occupation menu of character setup." - bugfix: "Fixed changing species in character setup not resetting your faction if it became invalid." - Not player-facing: - Fixed the TGUI dev server not working by bumping up the 'source-map' version. You still have to manually refresh the interface with Ctrl+R or Ctrl+F5 but it actually links now. - Added a `wiki_page` variable to each faction to allow for it to be manually set rather than being exclusively based on the name. (Mostly just futureproofing) - Added some line breaks to the faction descriptions to make them more easily readable. --------- Signed-off-by: SabreML <57483089+SabreML@users.noreply.github.com> Co-authored-by: VMSolidus --- aurorastation.dme | 1 + code/__HELPERS/sorts/cmp.dm | 3 + code/game/jobs/faction/admin.dm | 9 +- code/game/jobs/faction/faction.dm | 24 +- code/game/jobs/faction/hephaestus.dm | 9 +- code/game/jobs/faction/idris.dm | 9 +- code/game/jobs/faction/nanotrasen.dm | 13 +- code/game/jobs/faction/orion_express.dm | 11 +- code/game/jobs/faction/pmc.dm | 11 +- code/game/jobs/faction/scc.dm | 11 +- code/game/jobs/faction/unaffiliated.dm | 10 +- code/game/jobs/faction/zavodskoi.dm | 11 +- code/game/jobs/faction/zeng_hu.dm | 10 +- .../preference_setup/general/03_body.dm | 5 + .../preference_setup/occupation/occupation.dm | 81 +----- code/modules/tgui/modules/faction_select.dm | 95 +++++++ .../SabreML-PR-faction-select-tgui.yml | 14 ++ tgui/packages/tgui-dev-server/package.json | 2 +- .../tgui/interfaces/FactionSelect.tsx | 232 ++++++++++++++++++ .../tgui/styles/interfaces/FactionSelect.scss | 36 +++ tgui/packages/tgui/styles/main.scss | 5 +- tgui/yarn.lock | 9 +- 22 files changed, 482 insertions(+), 129 deletions(-) create mode 100644 code/modules/tgui/modules/faction_select.dm create mode 100644 html/changelogs/SabreML-PR-faction-select-tgui.yml create mode 100644 tgui/packages/tgui/interfaces/FactionSelect.tsx create mode 100644 tgui/packages/tgui/styles/interfaces/FactionSelect.scss diff --git a/aurorastation.dme b/aurorastation.dme index 4bbd1183f80..40c501046fb 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -3848,6 +3848,7 @@ #include "code\modules\tgui\tgui.dm" #include "code\modules\tgui\tgui_window.dm" #include "code\modules\tgui\modules\armor_values.dm" +#include "code\modules\tgui\modules\faction_select.dm" #include "code\modules\tgui\modules\flavor_text.dm" #include "code\modules\tgui\modules\hivenet_manifest.dm" #include "code\modules\tgui\modules\ipc_diagnostic.dm" diff --git a/code/__HELPERS/sorts/cmp.dm b/code/__HELPERS/sorts/cmp.dm index 4b3b935c6e7..4f582518cd2 100644 --- a/code/__HELPERS/sorts/cmp.dm +++ b/code/__HELPERS/sorts/cmp.dm @@ -119,3 +119,6 @@ /proc/cmp_fusion_reaction_des(singleton/fusion_reaction/A, singleton/fusion_reaction/B) return B.priority - A.priority + +/proc/cmp_faction(datum/faction/A, datum/faction/B) + return A.ui_priority - B.ui_priority diff --git a/code/game/jobs/faction/admin.dm b/code/game/jobs/faction/admin.dm index 0dc028aa2d2..0439ee18600 100644 --- a/code/game/jobs/faction/admin.dm +++ b/code/game/jobs/faction/admin.dm @@ -1,12 +1,11 @@ /datum/faction/admin name = "Admin Jobs" - description = {"

- This faction is for IC administrative jobs used by staff. -

"} - - departments = {"Command"} + description = "This faction is for IC administrative jobs used by staff." + departments = list(DEPARTMENT_COMMAND) title_suffix = "SCC" + ui_priority = -INFINITY // top of the list + allowed_role_types = ADMIN_ROLES allowed_species_types = list( diff --git a/code/game/jobs/faction/faction.dm b/code/game/jobs/faction/faction.dm index 75493c3f379..ddf7ee41ef2 100644 --- a/code/game/jobs/faction/faction.dm +++ b/code/game/jobs/faction/faction.dm @@ -2,7 +2,11 @@ var/name var/description var/title_suffix - var/departments + var/list/departments + var/wiki_page + + /// Number indicating the list index of this faction in the 'faction select' interface. + var/ui_priority var/list/allowed_role_types var/list/allowed_species_types @@ -60,33 +64,31 @@ role.blacklisted_species = J.blacklisted_species . += role -/datum/faction/proc/get_selection_error(datum/preferences/prefs, var/mob/user) +/datum/faction/proc/get_selection_error(datum/preferences/prefs, mob/user) if(length(allowed_species_types)) - var/datum/species/S = prefs.get_species_datum() if(!S) - return "No valid species selected." + return "No valid species" if(!is_type_in_typecache(S, allowed_species_types)) - return "Invalid species selected." + return "Invalid species" if(length(blacklisted_citizenship_types)) - var/datum/citizenship/C = SSrecords.citizenships[prefs.citizenship] if(!C) - return "No valid nation selected." + return "No valid nation" if(is_type_in_typecache(C, blacklisted_citizenship_types)) - return "Invalid nation selected." + return "Invalid nation" - if (!is_visible(user)) - return "This faction is not available to you." + if(!is_visible(user)) + return "This faction is not available to you" return null -/datum/faction/proc/can_select(datum/preferences/prefs, var/mob/user) +/datum/faction/proc/can_select(datum/preferences/prefs, mob/user) return !get_selection_error(prefs, user) /datum/faction/proc/get_logo_name() diff --git a/code/game/jobs/faction/hephaestus.dm b/code/game/jobs/faction/hephaestus.dm index e0ec679eb6d..674b2948e74 100644 --- a/code/game/jobs/faction/hephaestus.dm +++ b/code/game/jobs/faction/hephaestus.dm @@ -1,17 +1,18 @@ /datum/faction/hephaestus_industries name = "Hephaestus Industries" - description = {"

+ description = {" Hephaestus Industries, a sprawling and diverse mega-corporation focused on engineering and manufacturing on a massive scale, found their start - as a conglomerate of several aerospace companies in the 22nd century. Initially + as a conglomerate of several aerospace companies in the 22nd century.

Initially funded by sales of new designs for warp technology, the company fell on hard times during the Second Great Depression in the late 23rd century. Receiving bailouts from the Sol Alliance and securing several crucial production contracts, they have slowly worked their way to become the dominant manufacturing mega-corporation in the Sol Alliance, pioneering interstellar logistics and construction on an awe-inspiring scale. -

"} - departments = {"Engineering
Operations"} + "} + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_CARGO) title_suffix = "Hepht" + wiki_page = "Hephaestus_Industries" allowed_role_types = HEPH_ROLES diff --git a/code/game/jobs/faction/idris.dm b/code/game/jobs/faction/idris.dm index 717916262d6..faa1a68b57a 100644 --- a/code/game/jobs/faction/idris.dm +++ b/code/game/jobs/faction/idris.dm @@ -1,16 +1,17 @@ /datum/faction/idris_incorporated name = "Idris Incorporated" - description = {"

+ description = {" The Orion Spur's largest interstellar banking conglomerate, Idris Incorporated is operated by the mysterious Idris family. Idris Incorporated's influence can be found in nearly every corner of human space with their financing of - nearly every type of business and enterprise. Their higher risk ventures have + nearly every type of business and enterprise.
Their higher risk ventures have payment enforced by the infamous Idris Reclamation Units, shell IPCs sent to claim payment from negligent loan takers. In recent years, they have begun diversifying into more service-based industries. -

"} - departments = {"Security
Service"} + "} + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_SERVICE) title_suffix = "Idris" + wiki_page = "Idris_Incorporated" allowed_role_types = IDRIS_ROLES diff --git a/code/game/jobs/faction/nanotrasen.dm b/code/game/jobs/faction/nanotrasen.dm index a9469d53d2e..92eda15db9f 100644 --- a/code/game/jobs/faction/nanotrasen.dm +++ b/code/game/jobs/faction/nanotrasen.dm @@ -1,15 +1,18 @@ /datum/faction/nano_trasen name = "NanoTrasen" - description = {"

- Considered the largest megacorporation within the Orion Spur, many will find themselves doing the biddings of NanoTrasen. + description = {" + Considered the largest megacorporation within the Orion Spur, many will find themselves doing the biddings of NanoTrasen.
Initially a biotechnical research company, it rapidly grew in size after the discovery of phoron. - NanoTrasen's continued monopoly on the resource catapulted it into the limelight, where it has remained for the last forty-odd years. + NanoTrasen's continued monopoly on the resource catapulted it into the limelight, where it has remained for the last forty-odd years.

During the Phoron Scarcity, NanoTrasen's power has since begun to waver, resulting in their profit margins diminishing considerably. Nonetheless, NanoTrasen has managed to secure itself as a crucial member of the newly-founded Stellar Corporate Conglomerate allowing themselves to remain as a dominant corporate presence within the Orion Spur. -

"} - departments = {"Medical
Research
Service"} + "} + departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_SCIENCE, DEPARTMENT_SERVICE) title_suffix = "NT" + wiki_page = "NanoTrasen_Corporation" + + ui_priority = -1 is_default = TRUE diff --git a/code/game/jobs/faction/orion_express.dm b/code/game/jobs/faction/orion_express.dm index 59a6f07c035..bf64a623fa8 100644 --- a/code/game/jobs/faction/orion_express.dm +++ b/code/game/jobs/faction/orion_express.dm @@ -1,15 +1,16 @@ /datum/faction/orion_express name = "Orion Express" - description = {"

+ description = {" Founded in 2464, the Orion Express is a corporation designed to handle logistics for the Stellar Corporate Conglomerate in the wake of the Phoron Scarcity and the sudden issues - the Conglomeration of the megacorporations presented. It consists of its main branch, dedicated + the Conglomeration of the megacorporations presented.
It consists of its main branch, dedicated to cargo services and transport, but also features a fledgling robotics division, mainly focused - on industrial synthetics to aid in its logistics missions. The Orion Express is expected to become an + on industrial synthetics to aid in its logistics missions.
The Orion Express is expected to become an integral part of the Stellar Corporate Conglomerate's future through delivering supplies and merchandise throughout the Orion Spur. -

"} - departments = {"Operations
Service"} + "} + departments = list(DEPARTMENT_CARGO, DEPARTMENT_SERVICE) title_suffix = "Orion" + wiki_page = "Orion_Express" allowed_role_types = ORION_ROLES diff --git a/code/game/jobs/faction/pmc.dm b/code/game/jobs/faction/pmc.dm index e43db29e4b4..53fdaad3986 100644 --- a/code/game/jobs/faction/pmc.dm +++ b/code/game/jobs/faction/pmc.dm @@ -1,16 +1,17 @@ /datum/faction/pmc name = "Private Military Contracting Group" - description = {"

+ description = {" The Private Military Contracting Group is a coalition of security and medical contractors; - borne from the necessity of protecting the Stellar Corporate Conglomerate and its holdings. + borne from the necessity of protecting the Stellar Corporate Conglomerate and its holdings.
Following the ever-growing corporate empire, mercenaries and contractors from all across the spur are deployed accordingly; - from mere office buildings to outposts in the Corporate Reconstruction Zone. + from mere office buildings to outposts in the Corporate Reconstruction Zone.

Unlike the other members of the Corporate Conglomerate, the Private Military Contracting Group has few employees of its own. Only some liaisons and bureaucrats work behind the scenes to hire and manage the contractors; the rest of its members are part of several organizations contracted to supply the PMCG. -

"} - departments = {"Medical
Security"} + "} + departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_SECURITY) title_suffix = "PMCG" + wiki_page = "Private_Military_Contracting_Group" allowed_role_types = PMC_ROLES diff --git a/code/game/jobs/faction/scc.dm b/code/game/jobs/faction/scc.dm index 45c6292ac77..f35a9b0916d 100644 --- a/code/game/jobs/faction/scc.dm +++ b/code/game/jobs/faction/scc.dm @@ -1,16 +1,19 @@ /datum/faction/scc name = "Stellar Corporate Conglomerate" - description = {"

+ description = {" The Stellar Corporate Conglomerate (SCC) was formed at the height of corporate power in the galaxy, originally to secure Tau Ceti assets during the Second Solarian Incursion. Chainlink now exercises undisputed economic dominance over the Orion Spur, - and is now willing and eager to supersede any and all higher authority placed before it. + and is now willing and eager to supersede any and all higher authority placed before it.
Cooperation has been deemed essential ever since Einstein Engines resurfaced due to the emerging prominence of warp travel. They've remained in a shaky peace, puppeteered to carry out the engimatic whims of the Trasen family. Of course, this doesn't stop their greed for power and glory - they're just corporations, after all. -

"} - departments = {"Command
Equipment"} + "} + departments = list(DEPARTMENT_COMMAND, DEPARTMENT_EQUIPMENT) title_suffix = "SCC" + wiki_page = "Stellar_Corporate_Conglomerate" + + ui_priority = -2 allowed_role_types = SCC_ROLES diff --git a/code/game/jobs/faction/unaffiliated.dm b/code/game/jobs/faction/unaffiliated.dm index 6d1a56bc732..e968c7d720f 100644 --- a/code/game/jobs/faction/unaffiliated.dm +++ b/code/game/jobs/faction/unaffiliated.dm @@ -1,14 +1,16 @@ /datum/faction/unaffiliated name = "Independent" - description = {"

+ description = {" For individuals not necessarily tied to any of the large mega corporations lording over human space, the mark of "independent" can open as many doors as it is likely to - close. You would often see people lacking a concrete affiliation popping up as + close.
You would often see people lacking a concrete affiliation popping up as reporters, journalists, individual traders. Their presence, though often an inconvenience for the mega corporations, is often necessary to forward the facade of economic freedom that Tau Ceti reports to have. -

"} - departments = {"Civilian"} + "} + departments = list(DEPARTMENT_CIVILIAN) allowed_role_types = INDEP_ROLES title_suffix = "INDEP" + + ui_priority = INFINITY // bottom of the list diff --git a/code/game/jobs/faction/zavodskoi.dm b/code/game/jobs/faction/zavodskoi.dm index ce374354795..52c8aa4eabe 100644 --- a/code/game/jobs/faction/zavodskoi.dm +++ b/code/game/jobs/faction/zavodskoi.dm @@ -1,15 +1,16 @@ /datum/faction/zavodskoi_interstellar name = "Zavodskoi Interstellar" - description = {"

+ description = {" The largest weapons producer in human space, Zavodskoi Interstellar initially - found its place with the invention of a militarized voidsuit for use in the Interstellar War. + found its place with the invention of a militarized voidsuit for use in the Interstellar War.
With many lucrative weapon contracts thanks to the Sol Alliance, as well as acquisitions of other major armaments companies, Zavodskoi weapons can be found in the hands of nearly every - military force across the Orion Spur. They are the main corporation found in the Empire of + military force across the Orion Spur.
They are the main corporation found in the Empire of Dominia, and are at the forefront of weapons development technology. -

"} - departments = {"Engineering
Research
Security"} + "} + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_SCIENCE, DEPARTMENT_SECURITY) title_suffix = "Zavod" + wiki_page = "Zavodskoi_Interstellar" allowed_role_types = ZAVOD_ROLES diff --git a/code/game/jobs/faction/zeng_hu.dm b/code/game/jobs/faction/zeng_hu.dm index cf9f6cb5d08..c527ac2bd0e 100644 --- a/code/game/jobs/faction/zeng_hu.dm +++ b/code/game/jobs/faction/zeng_hu.dm @@ -1,18 +1,18 @@ /datum/faction/zeng_hu name = "Zeng-Hu Pharmaceuticals" - description = {"

+ description = {" Zeng-Hu Pharmaceuticals, born of a merger of two major biotech companies on Earth in 2032, was the first to successfully develop cryogenics in the 21st century for the purposes of space travel. This development, crucial to interstellar colonization, helped propel them to their current position as the - largest pharmaceutical and medical corporation in the Orion Spur. In more recent + largest pharmaceutical and medical corporation in the Orion Spur.
In more recent years, they were also the first mega-corporation to partner with the newly-discovered Skrell, working closely with this alien species to pioneer cloning, a once controversial field that is now more accepted today. -

"} - - departments = {"Medical
Research"} + "} + departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_SCIENCE) title_suffix = "Zeng" + wiki_page = "Zeng-Hu_Pharmaceuticals" allowed_role_types = ZENG_ROLES diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm index 7acc3d13966..7ef392dc41d 100644 --- a/code/modules/client/preference_setup/general/03_body.dm +++ b/code/modules/client/preference_setup/general/03_body.dm @@ -490,6 +490,11 @@ GLOBAL_LIST_INIT(valid_bloodtypes, list( pref.citizenship = OO.possible_citizenships[1] pref.religion = OO.possible_religions[1] + var/datum/faction/current_faction = SSjobs.name_factions[pref.faction] + if(!current_faction?.can_select(pref, user)) + to_chat(user, SPAN_WARNING("[pref.faction] does not employ [mob_species.name_plural]. Resetting to [SSjobs.default_faction.name]!")) + pref.faction = SSjobs.default_faction.name + // Follows roughly the same way hair does above, but for gradient styles var/global/list/valid_gradients = list() diff --git a/code/modules/client/preference_setup/occupation/occupation.dm b/code/modules/client/preference_setup/occupation/occupation.dm index 8279494808f..bcbd86fcc69 100644 --- a/code/modules/client/preference_setup/occupation/occupation.dm +++ b/code/modules/client/preference_setup/occupation/occupation.dm @@ -1,6 +1,8 @@ /datum/category_item/player_setup_item/occupation name = "Occupation" sort_order = 1 + /// The module datum for the faction select interface, if it's currently open. + var/datum/tgui_module/faction_select/faction_ui /datum/category_item/player_setup_item/occupation/load_character(var/savefile/S) S["alternate_option"] >> pref.alternate_option @@ -163,7 +165,7 @@ "", "
Character Faction
", "This will influence the jobs you can select from, and the starting equipment.
", - "[pref.faction]


" + "[pref.faction]

" ) dat += list( @@ -292,17 +294,17 @@ if(SetJob(user, href_list["set_job"])) return TOPIC_REFRESH_UPDATE_PREVIEW - else if(href_list["faction_preview"]) - show_faction_menu(user, html_decode(href_list["faction_preview"])) + else if(href_list["faction_select"]) + if(!faction_ui) + faction_ui = new(src) + faction_ui.ui_interact(user) return TOPIC_NOACTION - else if(href_list["faction_select"]) - validate_and_set_faction(html_decode(href_list["faction_select"])) - show_faction_menu(user, html_decode(href_list["faction_select"])) - return TOPIC_REFRESH_UPDATE_PREVIEW - return ..() +/datum/category_item/player_setup_item/occupation/proc/on_ui_close() + faction_ui = null + /datum/category_item/player_setup_item/occupation/proc/sanitize_faction() if (!SSjobs.name_factions[pref.faction]) pref.faction = SSjobs.default_faction.name @@ -448,67 +450,10 @@ pref.player_alt_titles.Cut() -/datum/category_item/player_setup_item/occupation/proc/show_faction_menu(mob/user, selected_faction) //note : selected faction is what you choose to see, pref.faction is the actual chosen faction - simple_asset_ensure_is_sent(user, /datum/asset/simple/faction_icons) - - var/list/dat = list("

") - - var/list/factions = list() - for (var/datum/faction/faction in SSjobs.factions) - if(!faction.is_visible(user)) - continue - - if (faction.name == selected_faction) - factions += "[faction.name]" - else - factions += "[faction.name]" - - dat += factions.Join(" ") - - var/datum/faction/faction = SSjobs.name_factions[selected_faction] - if(!istype(faction)) - to_client_chat(SPAN_DANGER("Invalid faction chosen. Resetting to [SSjobs.default_faction.name].")) - selected_faction = SSjobs.default_faction.name - faction = SSjobs.name_factions[selected_faction] - return - - dat += "


" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "
[faction.description]" - dat += {""} - - dat += "
Departments:
" - dat += "[faction.departments]" - dat += "

" - - dat += "You can learn more about this faction on the wiki." - - if (selected_faction == pref.faction) - dat += "
\[Faction selected\]" - else if (faction.can_select(pref,user)) - dat += "
\[Select faction\]" - else - dat += "
[faction.get_selection_error(pref, user)]" - dat += "
" - - user << browse(HTML_SKELETON(dat.Join()), "window=factionpreview;size=750x450") - -/datum/category_item/player_setup_item/occupation/proc/validate_and_set_faction(selected_faction) - var/datum/faction/faction = SSjobs.name_factions[selected_faction] - - if (!faction) - to_client_chat(SPAN_DANGER("Invalid faction chosen. Resetting to default.")) - selected_faction = SSjobs.default_faction.name - +/datum/category_item/player_setup_item/occupation/proc/validate_and_set_faction(datum/faction/faction) ResetJobs() // How to be horribly lazy. - - pref.faction = selected_faction - - to_client_chat(SPAN_NOTICE("New faction chosen. Job preferences reset.")) + pref.faction = faction.name + to_client_chat(SPAN_NOTICE("New faction chosen! Job preferences reset.")) /datum/preferences/proc/GetPlayerAltTitle(datum/job/job) return player_alt_titles[job.title] || job.title diff --git a/code/modules/tgui/modules/faction_select.dm b/code/modules/tgui/modules/faction_select.dm new file mode 100644 index 00000000000..bcf85fab657 --- /dev/null +++ b/code/modules/tgui/modules/faction_select.dm @@ -0,0 +1,95 @@ +/datum/tgui_module/faction_select + /// The 'occupation' datum of the user viewing the interface. + var/datum/category_item/player_setup_item/occupation/occupation + /// The name of the currently viewed faction. + var/viewed_faction + +/datum/tgui_module/faction_select/New(datum/category_item/player_setup_item/occupation/occupation) + . = ..() + src.occupation = occupation + viewed_faction = occupation.pref.faction + +/datum/tgui_module/faction_select/ui_interact(mob/user, datum/tgui/ui) + . = ..() + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "FactionSelect") + ui.set_autoupdate(FALSE) + ui.open() + +/datum/tgui_module/faction_select/ui_close(mob/user) + . = ..() + occupation.on_ui_close() + +/datum/tgui_module/faction_select/ui_data(mob/user) + var/list/data = alist() + data["chosen_faction"] = occupation.pref.faction + + var/datum/faction/faction_datum = get_faction(viewed_faction, user) + // Set `viewed_faction` to the `get_faction()` result from it, since that double checks that it's actually valid. + data["viewed_faction"] = faction_datum.name + data["viewed_selection_error"] = faction_datum.get_selection_error(occupation.pref, user) + + return data + +/datum/tgui_module/faction_select/ui_static_data(mob/user) + var/list/data = alist() + + var/list/factions = list() + for(var/datum/faction/faction as anything in sort_list(SSjobs.factions, GLOBAL_PROC_REF(cmp_faction))) + if(!faction.is_visible(user)) + continue + + factions.Add(list(alist( + "name" = faction.name, + "suffix" = faction.title_suffix, + "desc" = faction.description, + "logo" = faction.get_logo_name(), + "departments" = faction.departments, + "wiki_page" = faction.wiki_page + ))) + data["factions"] = factions + data["wiki_url"] = GLOB.config.wikiurl + return data + +/datum/tgui_module/faction_select/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + + var/datum/faction/faction = get_faction(params["faction"], usr, FALSE) + + if(action == "view_faction") + viewed_faction = faction.name + return TRUE + + if(action == "choose_faction") + var/faction_error = faction.get_selection_error(occupation.pref, usr) + if(faction_error) + to_chat(usr, SPAN_DANGER("Error selecting faction!: [faction_error]")) + return FALSE + + occupation.validate_and_set_faction(faction) + usr.client.prefs.ShowChoices(usr) + usr.client.prefs.update_preview_icon() + return TRUE + + if(action == "open_wiki") + var/client/client = usr.get_client() + client?.wiki(faction.wiki_page) + return FALSE + +/datum/tgui_module/faction_select/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/simple/faction_icons) + ) + +/datum/tgui_module/faction_select/proc/get_faction(faction_name, mob/user, quiet = TRUE) + var/datum/faction/faction = SSjobs.name_factions[faction_name] + if(!istype(faction) || !faction.is_visible(user)) + if(!quiet) + to_chat(usr, SPAN_WARNING("Invalid faction chosen. Resetting to [SSjobs.default_faction.name].")) + update_static_data(user) // Update the faction list to hopefully remove anything invalid. + return SSjobs.default_faction + + return faction diff --git a/html/changelogs/SabreML-PR-faction-select-tgui.yml b/html/changelogs/SabreML-PR-faction-select-tgui.yml new file mode 100644 index 00000000000..7a9564b09c1 --- /dev/null +++ b/html/changelogs/SabreML-PR-faction-select-tgui.yml @@ -0,0 +1,14 @@ +# Your name. +author: SabreML + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit. +# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog. +changes: + - rscadd: "Revamped the 'Faction Select' UI in the occupation menu of character setup." + - bugfix: "Fixed changing species in character setup not resetting your faction if it became invalid." diff --git a/tgui/packages/tgui-dev-server/package.json b/tgui/packages/tgui-dev-server/package.json index 6cf786961b4..aba7aae098b 100644 --- a/tgui/packages/tgui-dev-server/package.json +++ b/tgui/packages/tgui-dev-server/package.json @@ -6,7 +6,7 @@ "dependencies": { "axios": "^1.12.0", "glob": "^7.1.7", - "source-map": "^0.7.3", + "source-map": "^0.7.4", "stacktrace-parser": "^0.1.10", "ws": "^7.5.10" } diff --git a/tgui/packages/tgui/interfaces/FactionSelect.tsx b/tgui/packages/tgui/interfaces/FactionSelect.tsx new file mode 100644 index 00000000000..69970334f4e --- /dev/null +++ b/tgui/packages/tgui/interfaces/FactionSelect.tsx @@ -0,0 +1,232 @@ +// code/modules/tgui/modules/faction_select.dm +import { classes } from 'common/react'; +import { resolveAsset } from '../assets'; +import { useBackend } from '../backend'; +import { Box, Button, Divider, Flex, Icon, LabeledList, Section, Stack } from '../components'; +import { Window } from '../layouts'; + +export type FactionSelectData = { + chosen_faction: string; + viewed_faction: string; + viewed_selection_error: string; + factions: Faction[]; + wiki_url: string; +}; + +export type Faction = { + name: string; + suffix: string; + desc: string; + logo: string; + departments: string[]; + wiki_page: string; +}; + +export const FactionSelect = () => { + return ( + + + + + + + + + + + + + ); +}; + +const FactionList = (props, context) => { + const { act, data } = useBackend(context); + return ( +
+ + {data.factions.map((faction) => ( + + + + ))} + +
+ ); +}; + +const FactionInfo = (props, context) => { + const { act, data } = useBackend(context); + + const currentFaction = data.factions.find( + (faction) => faction.name === data.viewed_faction + )!; // Non-null assertion for `currentFaction` since if it is null, that shouldn't be handled here. + + const FormatDescription = (text: string) => { + return text + .replace(/\n\t/g, ' ') + .replace(/
\s*/g, '\n') + .trim(); + }; + + return ( + + +
+ + {currentFaction.name} + +
+
+ + + + + + +
+ {FormatDescription(currentFaction.desc)} +
+
+ +
+ + + + + + + + + + ━━ You can learn more about this faction on the wiki:  + + + + + + {Checkmark(true)} + {/* NYI (/datum/faction/var/blacklisted_citizenship_types) */} + + + {Checkmark(!data.viewed_selection_error)} + + + + + + + ); +}; diff --git a/tgui/packages/tgui/styles/interfaces/FactionSelect.scss b/tgui/packages/tgui/styles/interfaces/FactionSelect.scss new file mode 100644 index 00000000000..ff40dba683e --- /dev/null +++ b/tgui/packages/tgui/styles/interfaces/FactionSelect.scss @@ -0,0 +1,36 @@ +@use '../components/Divider.scss'; + +// Colours based on the companies' 'Official Colours' sections on the wiki. +$company-colors: ( + 'SCC': #1966b3, + 'NT': #14498f, + 'Hepht': #aa6d18, + 'Idris': #369fa1, + 'Orion': #aa6818, + 'PMCG': #1a1a1a, + 'Zavod': #8a1414, + 'Zeng': #b56cea, + // 'Independent' doesn't get a colour :( +); + +.theme-faction_select { + .Divider { + &--vertical--dashed { + border-left: Divider.$thickness dashed Divider.$color; + } + } + + .LabeledList__content { + text-align: right; + } + + .Layout__content { + background-image: none; + } + + @each $name, $color in $company-colors { + .faction_entry--#{$name}:not(.Button--selected) { + background-image: linear-gradient(to right, transparent 65%, $color); + } + } +} diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss index 5c5b9e2777e..65226174596 100644 --- a/tgui/packages/tgui/styles/main.scss +++ b/tgui/packages/tgui/styles/main.scss @@ -23,6 +23,7 @@ @include meta.load-css('./components/BlockQuote.scss'); @include meta.load-css('./components/Button.scss'); @include meta.load-css('./components/ColorBox.scss'); +@include meta.load-css('./components/Dialog.scss'); @include meta.load-css('./components/Dimmer.scss'); @include meta.load-css('./components/Divider.scss'); @include meta.load-css('./components/Dropdown.scss'); @@ -44,12 +45,12 @@ @include meta.load-css('./components/Tabs.scss'); @include meta.load-css('./components/TextArea.scss'); @include meta.load-css('./components/Tooltip.scss'); -@include meta.load-css('./components/Dialog.scss'); // Interfaces -@include meta.load-css('./interfaces/ListInput.scss'); @include meta.load-css('./interfaces/AlertModal.scss'); @include meta.load-css('./interfaces/CrewManifest.scss'); +@include meta.load-css('./interfaces/FactionSelect.scss'); +@include meta.load-css('./interfaces/ListInput.scss'); @include meta.load-css('./interfaces/PersonalCrafting.scss'); @include meta.load-css('./interfaces/RequestManager.scss'); diff --git a/tgui/yarn.lock b/tgui/yarn.lock index c2b2af65f88..4529cc68842 100644 --- a/tgui/yarn.lock +++ b/tgui/yarn.lock @@ -9462,6 +9462,13 @@ resolve@^2.0.0-next.3: languageName: node linkType: hard +"source-map@npm:^0.7.4": + version: 0.7.6 + resolution: "source-map@npm:0.7.6" + checksum: 932f4a2390aa7100e91357d88cc272de984ad29139ac09eedfde8cc78d46da35f389065d0c5343c5d71d054a6ebd4939a8c0f2c98d5df64fe97bb8a730596c2d + languageName: node + linkType: hard + "sprintf-js@npm:~1.0.2": version: 1.0.3 resolution: "sprintf-js@npm:1.0.3" @@ -9967,7 +9974,7 @@ resolve@^2.0.0-next.3: dependencies: axios: ^1.12.0 glob: ^7.1.7 - source-map: ^0.7.3 + source-map: ^0.7.4 stacktrace-parser: ^0.1.10 ws: ^7.5.10 languageName: unknown