From c08d324f3838c491f72b5ddda02aec384baf4acf Mon Sep 17 00:00:00 2001 From: FalloutFalcon Date: Fri, 31 Jul 2026 00:55:44 -0500 Subject: [PATCH 1/6] Squashed commit of the following: commit b40c4f864092ad8cdc92a6404b5c76108b6cc865 Author: FalloutFalcon Date: Fri Jul 31 00:17:59 2026 -0500 minor mistake commit 58a4baa9be174f176c869a8a4b5ef6174cde634c Author: FalloutFalcon Date: Thu Jul 30 22:19:21 2026 -0500 yea commit de9cf3e833ba5f363612cbdf35c9c4090c1dd018 Author: FalloutFalcon Date: Thu Jul 30 20:20:27 2026 -0500 stuff commit f880094f1a94c88bed3d406b0007668b4cfdc7d1 Merge: 673029c946d1 36d9737d2d14 Author: FalloutFalcon Date: Thu Jul 30 18:40:28 2026 -0500 Merge branch 'master' of https://github.com/DarkPack13/SecondCity into whitelist-bullshit commit 673029c946d182e495c3dc3d579e4fb000f60b23 Author: FalloutFalcon Date: Sun Jul 26 01:16:50 2026 -0500 shit commit d3cb70bc25c89d7dfe70c661a18e1d54317bd219 Author: FalloutFalcon Date: Sat Jul 25 22:41:51 2026 -0500 yea commit e3aae1600d68c640eaa5cf48ff6bfe71b244fb61 Author: FalloutFalcon Date: Sat Jul 25 22:40:11 2026 -0500 renames disc editor cause it do 2 much commit b87bc7ffdf26922a1ccc3e6d70d985cb4046d16a Author: FalloutFalcon Date: Sat Jul 25 22:26:55 2026 -0500 shifts TFNadmin editeor back into darkpack one commit c318a1d77833e79bcd7382e2b1a6fff25560b46c Author: FalloutFalcon Date: Sat Jul 25 22:16:51 2026 -0500 relabel comments and moves stuff out of tfn files commit e5b5c485b349cb030589e2ab8dce0f0b3e687f13 Author: buffyuwu <42501819+buffyuwu@users.noreply.github.com> Date: Wed Apr 29 17:18:48 2026 -0400 offline save fix commit e755dc07b89315eebd8d5bf37a4a46756a024ca8 Author: buffyuwu <42501819+buffyuwu@users.noreply.github.com> Date: Wed Apr 29 07:59:12 2026 -0400 oops commit bfaaa44cac4b99c98b1044b9256f28e3adef28b9 Author: buffyuwu <42501819+buffyuwu@users.noreply.github.com> Date: Tue Apr 28 08:44:01 2026 -0400 Update whitelist.dm commit 0384a12ddc6124bb5e085275529ddacde1f05ae3 Author: buffyuwu <42501819+buffyuwu@users.noreply.github.com> Date: Tue Apr 28 08:04:31 2026 -0400 Update whitelist_savefile.dm commit 704a0768fdf5850c63068d362614b54bc47d681a Author: buffyuwu Date: Mon Apr 27 19:22:47 2026 -0400 Update whitelist_savefile.dm commit 9e7fcdf6894aecd4facf73dd4dd4c1c35085e310 Author: buffyuwu Date: Mon Apr 27 19:19:15 2026 -0400 Update whitelist_savefile.dm commit f252a76e86ed0b8578e1b4984e316082019b9172 Author: buffyuwu Date: Mon Apr 27 19:15:20 2026 -0400 gd it commit 1b2a307e5cb87c50fb5692ee63a2e2b173453a11 Author: buffyuwu Date: Sun Apr 26 11:45:53 2026 -0400 Update whitelist_savefile.dm commit e20ce12058188feee8d2b8b5e8e4a387da198cf4 Author: buffyuwu Date: Sat Apr 25 23:50:43 2026 -0400 Update TFNAdminDisciplineEditor.tsx commit 1278116694851c009688259bc27f4edda09dcca6 Author: buffyuwu Date: Sat Apr 25 23:20:46 2026 -0400 guh Co-authored-by: buffyuwu commit c1a301c85fce0c7b8856926ec1f7fd1c599467f4 Author: buffyuwu Date: Sat Apr 25 23:15:45 2026 -0400 1.0 commit c91acddd1783080281315a42807321f67fe363bd Author: buffyuwu <42501819+buffyuwu@users.noreply.github.com> Date: Tue Mar 31 18:07:50 2026 -0400 Update darkpack_height.tsx commit 797d4472662e1bab51b1fe28ccb45ee4dd69aa87 Author: buffyuwu <42501819+buffyuwu@users.noreply.github.com> Date: Tue Mar 31 18:07:37 2026 -0400 ship of theseus with a darkpark file commit 88d8bc4dafe96ea292b9057f894c7e3fdfcddf92 Author: buffyuwu <42501819+buffyuwu@users.noreply.github.com> Date: Tue Mar 31 16:37:26 2026 -0400 it should be its own component commit 9de6351eb0e8c9793d45f4d8cf93bb9f7fd2ad7c Author: buffyuwu Date: Sat Apr 25 22:57:05 2026 -0400 this was a dumb idea commit 46e31ba64f5511059d54477085d512ff37afb60c Author: buffyuwu Date: Sat Apr 25 20:06:09 2026 -0400 the whitelistening commit 67dc5443c9f7845c8aec9a417770e5575c8e4254 Author: Biplume Date: Wed Apr 15 19:32:30 2026 -0400 Whitelists & Unwhitelists Bloodlines (#199) * whitelists * free nagaraja * Update tfn_trusted_whitelist.tsx commit b64efa91f81921e81a6a0635a9eecddc9fca17c0 Author: XeonMations <62395746+XeonMations@users.noreply.github.com> Date: Tue Mar 31 18:35:01 2026 +0300 Fixes for #116 commit 299d28c102a76dbfe95a44ad261294e0d7ecaf05 Author: XeonMations <62395746+XeonMations@users.noreply.github.com> Date: Tue Mar 31 18:29:00 2026 +0300 Update trusted_whitelist.dm commit 9e771545e13799c44346ea67ab8987a81dbfdf10 Author: buffyuwu Date: Sat Apr 25 22:57:05 2026 -0400 this was a dumb idea commit 91e639c441565448d7e5a3f9cb7546ef06909797 Author: buffyuwu Date: Sat Apr 25 20:06:09 2026 -0400 the whitelistening commit 8c945e35290784cc1b0e76e2297cb9a2f96912a6 Author: buffyuwu Date: Fri Apr 10 18:05:02 2026 -0400 admin disc menu redesign, save_character on matrix, clear discs on new char (#145) * revert to upstream * admin disc menu redesign, save_character on matrix, clear discs on new char * missing comment Co-authored-by: buffyuwu * Update modular_darkpack/modules/powers/code/discipline_pref_middleware.dm Co-authored-by: XeonMations <62395746+XeonMations@users.noreply.github.com> * Update modular_darkpack/modules/powers/code/admin_discipline_editor.dm Co-authored-by: XeonMations <62395746+XeonMations@users.noreply.github.com> * Update modular_darkpack/modules/powers/code/admin_discipline_editor.dm Co-authored-by: XeonMations <62395746+XeonMations@users.noreply.github.com> * Update preferences.dm * Revert "Update preferences.dm" This reverts commit ef827ce8dee2dd6db161d6f37a824046ed6444f8. * Update TFNAdminDisciplineEditor.tsx * sfgsdfhgfdah * e --------- Co-authored-by: XeonMations <62395746+XeonMations@users.noreply.github.com> commit c6809fb852d471d4e103317173d7d71dc9992a1a Author: buffyuwu Date: Mon Mar 30 17:53:29 2026 -0400 Trusted whitelist (#105) * trusted whitelist maybe * admin abuse --- code/__DEFINES/~darkpack/jobs.dm | 12 +- code/__DEFINES/~darkpack/whitelist.dm | 17 + code/modules/client/preferences_savefile.dm | 1 + code/modules/jobs/job_exp.dm | 4 + config/darkpack_config.txt | 3 + .../modules/jobs/code/_departments.dm | 4 +- modular_darkpack/modules/jobs/code/_jobs.dm | 5 + .../modules/jobs/code/anarchs/baron.dm | 3 + .../modules/jobs/code/camarilla/harpy.dm | 6 +- .../modules/jobs/code/camarilla/hound.dm | 6 +- .../code/camarilla/primogens/banu_haqim.dm | 4 +- .../jobs/code/camarilla/primogens/lasombra.dm | 4 +- .../code/camarilla/primogens/malkavian.dm | 4 +- .../code/camarilla/primogens/nosferatu.dm | 4 +- .../jobs/code/camarilla/primogens/toreador.dm | 4 +- .../jobs/code/camarilla/primogens/ventrue.dm | 4 +- .../modules/jobs/code/camarilla/prince.dm | 4 +- .../modules/jobs/code/camarilla/seneschal.dm | 4 +- .../modules/jobs/code/camarilla/sheriff.dm | 4 +- .../jobs/code/camarilla/tower_employee.dm | 1 - .../modules/jobs/code/clinic/director.dm | 5 +- .../modules/jobs/code/clinic/doctor.dm | 2 +- .../modules/jobs/code/garou/councillor.dm | 4 +- .../modules/jobs/code/garou/guardian.dm | 3 - .../modules/jobs/code/garou/truthcatcher.dm | 4 +- .../modules/jobs/code/garou/warder.dm | 4 +- .../modules/jobs/code/garou/wyrmfoe.dm | 4 +- .../modules/jobs/code/giovanni/capo.dm | 4 +- .../modules/jobs/code/hunters/abbe.dm | 2 + .../jobs/code/miscelllaneous/citizen.dm | 1 - .../jobs/code/miscelllaneous/red_news.dm | 1 - .../modules/jobs/code/pentex/affairs.dm | 4 +- .../modules/jobs/code/pentex/branch_lead.dm | 4 +- .../modules/jobs/code/pentex/employee.dm | 3 - .../modules/jobs/code/pentex/executive.dm | 4 +- .../modules/jobs/code/pentex/sec.dm | 6 +- .../modules/jobs/code/pentex/secchief.dm | 4 +- .../modules/jobs/code/police/captain.dm | 2 + .../modules/jobs/code/police/fbi.dm | 1 - .../modules/jobs/code/police/police.dm | 2 +- .../modules/jobs/code/police/sergeant.dm | 3 +- .../modules/jobs/code/sabbat/ductus.dm | 2 +- .../modules/jobs/code/sabbat/pack.dm | 2 +- .../modules/jobs/code/sabbat/priest.dm | 4 + .../modules/jobs/code/supply/dealer.dm | 4 +- .../modules/jobs/code/supply/supply_tech.dm | 3 +- .../modules/jobs/code/tremere/archivist.dm | 2 +- .../modules/jobs/code/tremere/gargoyle.dm | 2 +- .../modules/jobs/code/tremere/regent.dm | 2 + .../modules/matrix/code/matrix.dm | 1 + ...e_editor.dm => admin_preference_editor.dm} | 64 ++- .../powers/code/discipline_pref_middleware.dm | 21 +- .../modules/splats/code/splat_configs.dm | 20 + .../code/vampire_clan/trusted_whitelist.dm | 64 +++ .../whitelist/code/whitelist_enforcement.dm | 42 ++ .../modules/whitelist/code/whitelist_prefs.dm | 80 +++ .../whitelist/code/whitelist_savefile.dm | 22 + tgstation.dme | 7 +- .../tgui/interfaces/AdminDisciplineEditor.tsx | 487 ---------------- .../tgui/interfaces/AdminPreferenceEditor.tsx | 519 ++++++++++++++++++ .../CharacterPreferences/MainPage.tsx | 103 ++-- .../CharacterPreferences/SplatsPage.tsx | 48 +- .../components/ConfirmModal.tsx | 62 +++ .../character_preferences/darkpack_height.tsx | 61 +- .../darkpack_trusted_whitelist.tsx | 100 ++++ .../vtm_clan_feature.tsx | 3 + .../tgui/interfaces/PreferencesMenu/types.ts | 1 + 67 files changed, 1225 insertions(+), 666 deletions(-) create mode 100644 code/__DEFINES/~darkpack/whitelist.dm rename modular_darkpack/modules/powers/code/{admin_discipline_editor.dm => admin_preference_editor.dm} (75%) create mode 100644 modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/trusted_whitelist.dm create mode 100644 modular_darkpack/modules/whitelist/code/whitelist_enforcement.dm create mode 100644 modular_darkpack/modules/whitelist/code/whitelist_prefs.dm create mode 100644 modular_darkpack/modules/whitelist/code/whitelist_savefile.dm delete mode 100644 tgui/packages/tgui/interfaces/AdminDisciplineEditor.tsx create mode 100644 tgui/packages/tgui/interfaces/AdminPreferenceEditor.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/components/ConfirmModal.tsx create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/darkpack_trusted_whitelist.tsx diff --git a/code/__DEFINES/~darkpack/jobs.dm b/code/__DEFINES/~darkpack/jobs.dm index dc0a0e29b8cb..88a064fb29b8 100644 --- a/code/__DEFINES/~darkpack/jobs.dm +++ b/code/__DEFINES/~darkpack/jobs.dm @@ -269,7 +269,7 @@ DEFINE_BITFIELD(departments_bitflags, list( // Playtime tracking system, see jobs_exp.dm // weird one. -#define EXP_TYPE_KINDRED "Kindred" +#define EXP_TYPE_CITIZEN "Citizen" #define EXP_TYPE_GAIA "Garou Nation" #define EXP_TYPE_SPIRAL "Pentex" #define EXP_TYPE_CAMARILLA "Camarilla Authorities" @@ -277,14 +277,16 @@ DEFINE_BITFIELD(departments_bitflags, list( #define EXP_TYPE_CHANTRY "Chantry" #define EXP_TYPE_ANARCH "Anarchs" #define EXP_TYPE_SABBAT "Sabbat" -#define EXP_TYPE_COUNCIL "Camarilla Primogen Council" #define EXP_TYPE_POLICE "Police Force" -#define EXP_TYPE_NATIONAL_SECURITY "National Security" #define EXP_TYPE_SERVICES "City Services" -// this prob should just say clinic genericly. -#define EXP_TYPE_CLINIC "St. John's Clinic" +#define EXP_TYPE_CLINIC "Clinic" #define EXP_TYPE_GIOVANNI "Giovanni Family" #define EXP_TYPE_MANOR "Voivodate" // APOC EDIT CHANGE - (Voivode) #define EXP_TYPE_CHURCH "Church" #define EXP_TYPE_CLUB "Strip Club" +#define EXP_TYPE_SOCIETY_OF_LEOPOLD "Society of Leopold" #define EXP_TYPE_OCEAN "Ocean 76" // APOC EDIT ADD - JOBS - Ocean 76 + +#define EXP_REQ_CRITICAL 720 +#define EXP_REQ_HEAD 360 +#define EXP_REQ_MINOR 180 diff --git a/code/__DEFINES/~darkpack/whitelist.dm b/code/__DEFINES/~darkpack/whitelist.dm new file mode 100644 index 000000000000..19172bfa7c5a --- /dev/null +++ b/code/__DEFINES/~darkpack/whitelist.dm @@ -0,0 +1,17 @@ +#define WHITELIST_TRUSTED "trusted" +#define WHITELIST_TIMELIMITS "timelimits" + +GLOBAL_LIST_INIT(default_player_whitelists, default_player_whitelists()) + +/proc/default_player_whitelists() + var/list/defs = list(SPLAT_NONE) + + for(var/splat_id in get_selectable_splats()) + var/splat_type = GLOB.splat_list[splat_id] + var/datum/splat/splat = GLOB.splat_prototypes[splat_type] + + if(!splat.check_whitelist_requirement()) + defs += splat_id + + return defs + diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 230da79e6db0..275e89daf94a 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -549,6 +549,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car stack_trace("remove_current_slot() being called when there are no slots to go to, the client should prevent this") return + discipline_levels = list() // TFN EDIT ADD - remove disciplines when removing a character slot savefile.remove_entry("character[default_slot]") tainted_character_profiles = TRUE switch_to_slot(closest_slot) diff --git a/code/modules/jobs/job_exp.dm b/code/modules/jobs/job_exp.dm index 2dfe36b6ed9a..7dd6d068ff2a 100644 --- a/code/modules/jobs/job_exp.dm +++ b/code/modules/jobs/job_exp.dm @@ -12,6 +12,10 @@ GLOBAL_PROTECT(exp_to_update) return 0 if(!IS_XP_LOCKED(src)) return 0 + // DARKPACK EDIT ADD START - (whitelist) + if(C.prefs?.has_whitelist(WHITELIST_TIMELIMITS)) + return 0 + // DARKPACK EDIT ADD END if(CONFIG_GET(flag/use_exp_restrictions_admin_bypass) && check_rights_for(C,R_ADMIN)) return 0 var/isexempt = C.prefs.db_flags & DB_FLAG_EXEMPT diff --git a/config/darkpack_config.txt b/config/darkpack_config.txt index e7839be05960..f324af16be87 100644 --- a/config/darkpack_config.txt +++ b/config/darkpack_config.txt @@ -51,6 +51,9 @@ ROUNDSTART_SPLATS splat_kinfolk ROUNDSTART_SPLATS splat_garou ROUNDSTART_SPLATS splat_corax +#WHITELISTED_SPLATS splat_garou +#WHITELISTED_SPLATS splat_corax + ## If dead people and ghosts can LOOC to people who are alive. DISABLE_GHOST_LOOC diff --git a/modular_darkpack/modules/jobs/code/_departments.dm b/modular_darkpack/modules/jobs/code/_departments.dm index 55420e018485..58c63b743323 100644 --- a/modular_darkpack/modules/jobs/code/_departments.dm +++ b/modular_darkpack/modules/jobs/code/_departments.dm @@ -126,6 +126,7 @@ /datum/job_department/city_services department_name = DEPARTMENT_CITY_SERVICES department_bitflags = DEPARTMENT_BITFLAG_CITY_SERVICES + department_experience_type = EXP_TYPE_SERVICES display_order = 14 // give its own ui color? @@ -134,8 +135,7 @@ department_name = DEPARTMENT_SOCIETY_OF_LEOPOLD department_bitflags = DEPARTMENT_BITFLAG_SOCIETY_OF_LEOPOLD department_head = /datum/job/vampire/abbe - // why are you reusing the church xp. - department_experience_type = EXP_TYPE_CHURCH + department_experience_type = EXP_TYPE_SOCIETY_OF_LEOPOLD display_order = 15 ui_color = "#fff022" diff --git a/modular_darkpack/modules/jobs/code/_jobs.dm b/modular_darkpack/modules/jobs/code/_jobs.dm index f31c0912168a..fe75404e40fc 100644 --- a/modular_darkpack/modules/jobs/code/_jobs.dm +++ b/modular_darkpack/modules/jobs/code/_jobs.dm @@ -108,6 +108,11 @@ ((guestbook_flags & GUESTBOOK_DEPARTMENT) && length(common_departments))) spawned.mind.guestbook.add_guest(spawned, player_mob, player_mob.mind.name, player_mob.mind.name, silent = TRUE) +/datum/job/vampire + abstract_type = /datum/job/vampire + exp_required_type = EXP_TYPE_CITIZEN + exp_granted_type = EXP_TYPE_CITIZEN + /** * This type is used to indicate a lack of a job. * The mind variable assigned_role will point here by default. diff --git a/modular_darkpack/modules/jobs/code/anarchs/baron.dm b/modular_darkpack/modules/jobs/code/anarchs/baron.dm index ca815fab1478..19efda1c8d1b 100644 --- a/modular_darkpack/modules/jobs/code/anarchs/baron.dm +++ b/modular_darkpack/modules/jobs/code/anarchs/baron.dm @@ -13,6 +13,9 @@ /datum/job_department/anarch, ) + exp_requirements = EXP_REQ_HEAD + exp_required_type_department = EXP_TYPE_ANARCH + known_contacts = list("Bouncer", "Emissary", "Sweeper", "Prince", "Sheriff") allowed_clans = list(VAMPIRE_CLAN_DAUGHTERS_OF_CACOPHONY, VAMPIRE_CLAN_BAALI, VAMPIRE_CLAN_BANU_HAQIM, VAMPIRE_CLAN_CAITIFF, VAMPIRE_CLAN_TRUE_BRUJAH, VAMPIRE_CLAN_BRUJAH, VAMPIRE_CLAN_NOSFERATU, VAMPIRE_CLAN_GANGREL, VAMPIRE_CLAN_CITY_GANGREL, VAMPIRE_CLAN_TOREADOR, VAMPIRE_CLAN_MALKAVIAN, VAMPIRE_CLAN_VENTRUE, VAMPIRE_CLAN_LASOMBRA, VAMPIRE_CLAN_GARGOYLE, VAMPIRE_CLAN_SETITE, VAMPIRE_CLAN_SAMEDI, VAMPIRE_CLAN_NAGARAJA) allowed_splats = list(SPLAT_KINDRED) diff --git a/modular_darkpack/modules/jobs/code/camarilla/harpy.dm b/modular_darkpack/modules/jobs/code/camarilla/harpy.dm index dc9c5d7c338d..995ff8c0784f 100644 --- a/modular_darkpack/modules/jobs/code/camarilla/harpy.dm +++ b/modular_darkpack/modules/jobs/code/camarilla/harpy.dm @@ -9,10 +9,10 @@ config_tag = "HARPY" req_admin_notify = 1 minimal_player_age = 10 - exp_requirements = 180 - exp_required_type = EXP_TYPE_CAMARILLA + + exp_requirements = EXP_REQ_MINOR exp_required_type_department = EXP_TYPE_CAMARILLA - exp_granted_type = EXP_TYPE_CAMARILLA + job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/harpy diff --git a/modular_darkpack/modules/jobs/code/camarilla/hound.dm b/modular_darkpack/modules/jobs/code/camarilla/hound.dm index 0f980d846e77..34f881bebd39 100644 --- a/modular_darkpack/modules/jobs/code/camarilla/hound.dm +++ b/modular_darkpack/modules/jobs/code/camarilla/hound.dm @@ -7,14 +7,12 @@ spawn_positions = 7 supervisors = SUPERVISOR_SHERIFF minimal_player_age = 7 - exp_requirements = 20 - exp_required_type = EXP_TYPE_CAMARILLA - exp_required_type_department = EXP_TYPE_CAMARILLA - exp_granted_type = EXP_TYPE_CAMARILLA config_tag = "HOUND" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/hound + exp_required_type_department = EXP_TYPE_CAMARILLA + display_order = JOB_DISPLAY_ORDER_HOUND departments_list = list( /datum/job_department/camarilla, diff --git a/modular_darkpack/modules/jobs/code/camarilla/primogens/banu_haqim.dm b/modular_darkpack/modules/jobs/code/camarilla/primogens/banu_haqim.dm index 4816a28c0b25..67d63d0c92b0 100644 --- a/modular_darkpack/modules/jobs/code/camarilla/primogens/banu_haqim.dm +++ b/modular_darkpack/modules/jobs/code/camarilla/primogens/banu_haqim.dm @@ -8,10 +8,8 @@ supervisors = SUPERVISOR_TRADITIONS req_admin_notify = 1 minimal_player_age = 14 - exp_requirements = 180 - exp_required_type = EXP_TYPE_CAMARILLA + exp_requirements = EXP_REQ_HEAD exp_required_type_department = EXP_TYPE_CAMARILLA - exp_granted_type = EXP_TYPE_CAMARILLA config_tag = "PRIMOGEN_BANU_HAQIM" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/banuprim diff --git a/modular_darkpack/modules/jobs/code/camarilla/primogens/lasombra.dm b/modular_darkpack/modules/jobs/code/camarilla/primogens/lasombra.dm index d5090b7b1ad3..223b90793d5e 100644 --- a/modular_darkpack/modules/jobs/code/camarilla/primogens/lasombra.dm +++ b/modular_darkpack/modules/jobs/code/camarilla/primogens/lasombra.dm @@ -8,10 +8,8 @@ supervisors = SUPERVISOR_TRADITIONS req_admin_notify = 1 minimal_player_age = 14 - exp_requirements = 180 - exp_required_type = EXP_TYPE_CAMARILLA + exp_requirements = EXP_REQ_HEAD exp_required_type_department = EXP_TYPE_CAMARILLA - exp_granted_type = EXP_TYPE_CAMARILLA config_tag = "PRIMOGEN_LASOMBRA" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/lasombraprim diff --git a/modular_darkpack/modules/jobs/code/camarilla/primogens/malkavian.dm b/modular_darkpack/modules/jobs/code/camarilla/primogens/malkavian.dm index 174c2c996329..65fa9c65f769 100644 --- a/modular_darkpack/modules/jobs/code/camarilla/primogens/malkavian.dm +++ b/modular_darkpack/modules/jobs/code/camarilla/primogens/malkavian.dm @@ -8,10 +8,8 @@ supervisors = SUPERVISOR_TRADITIONS req_admin_notify = 1 minimal_player_age = 14 - exp_requirements = 180 - exp_required_type = EXP_TYPE_CAMARILLA + exp_requirements = EXP_REQ_HEAD exp_required_type_department = EXP_TYPE_CAMARILLA - exp_granted_type = EXP_TYPE_CAMARILLA config_tag = "PRIMOGEN_MALKAVIAN" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/malkav diff --git a/modular_darkpack/modules/jobs/code/camarilla/primogens/nosferatu.dm b/modular_darkpack/modules/jobs/code/camarilla/primogens/nosferatu.dm index 9f5d30d84860..685565a4b9b2 100644 --- a/modular_darkpack/modules/jobs/code/camarilla/primogens/nosferatu.dm +++ b/modular_darkpack/modules/jobs/code/camarilla/primogens/nosferatu.dm @@ -8,10 +8,8 @@ supervisors = SUPERVISOR_TRADITIONS req_admin_notify = 1 minimal_player_age = 14 - exp_requirements = 180 - exp_required_type = EXP_TYPE_CAMARILLA + exp_requirements = EXP_REQ_HEAD exp_required_type_department = EXP_TYPE_CAMARILLA - exp_granted_type = EXP_TYPE_CAMARILLA config_tag = "PRIMOGEN_NOSFERATU" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/nosferatu diff --git a/modular_darkpack/modules/jobs/code/camarilla/primogens/toreador.dm b/modular_darkpack/modules/jobs/code/camarilla/primogens/toreador.dm index b9ccff0ea6e2..69d2eb1a4aa3 100644 --- a/modular_darkpack/modules/jobs/code/camarilla/primogens/toreador.dm +++ b/modular_darkpack/modules/jobs/code/camarilla/primogens/toreador.dm @@ -8,10 +8,8 @@ supervisors = SUPERVISOR_TRADITIONS req_admin_notify = 1 minimal_player_age = 14 - exp_requirements = 180 - exp_required_type = EXP_TYPE_CAMARILLA + exp_requirements = EXP_REQ_HEAD exp_required_type_department = EXP_TYPE_CAMARILLA - exp_granted_type = EXP_TYPE_CAMARILLA config_tag = "PRIMOGEN_TOREADOR" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/toreador diff --git a/modular_darkpack/modules/jobs/code/camarilla/primogens/ventrue.dm b/modular_darkpack/modules/jobs/code/camarilla/primogens/ventrue.dm index 2a4ae75592e8..f2fd46850f0d 100644 --- a/modular_darkpack/modules/jobs/code/camarilla/primogens/ventrue.dm +++ b/modular_darkpack/modules/jobs/code/camarilla/primogens/ventrue.dm @@ -8,10 +8,8 @@ supervisors = SUPERVISOR_TRADITIONS req_admin_notify = 1 minimal_player_age = 14 - exp_requirements = 180 - exp_required_type = EXP_TYPE_CAMARILLA + exp_requirements = EXP_REQ_HEAD exp_required_type_department = EXP_TYPE_CAMARILLA - exp_granted_type = EXP_TYPE_CAMARILLA config_tag = "PRIMOGEN_VENTRUE" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/ventrue diff --git a/modular_darkpack/modules/jobs/code/camarilla/prince.dm b/modular_darkpack/modules/jobs/code/camarilla/prince.dm index e86b3edde072..2ff3bd873014 100644 --- a/modular_darkpack/modules/jobs/code/camarilla/prince.dm +++ b/modular_darkpack/modules/jobs/code/camarilla/prince.dm @@ -8,10 +8,8 @@ supervisors = SUPERVISOR_TRADITIONS req_admin_notify = 1 minimal_player_age = 14 - exp_requirements = 180 - exp_required_type = EXP_TYPE_CAMARILLA + exp_requirements = EXP_REQ_CRITICAL exp_required_type_department = EXP_TYPE_CAMARILLA - exp_granted_type = EXP_TYPE_CAMARILLA config_tag = "PRINCE" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/prince diff --git a/modular_darkpack/modules/jobs/code/camarilla/seneschal.dm b/modular_darkpack/modules/jobs/code/camarilla/seneschal.dm index b4d93e481a52..913439df16f9 100644 --- a/modular_darkpack/modules/jobs/code/camarilla/seneschal.dm +++ b/modular_darkpack/modules/jobs/code/camarilla/seneschal.dm @@ -8,10 +8,8 @@ supervisors = SUPERVISOR_PRINCE req_admin_notify = 1 minimal_player_age = 10 - exp_requirements = 180 - exp_required_type = EXP_TYPE_CAMARILLA + exp_requirements = EXP_REQ_MINOR exp_required_type_department = EXP_TYPE_CAMARILLA - exp_granted_type = EXP_TYPE_CAMARILLA config_tag = "SENESCHAL" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/clerk diff --git a/modular_darkpack/modules/jobs/code/camarilla/sheriff.dm b/modular_darkpack/modules/jobs/code/camarilla/sheriff.dm index ad4bd4741b58..dff2b0f29964 100644 --- a/modular_darkpack/modules/jobs/code/camarilla/sheriff.dm +++ b/modular_darkpack/modules/jobs/code/camarilla/sheriff.dm @@ -8,10 +8,8 @@ supervisors = SUPERVISOR_PRINCE req_admin_notify = 1 minimal_player_age = 14 - exp_requirements = 120 - exp_required_type = EXP_TYPE_CAMARILLA + exp_requirements = EXP_REQ_MINOR exp_required_type_department = EXP_TYPE_CAMARILLA - exp_granted_type = EXP_TYPE_CAMARILLA config_tag = "SHERIFF" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/sheriff diff --git a/modular_darkpack/modules/jobs/code/camarilla/tower_employee.dm b/modular_darkpack/modules/jobs/code/camarilla/tower_employee.dm index 30112046d41e..11d03330c983 100644 --- a/modular_darkpack/modules/jobs/code/camarilla/tower_employee.dm +++ b/modular_darkpack/modules/jobs/code/camarilla/tower_employee.dm @@ -4,7 +4,6 @@ total_positions = 4 spawn_positions = 4 supervisors = SUPERVISOR_SENESCHAL_PUBLIC - exp_granted_type = EXP_TYPE_CAMARILLA config_tag = "TOWER_EMPLOYEE" outfit = /datum/outfit/job/vampire/towerwork job_flags = CITY_JOB_FLAGS diff --git a/modular_darkpack/modules/jobs/code/clinic/director.dm b/modular_darkpack/modules/jobs/code/clinic/director.dm index 4229413981c9..64b177dd80a9 100644 --- a/modular_darkpack/modules/jobs/code/clinic/director.dm +++ b/modular_darkpack/modules/jobs/code/clinic/director.dm @@ -4,10 +4,13 @@ total_positions = 1 spawn_positions = 1 supervisors = "the Hospital Administrator" - exp_required_type_department = EXP_TYPE_CLINIC config_tag = "CLINIC_DIRECTOR" outfit = /datum/outfit/job/vampire/clinic_director job_flags = CITY_JOB_FLAGS + + exp_required_type_department = EXP_TYPE_CLINIC + exp_requirements = EXP_REQ_HEAD + display_order = JOB_DISPLAY_ORDER_CLINICS_DIRECTOR departments_list = list( /datum/job_department/clinic, diff --git a/modular_darkpack/modules/jobs/code/clinic/doctor.dm b/modular_darkpack/modules/jobs/code/clinic/doctor.dm index 7f1c0e083e9c..a523074d1916 100644 --- a/modular_darkpack/modules/jobs/code/clinic/doctor.dm +++ b/modular_darkpack/modules/jobs/code/clinic/doctor.dm @@ -4,7 +4,7 @@ total_positions = 4 spawn_positions = 4 supervisors = "the Clinic Director" - exp_required_type_department = EXP_TYPE_CLINIC + // exp_required_type_department = EXP_TYPE_CLINIC config_tag = "DOCTOR" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/clinic_doctor diff --git a/modular_darkpack/modules/jobs/code/garou/councillor.dm b/modular_darkpack/modules/jobs/code/garou/councillor.dm index 9bfc9dea083f..8c62ceb181dc 100644 --- a/modular_darkpack/modules/jobs/code/garou/councillor.dm +++ b/modular_darkpack/modules/jobs/code/garou/councillor.dm @@ -8,10 +8,8 @@ supervisors = SUPERVISOR_LITANY req_admin_notify = 1 minimal_player_age = 25 - exp_requirements = 180 - exp_required_type = EXP_TYPE_GAIA + exp_requirements = EXP_REQ_HEAD exp_required_type_department = EXP_TYPE_GAIA - exp_granted_type = EXP_TYPE_GAIA config_tag = "COUNCILLOR" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/councillor diff --git a/modular_darkpack/modules/jobs/code/garou/guardian.dm b/modular_darkpack/modules/jobs/code/garou/guardian.dm index 23b2d8c18175..31ce42d50368 100644 --- a/modular_darkpack/modules/jobs/code/garou/guardian.dm +++ b/modular_darkpack/modules/jobs/code/garou/guardian.dm @@ -8,10 +8,7 @@ supervisors = /datum/job/vampire/warder req_admin_notify = 1 minimal_player_age = 25 - exp_requirements = 50 - exp_required_type = EXP_TYPE_GAIA exp_required_type_department = EXP_TYPE_GAIA - exp_granted_type = EXP_TYPE_GAIA config_tag = "GUARDIAN" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/guardian diff --git a/modular_darkpack/modules/jobs/code/garou/truthcatcher.dm b/modular_darkpack/modules/jobs/code/garou/truthcatcher.dm index c0f610d7e326..724c246309ce 100644 --- a/modular_darkpack/modules/jobs/code/garou/truthcatcher.dm +++ b/modular_darkpack/modules/jobs/code/garou/truthcatcher.dm @@ -8,10 +8,8 @@ supervisors = SUPERVISOR_LITANY req_admin_notify = 1 minimal_player_age = 25 - exp_requirements = 100 - exp_required_type = EXP_TYPE_GAIA + exp_requirements = EXP_REQ_MINOR exp_required_type_department = EXP_TYPE_GAIA - exp_granted_type = EXP_TYPE_GAIA config_tag = "TRUTHCATCHER" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/trutchcatcher diff --git a/modular_darkpack/modules/jobs/code/garou/warder.dm b/modular_darkpack/modules/jobs/code/garou/warder.dm index 5d991a16154f..68160f297695 100644 --- a/modular_darkpack/modules/jobs/code/garou/warder.dm +++ b/modular_darkpack/modules/jobs/code/garou/warder.dm @@ -8,10 +8,8 @@ supervisors = SUPERVISOR_LITANY req_admin_notify = 1 minimal_player_age = 25 - exp_requirements = 100 - exp_required_type = EXP_TYPE_GAIA + exp_requirements = EXP_REQ_MINOR exp_required_type_department = EXP_TYPE_GAIA - exp_granted_type = EXP_TYPE_GAIA config_tag = "WARDER" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/warder diff --git a/modular_darkpack/modules/jobs/code/garou/wyrmfoe.dm b/modular_darkpack/modules/jobs/code/garou/wyrmfoe.dm index 30091dc9cb7d..6f339bb2416e 100644 --- a/modular_darkpack/modules/jobs/code/garou/wyrmfoe.dm +++ b/modular_darkpack/modules/jobs/code/garou/wyrmfoe.dm @@ -8,10 +8,8 @@ supervisors = SUPERVISOR_LITANY req_admin_notify = 1 minimal_player_age = 25 - exp_requirements = 100 - exp_required_type = EXP_TYPE_GAIA + exp_requirements = EXP_REQ_MINOR exp_required_type_department = EXP_TYPE_GAIA - exp_granted_type = EXP_TYPE_GAIA config_tag = "WYRMFOE" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/wyrmfoe diff --git a/modular_darkpack/modules/jobs/code/giovanni/capo.dm b/modular_darkpack/modules/jobs/code/giovanni/capo.dm index 589e2881f17d..ada208f2e5b8 100644 --- a/modular_darkpack/modules/jobs/code/giovanni/capo.dm +++ b/modular_darkpack/modules/jobs/code/giovanni/capo.dm @@ -8,11 +8,13 @@ outfit = /datum/outfit/job/vampire/capo job_flags = CITY_JOB_FLAGS display_order = 1 - exp_required_type_department = EXP_TYPE_GIOVANNI departments_list = list( /datum/job_department/giovanni, ) + exp_required_type_department = EXP_TYPE_GIOVANNI + exp_requirements = EXP_REQ_HEAD + description = "Pure blood runs through your veins and, with it, old power. Throughout your long life you have learnt to hold onto two things and never let go: money, and family." minimum_masquerade = 0 allowed_splats = list(SPLAT_KINDRED) diff --git a/modular_darkpack/modules/jobs/code/hunters/abbe.dm b/modular_darkpack/modules/jobs/code/hunters/abbe.dm index f7c8c7a3d98d..173b31344210 100644 --- a/modular_darkpack/modules/jobs/code/hunters/abbe.dm +++ b/modular_darkpack/modules/jobs/code/hunters/abbe.dm @@ -11,6 +11,8 @@ job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/abbe + exp_requirements = EXP_REQ_HEAD + display_order = JOB_DISPLAY_ORDER_ABBE departments_list = list( /datum/job_department/society_of_leopold, diff --git a/modular_darkpack/modules/jobs/code/miscelllaneous/citizen.dm b/modular_darkpack/modules/jobs/code/miscelllaneous/citizen.dm index 1eecac4916c9..b4a0832b0700 100644 --- a/modular_darkpack/modules/jobs/code/miscelllaneous/citizen.dm +++ b/modular_darkpack/modules/jobs/code/miscelllaneous/citizen.dm @@ -5,7 +5,6 @@ total_positions = -1 spawn_positions = -1 outfit = /datum/outfit/job/vampire/citizen - exp_granted_type = EXP_TYPE_KINDRED config_tag = "CITIZEN" display_order = JOB_DISPLAY_ORDER_CITIZEN departments_list = list( diff --git a/modular_darkpack/modules/jobs/code/miscelllaneous/red_news.dm b/modular_darkpack/modules/jobs/code/miscelllaneous/red_news.dm index 434bbe972bc3..5bc7c67718c4 100644 --- a/modular_darkpack/modules/jobs/code/miscelllaneous/red_news.dm +++ b/modular_darkpack/modules/jobs/code/miscelllaneous/red_news.dm @@ -5,7 +5,6 @@ total_positions = 2 spawn_positions = 2 outfit = /datum/outfit/job/vampire/red_news_reporter - exp_granted_type = EXP_TYPE_SPIRAL config_tag = "RED_NEWS_REPORTER" display_order = JOB_DISPLAY_ORDER_RED_NEWS_REPORTER departments_list = list( diff --git a/modular_darkpack/modules/jobs/code/pentex/affairs.dm b/modular_darkpack/modules/jobs/code/pentex/affairs.dm index eb7cbfb4dffc..853354471ac6 100644 --- a/modular_darkpack/modules/jobs/code/pentex/affairs.dm +++ b/modular_darkpack/modules/jobs/code/pentex/affairs.dm @@ -8,10 +8,8 @@ supervisors = "the Board and the Branch Lead" req_admin_notify = 1 minimal_player_age = 25 - exp_requirements = 150 - exp_required_type = EXP_TYPE_SPIRAL + exp_requirements = EXP_REQ_MINOR exp_required_type_department = EXP_TYPE_SPIRAL - exp_granted_type = EXP_TYPE_SPIRAL config_tag = "PENTEX_AFFAIRS" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/affairs diff --git a/modular_darkpack/modules/jobs/code/pentex/branch_lead.dm b/modular_darkpack/modules/jobs/code/pentex/branch_lead.dm index 7a56cc10c5c8..ed117ef12ceb 100644 --- a/modular_darkpack/modules/jobs/code/pentex/branch_lead.dm +++ b/modular_darkpack/modules/jobs/code/pentex/branch_lead.dm @@ -8,10 +8,8 @@ supervisors = "the Board" req_admin_notify = 1 minimal_player_age = 25 - exp_requirements = 180 - exp_required_type = EXP_TYPE_SPIRAL + exp_requirements = EXP_REQ_HEAD exp_required_type_department = EXP_TYPE_SPIRAL - exp_granted_type = EXP_TYPE_SPIRAL config_tag = "PENTEX_BRANCH_LEAD" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/branch_lead diff --git a/modular_darkpack/modules/jobs/code/pentex/employee.dm b/modular_darkpack/modules/jobs/code/pentex/employee.dm index c0cd69ff0cf8..9d6045c14447 100644 --- a/modular_darkpack/modules/jobs/code/pentex/employee.dm +++ b/modular_darkpack/modules/jobs/code/pentex/employee.dm @@ -8,10 +8,7 @@ supervisors = "the Board and the Branch Lead" req_admin_notify = 1 minimal_player_age = 25 - exp_requirements = 50 - exp_required_type = EXP_TYPE_SPIRAL exp_required_type_department = EXP_TYPE_SPIRAL - exp_granted_type = EXP_TYPE_SPIRAL config_tag = "PENTEX_EMPLOYEE" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/employee diff --git a/modular_darkpack/modules/jobs/code/pentex/executive.dm b/modular_darkpack/modules/jobs/code/pentex/executive.dm index 6f4cfc3bdca8..98c2578c3036 100644 --- a/modular_darkpack/modules/jobs/code/pentex/executive.dm +++ b/modular_darkpack/modules/jobs/code/pentex/executive.dm @@ -8,10 +8,8 @@ supervisors = "the Board and the Branch Lead" req_admin_notify = 1 minimal_player_age = 25 - exp_requirements = 150 - exp_required_type = EXP_TYPE_SPIRAL + exp_requirements = EXP_REQ_HEAD exp_required_type_department = EXP_TYPE_SPIRAL - exp_granted_type = EXP_TYPE_SPIRAL config_tag = "PENTEX_EXECUTIVE" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/executive diff --git a/modular_darkpack/modules/jobs/code/pentex/sec.dm b/modular_darkpack/modules/jobs/code/pentex/sec.dm index 75364f7805d0..461776fe51c4 100644 --- a/modular_darkpack/modules/jobs/code/pentex/sec.dm +++ b/modular_darkpack/modules/jobs/code/pentex/sec.dm @@ -8,10 +8,8 @@ supervisors = "the Board, Branch Lead, and Chief of Security" req_admin_notify = 1 minimal_player_age = 25 - exp_requirements = 100 - exp_required_type = EXP_TYPE_SPIRAL - exp_required_type_department = EXP_TYPE_SPIRAL - exp_granted_type = EXP_TYPE_SPIRAL + exp_requirements = EXP_REQ_MINOR + // exp_required_type_department = EXP_TYPE_SPIRAL config_tag = "PENTEX_SEC" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/pentex_sec diff --git a/modular_darkpack/modules/jobs/code/pentex/secchief.dm b/modular_darkpack/modules/jobs/code/pentex/secchief.dm index fa07dde36ab6..b587ac474f4c 100644 --- a/modular_darkpack/modules/jobs/code/pentex/secchief.dm +++ b/modular_darkpack/modules/jobs/code/pentex/secchief.dm @@ -8,10 +8,8 @@ supervisors = "the Board and the Branch Lead" req_admin_notify = 1 minimal_player_age = 25 - exp_requirements = 150 - exp_required_type = EXP_TYPE_SPIRAL + exp_requirements = EXP_REQ_MINOR exp_required_type_department = EXP_TYPE_SPIRAL - exp_granted_type = EXP_TYPE_SPIRAL config_tag = "PENTEX_SECCHIEF" job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/secchief diff --git a/modular_darkpack/modules/jobs/code/police/captain.dm b/modular_darkpack/modules/jobs/code/police/captain.dm index f3a4db76409b..aaa3ba5793c8 100644 --- a/modular_darkpack/modules/jobs/code/police/captain.dm +++ b/modular_darkpack/modules/jobs/code/police/captain.dm @@ -13,6 +13,8 @@ /datum/job_department/police, ) + exp_requirements = EXP_REQ_HEAD + allowed_splats = list(SPLAT_NONE) description = "Underpaid, overworked, and understrength. Do your best to keep the order in " + CITY_NAME + ". Keep the officers in line." diff --git a/modular_darkpack/modules/jobs/code/police/fbi.dm b/modular_darkpack/modules/jobs/code/police/fbi.dm index 75b247a8b663..441954bab9ce 100644 --- a/modular_darkpack/modules/jobs/code/police/fbi.dm +++ b/modular_darkpack/modules/jobs/code/police/fbi.dm @@ -8,7 +8,6 @@ outfit = /datum/outfit/job/vampire/fbi job_flags = CITY_JOB_FLAGS display_order = JOB_DISPLAY_ORDER_FBI - exp_required_type_department = EXP_TYPE_NATIONAL_SECURITY departments_list = list( /datum/job_department/police, ) diff --git a/modular_darkpack/modules/jobs/code/police/police.dm b/modular_darkpack/modules/jobs/code/police/police.dm index 11e2fb127123..1961fca3a106 100644 --- a/modular_darkpack/modules/jobs/code/police/police.dm +++ b/modular_darkpack/modules/jobs/code/police/police.dm @@ -8,7 +8,7 @@ outfit = /datum/outfit/job/vampire/police_officer job_flags = CITY_JOB_FLAGS display_order = JOB_DISPLAY_ORDER_POLICE - exp_required_type_department = EXP_TYPE_POLICE + // exp_required_type_department = EXP_TYPE_POLICE departments_list = list( /datum/job_department/police, ) diff --git a/modular_darkpack/modules/jobs/code/police/sergeant.dm b/modular_darkpack/modules/jobs/code/police/sergeant.dm index f88bb12b754a..c61cddabcba8 100644 --- a/modular_darkpack/modules/jobs/code/police/sergeant.dm +++ b/modular_darkpack/modules/jobs/code/police/sergeant.dm @@ -8,7 +8,8 @@ outfit = /datum/outfit/job/vampire/police_sergeant job_flags = CITY_JOB_FLAGS display_order = JOB_DISPLAY_ORDER_POLICE_SERGEANT - exp_required_type_department = EXP_TYPE_POLICE + exp_requirements = EXP_REQ_MINOR + // exp_required_type_department = EXP_TYPE_POLICE departments_list = list( /datum/job_department/police, ) diff --git a/modular_darkpack/modules/jobs/code/sabbat/ductus.dm b/modular_darkpack/modules/jobs/code/sabbat/ductus.dm index 54320a1261a2..558e7eaea59e 100644 --- a/modular_darkpack/modules/jobs/code/sabbat/ductus.dm +++ b/modular_darkpack/modules/jobs/code/sabbat/ductus.dm @@ -8,7 +8,7 @@ outfit = /datum/outfit/job/vampire/sabbatductus allowed_splats = list(SPLAT_KINDRED) job_flags = CITY_JOB_FLAGS - exp_required_type_department = EXP_TYPE_SABBAT + // exp_required_type_department = EXP_TYPE_SABBAT departments_list = list( /datum/job_department/sabbat, ) diff --git a/modular_darkpack/modules/jobs/code/sabbat/pack.dm b/modular_darkpack/modules/jobs/code/sabbat/pack.dm index de5db3a7fbf2..a08ab00c1690 100644 --- a/modular_darkpack/modules/jobs/code/sabbat/pack.dm +++ b/modular_darkpack/modules/jobs/code/sabbat/pack.dm @@ -8,7 +8,7 @@ outfit = /datum/outfit/job/vampire/sabbatpack job_flags = CITY_JOB_FLAGS allowed_splats = list(SPLAT_KINDRED) - exp_required_type_department = EXP_TYPE_SABBAT + // exp_required_type_department = EXP_TYPE_SABBAT departments_list = list( /datum/job_department/sabbat, ) diff --git a/modular_darkpack/modules/jobs/code/sabbat/priest.dm b/modular_darkpack/modules/jobs/code/sabbat/priest.dm index 208b076c0927..6262afa2827e 100644 --- a/modular_darkpack/modules/jobs/code/sabbat/priest.dm +++ b/modular_darkpack/modules/jobs/code/sabbat/priest.dm @@ -8,7 +8,10 @@ outfit = /datum/outfit/job/vampire/sabbatpriest allowed_splats = list(SPLAT_KINDRED) job_flags = CITY_JOB_FLAGS + + exp_requirements = EXP_REQ_HEAD exp_required_type_department = EXP_TYPE_SABBAT + departments_list = list( /datum/job_department/sabbat, ) @@ -18,6 +21,7 @@ display_order = JOB_DISPLAY_ORDER_SABBATPRIEST whitelisted = TRUE + /datum/outfit/job/vampire/sabbatpriest name = "Sabbat Priest" jobtype = /datum/job/vampire/sabbatpriest diff --git a/modular_darkpack/modules/jobs/code/supply/dealer.dm b/modular_darkpack/modules/jobs/code/supply/dealer.dm index 66bf314be100..39d90a402572 100644 --- a/modular_darkpack/modules/jobs/code/supply/dealer.dm +++ b/modular_darkpack/modules/jobs/code/supply/dealer.dm @@ -9,11 +9,13 @@ outfit = /datum/outfit/job/vampire/dealer display_order = JOB_DISPLAY_ORDER_DEALER - exp_required_type_department = EXP_TYPE_WAREHOUSE departments_list = list( /datum/job_department/supply, ) + exp_requirements = EXP_REQ_HEAD + // exp_required_type_department = EXP_TYPE_WAREHOUSE + known_contacts = list("Prince", "Seneschal", "Sheriff", "Baron") allowed_splats = list(SPLAT_KINDRED, SPLAT_GHOUL, SPLAT_GAROU, SPLAT_NONE) splat_slots = list(SPLAT_NONE = 1) diff --git a/modular_darkpack/modules/jobs/code/supply/supply_tech.dm b/modular_darkpack/modules/jobs/code/supply/supply_tech.dm index 71f6f2d51712..3d19577b6121 100644 --- a/modular_darkpack/modules/jobs/code/supply/supply_tech.dm +++ b/modular_darkpack/modules/jobs/code/supply/supply_tech.dm @@ -8,8 +8,9 @@ job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/supply_tech + // exp_required_type_department = EXP_TYPE_WAREHOUSE + display_order = JOB_DISPLAY_ORDER_SUPPLY - exp_required_type_department = EXP_TYPE_WAREHOUSE departments_list = list( /datum/job_department/supply, ) diff --git a/modular_darkpack/modules/jobs/code/tremere/archivist.dm b/modular_darkpack/modules/jobs/code/tremere/archivist.dm index d82508c7000f..a6d2156337ed 100644 --- a/modular_darkpack/modules/jobs/code/tremere/archivist.dm +++ b/modular_darkpack/modules/jobs/code/tremere/archivist.dm @@ -7,7 +7,7 @@ config_tag = "CHANTRY_ARCHIVIST" outfit = /datum/outfit/job/vampire/archivist job_flags = CITY_JOB_FLAGS - exp_required_type_department = EXP_TYPE_CHANTRY + // exp_required_type_department = EXP_TYPE_CHANTRY departments_list = list( /datum/job_department/chantry, ) diff --git a/modular_darkpack/modules/jobs/code/tremere/gargoyle.dm b/modular_darkpack/modules/jobs/code/tremere/gargoyle.dm index ae4174802d7e..e2e10638ba38 100644 --- a/modular_darkpack/modules/jobs/code/tremere/gargoyle.dm +++ b/modular_darkpack/modules/jobs/code/tremere/gargoyle.dm @@ -7,7 +7,7 @@ config_tag = "CHANTRY_GARGOYLE" outfit = /datum/outfit/job/vampire/gargoyle job_flags = CITY_JOB_FLAGS - exp_required_type_department = EXP_TYPE_CHANTRY + // exp_required_type_department = EXP_TYPE_CHANTRY departments_list = list( /datum/job_department/chantry, ) diff --git a/modular_darkpack/modules/jobs/code/tremere/regent.dm b/modular_darkpack/modules/jobs/code/tremere/regent.dm index 199672b1d445..e7bd89bb2055 100644 --- a/modular_darkpack/modules/jobs/code/tremere/regent.dm +++ b/modular_darkpack/modules/jobs/code/tremere/regent.dm @@ -13,6 +13,8 @@ ) display_order = JOB_DISPLAY_ORDER_REGENT + exp_requirements = EXP_REQ_HEAD + description = "Lead the Chantry. You serve as both the Regent and Tremere Primogen. You report to the Tremere Lord of this region first, Prince second." minimum_masquerade = 4 allowed_splats = list(SPLAT_KINDRED) diff --git a/modular_darkpack/modules/matrix/code/matrix.dm b/modular_darkpack/modules/matrix/code/matrix.dm index 19296edd0e48..3cfd2402cefd 100644 --- a/modular_darkpack/modules/matrix/code/matrix.dm +++ b/modular_darkpack/modules/matrix/code/matrix.dm @@ -58,6 +58,7 @@ ADMIN_VERB_AND_CONTEXT_MENU(matrix_mob_verb, R_ADMIN, "Matrix Mob", "Matrix (des //handle_objectives() SSmasquerade.matrix_masquerade_breacher(despawning_mob, TRUE) + despawning_mob.client?.prefs?.save_character() // DARKPACK EDIT ADD despawning_mob.ghostize(FALSE) QDEL_NULL(despawning_mob) diff --git a/modular_darkpack/modules/powers/code/admin_discipline_editor.dm b/modular_darkpack/modules/powers/code/admin_preference_editor.dm similarity index 75% rename from modular_darkpack/modules/powers/code/admin_discipline_editor.dm rename to modular_darkpack/modules/powers/code/admin_preference_editor.dm index aa4acd106d75..1713a6f25181 100644 --- a/modular_darkpack/modules/powers/code/admin_discipline_editor.dm +++ b/modular_darkpack/modules/powers/code/admin_preference_editor.dm @@ -1,27 +1,27 @@ -/datum/admin_discipline_editor +/datum/admin_preference_editor var/target_ckey = "" var/selected_slot = 0 var/datum/preferences/target_prefs = null var/loaded_offline = FALSE var/not_found = FALSE - var/list/discipline_cache = null + var/static/list/discipline_cache = null -/datum/admin_discipline_editor/Destroy() +/datum/admin_preference_editor/Destroy() if(loaded_offline) QDEL_NULL(target_prefs) target_prefs = null return ..() -/datum/admin_discipline_editor/ui_interact(mob/user, datum/tgui/ui) +/datum/admin_preference_editor/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, "AdminDisciplineEditor") + ui = new(user, src, "AdminPreferenceEditor") ui.open() -/datum/admin_discipline_editor/ui_state(mob/user) +/datum/admin_preference_editor/ui_state(mob/user) return ADMIN_STATE(R_ADMIN) -/datum/admin_discipline_editor/ui_data(mob/user) +/datum/admin_preference_editor/ui_data(mob/user) var/list/data = list() data["target_ckey"] = target_ckey data["selected_slot"] = selected_slot @@ -97,9 +97,22 @@ data["discipline_validation"] = validate_discipline_sheet(target_prefs.discipline_levels, clan_discs, target_prefs.discipline_trusted) + var/splat_path = target_prefs?.read_preference(/datum/preference/choiced/splats) + if(!ispath(splat_path, /datum/splat/vampire)) + data["clan_name"] = null + data["clan_disciplines"] = list() + + var/client/target_client = GLOB.directory[target_ckey] + // if they have a value for screen_maps, they are in the character creator. + // an admin shouldnt be editing the same slot at the same time or it will risk wiping the slot + data["is_editing_character"] = target_client && length(target_client.screen_maps) > 0 + var/list/target_wl = target_prefs ? target_prefs.get_player_whitelists() : null + data["player_whitelists"] = target_wl || list() + data["whitelist_definitions"] = get_whitelist_definitions() + return data -/datum/admin_discipline_editor/proc/build_discipline_cache() +/datum/admin_preference_editor/proc/build_discipline_cache() if(discipline_cache) return discipline_cache @@ -124,7 +137,7 @@ return discipline_cache -/datum/admin_discipline_editor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) +/datum/admin_preference_editor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() if(.) return @@ -200,15 +213,36 @@ log_admin("[key_name_admin(ui.user)] set [ADMIN_LOOKUPFLW(target_ckey)]'s character [character_name] to generation [new_gen]).") return TRUE + if("set_whitelist") + if(!target_prefs) + return FALSE + var/whitelist_id = params["id"] + if(!istext(whitelist_id) || !length(whitelist_id)) + return FALSE + var/should_grant = text2num(params["grant"]) + if(should_grant) + target_prefs.grant_whitelist(whitelist_id) + else + target_prefs.revoke_whitelist(whitelist_id) + target_prefs.save_preferences() + var/granted_revoked = should_grant ? "granted" : "revoked" + message_admins("[key_name_admin(ui.user)] [granted_revoked] whitelist '[whitelist_id]' for [ADMIN_LOOKUPFLW(target_ckey)].") + log_admin("[key_name_admin(ui.user)] [granted_revoked] whitelist '[whitelist_id]' for [ADMIN_LOOKUPFLW(target_ckey)].") + // SSoverwatch.record_action(null, "[key_name_admin(ui.user)] [granted_revoked] whitelist '[whitelist_id]' for [ADMIN_LOOKUPFLW(target_ckey)].") + return TRUE + if("toggle_trusted") if(!target_prefs) return FALSE - target_prefs.discipline_trusted = !target_prefs.discipline_trusted + if(target_prefs.has_whitelist(WHITELIST_TRUSTED)) + target_prefs.revoke_whitelist(WHITELIST_TRUSTED) + else + target_prefs.grant_whitelist(WHITELIST_TRUSTED) target_prefs.save_preferences() - message_admins("[ui.user] [target_prefs.discipline_trusted ? "granted" : "revoked"] trusted discipline whitelist for [ADMIN_LOOKUPFLW(target_ckey)].") + message_admins("[key_name_admin(ui.user)] [target_prefs.has_whitelist(WHITELIST_TRUSTED) ? "granted" : "revoked"] trusted whitelist for [ADMIN_LOOKUPFLW(target_ckey)].") return TRUE -/datum/admin_discipline_editor/proc/load_target(search_ckey) +/datum/admin_preference_editor/proc/load_target(search_ckey) if(loaded_offline && target_prefs) qdel(target_prefs) target_prefs = null @@ -237,7 +271,7 @@ selected_slot = 1 return TRUE -ADMIN_VERB(discipline_menu, R_ADMIN, "Discipline Menu", "Edit a player's disciplines.", ADMIN_CATEGORY_SECOND_CITY) - var/datum/admin_discipline_editor/editor = new +ADMIN_VERB(admin_preference_editor, R_ADMIN, "Discipline/Preference Editor", "Edit and view a player's disciplines and preferences.", ADMIN_CATEGORY_SECOND_CITY) + var/datum/admin_preference_editor/editor = new editor.ui_interact(user.mob) - BLACKBOX_LOG_ADMIN_VERB("Discipline Menu") + BLACKBOX_LOG_ADMIN_VERB("Discipline/Preference Editor") diff --git a/modular_darkpack/modules/powers/code/discipline_pref_middleware.dm b/modular_darkpack/modules/powers/code/discipline_pref_middleware.dm index 4017f4560f67..502790d93cab 100644 --- a/modular_darkpack/modules/powers/code/discipline_pref_middleware.dm +++ b/modular_darkpack/modules/powers/code/discipline_pref_middleware.dm @@ -7,9 +7,10 @@ GLOBAL_LIST_INIT(rare_discipline_types, list( /datum/discipline/obtenebration, /datum/discipline/thaumaturgy, /datum/discipline/necromancy, - /datum/discipline/daimoinon, /datum/discipline/valeren, - // melpominee not yet implemented but will go here + /datum/discipline/obeah, + /datum/discipline/daimoinon, + /datum/discipline/melpominee, )) // warns a player if they have no discipline dots assigned before joining @@ -118,6 +119,7 @@ GLOBAL_LIST_INIT(rare_discipline_types, list( data["clan_disciplines"] = list() data["clan_name"] = null var/clan_value = preferences.read_preference(/datum/preference/choiced/subsplat/vampire_clan) + /* // DARKPACK EDIT REMOVAL - dont automatically give them their clan disciplines in the UI actually. sometimes people want to be dominate malks or otherwise go homebrew if(clan_value) var/datum/subsplat/vampire_clan/clan_datum = get_vampire_clan(clan_value) if(clan_datum) @@ -125,6 +127,7 @@ GLOBAL_LIST_INIT(rare_discipline_types, list( for(var/disc_type in clan_datum.clan_disciplines) if(ispath(disc_type, /datum/discipline)) data["clan_disciplines"] += "[disc_type]" + */ var/discipline_count = 0 var/list/counted_discs = list() @@ -257,8 +260,18 @@ GLOBAL_LIST_INIT(rare_discipline_types, list( if(!isnewplayer(user) && ("[user.client.prefs.default_slot]" in user.persistent_client.joined_as_slots)) to_chat(user, span_warning("You may not adjust discipline dots of characters that have played in the current round.")) return FALSE - - preferences.discipline_levels = list() + // preferences.discipline_levels = list() // DARKPACK EDIT REMOVAL + // DARKPACK EDIT START + var/clan_value = preferences.read_preference(/datum/preference/choiced/subsplat/vampire_clan) + if(!clan_value) + return FALSE + preferences.discipline_levels = list() // restore them to default + var/datum/subsplat/vampire_clan/clan_datum = get_vampire_clan(clan_value) // then give them their default clan discs. clear_discipline_levels fires from changing clans + if(clan_datum) + for(var/disc_type in clan_datum.clan_disciplines) + if(ispath(disc_type, /datum/discipline)) + preferences.discipline_levels += disc_type + // DARKPACK EDIT END preferences.save_character() return TRUE diff --git a/modular_darkpack/modules/splats/code/splat_configs.dm b/modular_darkpack/modules/splats/code/splat_configs.dm index 06f159a8a209..0c0f3e6f0b30 100644 --- a/modular_darkpack/modules/splats/code/splat_configs.dm +++ b/modular_darkpack/modules/splats/code/splat_configs.dm @@ -1,5 +1,6 @@ /// List of roundstart splats' their splat_id's GLOBAL_LIST_EMPTY(roundstart_splats) +GLOBAL_LIST_EMPTY(whitelisted_splats) /datum/config_entry/keyed_list/roundstart_splats //splats you can play as from the get go. key_mode = KEY_MODE_TEXT @@ -12,6 +13,19 @@ GLOBAL_LIST_EMPTY(roundstart_splats) log_config("ERROR: [key_name] is not a valid race ID.") return FALSE + +/datum/config_entry/keyed_list/whitelisted_splats //splats you can play as from the get go. + key_mode = KEY_MODE_TEXT + value_mode = VALUE_MODE_FLAG + +/datum/config_entry/keyed_list/whitelisted_splats/ValidateListEntry(key_name, key_value) + if(key_name in GLOB.splat_list) + return TRUE + + log_config("ERROR: [key_name] is not a valid race ID.") + return FALSE + + /** * Checks if a splat is eligible to be picked at roundstart. * @@ -23,6 +37,12 @@ GLOBAL_LIST_EMPTY(roundstart_splats) return TRUE return FALSE +/datum/splat/proc/check_whitelist_requirement() + if(id in (CONFIG_GET(keyed_list/whitelisted_splats))) + return TRUE + return FALSE + + /** * Generates splat available to choose in character setup at roundstart * diff --git a/modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/trusted_whitelist.dm b/modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/trusted_whitelist.dm new file mode 100644 index 000000000000..60c03a015647 --- /dev/null +++ b/modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/trusted_whitelist.dm @@ -0,0 +1,64 @@ +GLOBAL_LIST_INIT(trusted_only_clans, list( + VAMPIRE_CLAN_BAALI, + VAMPIRE_CLAN_HEALER_SALUBRI, + VAMPIRE_CLAN_WARRIOR_SALUBRI, + VAMPIRE_CLAN_TRUE_BRUJAH, + VAMPIRE_CLAN_CAPPADOCIAN, + VAMPIRE_CLAN_HARBINGER +)) + +// remember kids, you should always obtain enthusiastic informed values before proceeding +/datum/preference/choiced/subsplat/vampire_clan/create_informed_default_value(datum/preferences/preferences) + if(preferences && !preferences.has_whitelist(WHITELIST_TRUSTED)) + var/list/safe_choices = list() + for(var/choice in get_choices()) + var/datum/subsplat/vampire_clan/clan = get_vampire_clan(choice) + if(!clan || !(clan.id in GLOB.trusted_only_clans)) + safe_choices += choice + else if(preferences.has_whitelist(clan.id)) + safe_choices += choice + if(length(safe_choices)) + return pick(safe_choices) + return ..() + +/datum/preference/choiced/subsplat/vampire_clan/is_valid(value, datum/preferences/preferences) + if(preferences && !preferences.has_whitelist(WHITELIST_TRUSTED)) + var/datum/subsplat/vampire_clan/clan = get_vampire_clan(value) + if(clan?.id in GLOB.trusted_only_clans) + if(!preferences.has_whitelist(clan.id)) + to_chat(preferences.parent, span_warning("The [clan.name] clan requires a special whitelisting process. Feel free to apply for it on Discord!")) + return FALSE + return ..() + +// helper procs to make absolutely sure unwhitelisted people cannot join with whitelisted splats +// useful in-case a regime member removes a trusted player from the whitelist +// so they cant continue to play on the trusted character with that clan selected +/proc/get_restricted_clan(mob/dead/new_player/new_player) + var/client/C = new_player.client + if(!C?.prefs) + return null + if(C.prefs.has_whitelist(WHITELIST_TRUSTED)) + return null + var/selected = C.prefs.read_preference(/datum/preference/choiced/subsplat/vampire_clan) + var/datum/subsplat/vampire_clan/clan = get_vampire_clan(selected) + if(clan?.id in GLOB.trusted_only_clans) + if(!C.prefs.has_whitelist(clan.id)) + return clan + return null + +/atom/movable/screen/lobby/button/ready/Click(location, control, params) + var/mob/dead/new_player/new_player = hud.mymob + if(new_player.ready == PLAYER_NOT_READY) + var/datum/subsplat/vampire_clan/clan = get_restricted_clan(new_player) + if(clan) + to_chat(new_player, span_warning("[clan.name] requires a special whitelisting process. Feel free to apply for it on Discord!")) + return + return ..() + +/atom/movable/screen/lobby/button/join/Click(location, control, params) + var/mob/dead/new_player/new_player = hud.mymob + var/datum/subsplat/vampire_clan/clan = get_restricted_clan(new_player) + if(clan) + to_chat(new_player, span_warning("[clan.name] requires a special whitelisting process. Feel free to apply for it on Discord!")) + return + return ..() diff --git a/modular_darkpack/modules/whitelist/code/whitelist_enforcement.dm b/modular_darkpack/modules/whitelist/code/whitelist_enforcement.dm new file mode 100644 index 000000000000..e0318f6227ff --- /dev/null +++ b/modular_darkpack/modules/whitelist/code/whitelist_enforcement.dm @@ -0,0 +1,42 @@ +/mob/dead/new_player/proc/get_restricted_splat_name() + var/client/C = client + if(!C?.prefs) + return null + var/splat_path = C.prefs.read_preference(/datum/preference/choiced/splats) + if(!ispath(splat_path, /datum/splat)) + if(!C.prefs.has_whitelist(SPLAT_NONE)) + return "Human" + return null + var/datum/splat/splat = GLOB.splat_prototypes[splat_path] + if(!splat) + return null + if(!isnull(splat.id) && !C.prefs.has_whitelist(splat.id)) + return splat.name + return null + +/atom/movable/screen/lobby/button/ready/Click(location, control, params) + var/mob/dead/new_player/new_player = hud.mymob + if(new_player.ready == PLAYER_NOT_READY) + var/splat_name = new_player.get_restricted_splat_name() + if(splat_name) + to_chat(new_player, span_warning("[splat_name] requires a special whitelisting process. Feel free to apply for it on Discord!")) + return + return ..() + +/atom/movable/screen/lobby/button/join/Click(location, control, params) + var/mob/dead/new_player/new_player = hud.mymob + var/splat_name = new_player.get_restricted_splat_name() + if(splat_name) + to_chat(new_player, span_warning("[splat_name] requires a special whitelisting process. Feel free to apply for it on Discord!")) + return + return ..() + +/datum/preference/choiced/splats/is_valid(value, datum/preferences/preferences) + if(preferences) + var/splat_id = SPLAT_NONE + if(ispath(value, /datum/splat)) + var/datum/splat/splat = GLOB.splat_prototypes[value] + splat_id = splat?.id || SPLAT_NONE + if(!isnull(splat_id) && !preferences.has_whitelist(splat_id)) + return FALSE + return ..() diff --git a/modular_darkpack/modules/whitelist/code/whitelist_prefs.dm b/modular_darkpack/modules/whitelist/code/whitelist_prefs.dm new file mode 100644 index 000000000000..71c5cdcecca7 --- /dev/null +++ b/modular_darkpack/modules/whitelist/code/whitelist_prefs.dm @@ -0,0 +1,80 @@ +/datum/preferences + var/list/player_whitelists = null + +/datum/preferences/proc/get_player_whitelists() + if(isnull(player_whitelists)) + player_whitelists = GLOB.default_player_whitelists.Copy() + return player_whitelists + +/datum/preferences/proc/has_whitelist(whitelist_id) + return whitelist_id in get_player_whitelists() + +/datum/preferences/proc/grant_whitelist(whitelist_id) + var/list/wl = get_player_whitelists() + if(whitelist_id in wl) + return + wl += whitelist_id + if(whitelist_id == WHITELIST_TRUSTED) + discipline_trusted = TRUE + +/datum/preferences/proc/revoke_whitelist(whitelist_id) + if(whitelist_id == SPLAT_NONE) // as funny as it would be, this should probably be protected + return + var/list/wl = get_player_whitelists() + wl -= whitelist_id + if(whitelist_id == WHITELIST_TRUSTED) + discipline_trusted = FALSE + +/datum/preference_middleware/disciplines/get_ui_data(mob/user) + var/list/data = ..() + var/datum/preferences/prefs = user?.client?.prefs + var/list/player_wl = prefs ? prefs.get_player_whitelists() : null + data["player_whitelists"] = player_wl ? player_wl.Copy() : list() + return data + +/datum/admin_preference_editor/proc/get_whitelist_definitions() + var/list/defs = list() + + defs[SPLAT_NONE] = list( + "name" = "Human", + "description" = "Access to play as a human.", + "category" = "splat", + "is_default" = TRUE, + ) + for(var/splat_id in get_selectable_splats()) + var/splat_type = GLOB.splat_list[splat_id] + var/datum/splat/splat = GLOB.splat_prototypes[splat_type] + + defs[splat_id] = list( + "name" = splat.name, + "description" = "Access to play as a [splat.name].", + "category" = "splat", + "is_default" = !splat.check_whitelist_requirement(), + ) + + defs[WHITELIST_TRUSTED] = list( + "name" = "Trusted", + "description" = "Bypasses discipline sheet limits, unlocks all trusted-only clans, and allows them to be a lower generation kindred.", + "category" = "access", + "is_default" = FALSE, + ) + defs[WHITELIST_TIMELIMITS] = list( + "name" = "Bypass Time Requirements", + "description" = "Bypasses time requierments for jobs.", + "category" = "access", + "is_default" = FALSE, + ) + + + for(var/clan_name in GLOB.vampire_clan_list) + var/datum/subsplat/vampire_clan/clan = get_vampire_clan(clan_name) + if(!clan || !(clan.id in GLOB.trusted_only_clans)) + continue + defs[clan.id] = list( + "name" = clan.name, + "description" = "Access to play [clan.name] without requiring trusted whitelist", + "category" = "clan", + "is_default" = FALSE, + ) + + return defs diff --git a/modular_darkpack/modules/whitelist/code/whitelist_savefile.dm b/modular_darkpack/modules/whitelist/code/whitelist_savefile.dm new file mode 100644 index 000000000000..cea59ea9fe0e --- /dev/null +++ b/modular_darkpack/modules/whitelist/code/whitelist_savefile.dm @@ -0,0 +1,22 @@ +/datum/preferences/load_preferences() + . = ..() + var/list/saved = savefile.get_entry("player_whitelists") + if(isnull(saved) || !islist(saved)) + player_whitelists = GLOB.default_player_whitelists.Copy() + else + player_whitelists = list() + for(var/entry in saved) + if(istext(entry)) + player_whitelists += entry + + if(discipline_trusted && !(WHITELIST_TRUSTED in player_whitelists)) // backwards compatibility + player_whitelists += WHITELIST_TRUSTED + if(!isnull(parent)) + to_chat(parent, span_notice("LOG: Great news! Your existing trusted status was successfully migrated to the new splat whitelist system.")) + + discipline_trusted = (WHITELIST_TRUSTED in player_whitelists) + +/datum/preferences/save_preferences() + if(!isnull(player_whitelists)) + savefile.set_entry("player_whitelists", player_whitelists) + . = ..() diff --git a/tgstation.dme b/tgstation.dme index c0bdda7d0859..81fd9dd2182c 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -478,6 +478,7 @@ #include "code\__DEFINES\~darkpack\subsystems.dm" #include "code\__DEFINES\~darkpack\vampire_clan.dm" #include "code\__DEFINES\~darkpack\walls.dm" +#include "code\__DEFINES\~darkpack\whitelist.dm" #include "code\__DEFINES\~darkpack\xp_and_freebie.dm" #include "code\__DEFINES\~darkpack\dcs\beastmaster_signals.dm" #include "code\__DEFINES\~darkpack\dcs\bouncer_signals.dm" @@ -7757,7 +7758,7 @@ #include "modular_darkpack\modules\pixel_shift\code\pixel_shift_component.dm" #include "modular_darkpack\modules\pixel_shift\code\pixel_shift_keybind.dm" #include "modular_darkpack\modules\postal\code\postal.dm" -#include "modular_darkpack\modules\powers\code\admin_discipline_editor.dm" +#include "modular_darkpack\modules\powers\code\admin_preference_editor.dm" #include "modular_darkpack\modules\powers\code\discipline_actions.dm" #include "modular_darkpack\modules\powers\code\discipline_configs.dm" #include "modular_darkpack\modules\powers\code\discipline_gain.dm" @@ -8001,6 +8002,7 @@ #include "modular_darkpack\modules\vampire_the_masquerade\code\vampire_clan\clan_mark.dm" #include "modular_darkpack\modules\vampire_the_masquerade\code\vampire_clan\clan_mark_pref.dm" #include "modular_darkpack\modules\vampire_the_masquerade\code\vampire_clan\clan_pref.dm" +#include "modular_darkpack\modules\vampire_the_masquerade\code\vampire_clan\trusted_whitelist.dm" #include "modular_darkpack\modules\vampire_the_masquerade\code\vampire_clan\clans\banu_haqim.dm" #include "modular_darkpack\modules\vampire_the_masquerade\code\vampire_clan\clans\brujah.dm" #include "modular_darkpack\modules\vampire_the_masquerade\code\vampire_clan\clans\caitiff.dm" @@ -8139,6 +8141,9 @@ #include "modular_darkpack\modules\werewolf_the_apocalypse\code\subsplats\tribes\garou.dm" #include "modular_darkpack\modules\werewolf_the_apocalypse\code\weapons\klaives.dm" #include "modular_darkpack\modules\westfield_mall\code\mall_areas.dm" +#include "modular_darkpack\modules\whitelist\code\whitelist_enforcement.dm" +#include "modular_darkpack\modules\whitelist\code\whitelist_savefile.dm" +#include "modular_darkpack\modules\whitelist\code\whitelist_prefs.dm" #include "modular_darkpack\modules\westfield_mall\code\modular_rooms.dm" #include "modular_darkpack\modules\z_travel\code\helpers.dm" #include "modular_darkpack\modules\z_travel\code\manhole.dm" diff --git a/tgui/packages/tgui/interfaces/AdminDisciplineEditor.tsx b/tgui/packages/tgui/interfaces/AdminDisciplineEditor.tsx deleted file mode 100644 index 92a24b3b7316..000000000000 --- a/tgui/packages/tgui/interfaces/AdminDisciplineEditor.tsx +++ /dev/null @@ -1,487 +0,0 @@ -import { useState } from 'react'; - -import { useBackend } from 'tgui/backend'; -import { Box, Button, DmIcon, Icon, Input, NumberInput, Section, Stack, Tooltip } from 'tgui-core/components'; -import { Window } from '../layouts'; - -type DisciplineInfo = { - name: string; - desc: string; - max_level: number; - rarity: 'rare' | 'common'; - icon?: string; - icon_state?: string; -}; - -type DisciplineValidation = { - total: number; - additional: number; - additional_rare: number; - valid: boolean; - violations: string[]; -}; - -type Data = { - target_ckey: string; - selected_slot: number; - not_found: boolean; - is_trusted: boolean; - character_slots: { slot: number; name: string }[]; - discipline_levels: Record; - clan_disciplines: string[]; - disciplines: Record; - discipline_validation: DisciplineValidation | null; - connected_ckeys: string[]; - invalid_ckeys: string[]; - trusted_ckeys: string[]; - character_name: string | null; - character_age: number | null; - immortal_age: number | null; - clan_name: string | null; - splat_name: string | null; - generation: number | null; - flavor_text: string | null; - headshot: string | null; -}; - -type DisciplineCardProps = { - path: string; - discipline: DisciplineInfo; - level: number; - isClanDiscipline: boolean; - clanName: string | null; - isAdditional: boolean; - onDotClick: (position: number) => void; -}; - -function DisciplineCard(props: DisciplineCardProps) { - const { discipline, level, isClanDiscipline, clanName, isAdditional, onDotClick } = props; - const isRare = discipline.rarity === 'rare'; - - return ( - -
- - {discipline.icon && discipline.icon_state && ( - - - - )} - - - {isClanDiscipline && clanName && ( - - ({clanName} Clan Discipline) - - )} - - {isRare ? 'Rare Discipline' : 'Common Discipline'} - - {discipline.desc} - - } - > - - {discipline.name} - {isClanDiscipline && ( - - - - )} - - - - - - {!isAdditional && ( - - Clan - - )} - - {isRare ? 'Rare' : 'Common'} - - - - - - {Array.from({ length: discipline.max_level }, (_, i) => { - const position = i + 1; - const filled = position <= level; - return ( - - onDotClick(position)} - style={{ cursor: 'pointer', fontSize: '13px', margin: '0 2px' }} - /> - - ); - })} - - - -
-
- ); -} - -export function AdminDisciplineEditor() { - const { act, data } = useBackend(); - const { - target_ckey, - selected_slot, - not_found, - is_trusted, - character_slots, - discipline_levels, - clan_disciplines, - disciplines, - discipline_validation, - connected_ckeys, - invalid_ckeys, - trusted_ckeys, - character_name, - character_age, - immortal_age, - clan_name, - splat_name, - generation, - flavor_text, - headshot, - } = data; - - const [ckeyInput, setCkeyInput] = useState(''); - const clanSet = new Set(clan_disciplines || []); - const allDisciplines = disciplines || {}; - const levels = discipline_levels || {}; - const connected = connected_ckeys || []; - const invalidSet = new Set(invalid_ckeys || []); - const trustedSet = new Set(trusted_ckeys || []); - - const handleDotClick = (path: string, position: number, currentLevel: number) => { - const newLevel = position <= currentLevel ? (position === 1 ? 0 : position) : position; - act('set_discipline_level', { discipline: path, level: newLevel }); - }; - - const disciplineEntries = Object.entries(allDisciplines); - - return ( - - - - -
- {connected.length === 0 && ( - - No players online. - - )} - {connected.map((ckey) => ( - - - - ))} -
-
- -
- - - setCkeyInput(value)} - onEnter={() => act('search_ckey', { ckey: ckeyInput })} - /> - - - - - - {!!not_found && ( - - No player found for ckey "{target_ckey}". - - )} -
- {target_ckey && !not_found && character_slots.length > 0 && ( -
- {target_ckey}'s character slots - - - - - - - } - > - - {character_slots.map(({ slot, name }) => ( - - - - ))} - -
- )} - {selected_slot > 0 && character_name && ( -
- - - - {character_name} - - - {character_age !== null && ( - - - Living Age: - - - {character_age} - - - )} - {immortal_age !== null && ( - - - Immortal Age: - - - {immortal_age} - - - )} - {character_age !== null && immortal_age !== null && ( - - - Chronological Age: - - - {character_age + immortal_age} - - - )} - {splat_name && ( - - - Splat: - - - {splat_name} - - - )} - {clan_name && ( - - - Clan: - - - {clan_name} - - - )} - {generation !== null && ( - - - Generation: - - - act('set_generation', { generation: value }) - } - /> - - )} - - {flavor_text && ( - - {flavor_text} - - )} - - - - {headshot && ( - - )} - - - -
- )} - {discipline_validation && ( -
- - - - Total disciplines: - - 5 ? 'red' : 'white'}> - {discipline_validation.total} / 5 - - - - - Non-Clan Rares in Use: - - = 2 ? 'red' : 'white'} - > - {discipline_validation.additional_rare} - - - - {is_trusted ? ( - - - Trusted - - ) : discipline_validation.valid ? ( - - - In compliance - - ) : ( - - - - Invalid! - - - )} - - -
- )} - - {selected_slot > 0 && splat_name === 'Human' && ( - - This character cannot be assigned disciplines or gifts. - - )} - {selected_slot > 0 && splat_name !== 'Human' && ( - - {disciplineEntries.map(([path, discipline]) => { - const level = levels[path] ?? 0; - return ( - handleDotClick(path, position, level)} - /> - ); - })} - - )} -
-
-
-
- ); -} diff --git a/tgui/packages/tgui/interfaces/AdminPreferenceEditor.tsx b/tgui/packages/tgui/interfaces/AdminPreferenceEditor.tsx new file mode 100644 index 000000000000..bd1e0494d582 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AdminPreferenceEditor.tsx @@ -0,0 +1,519 @@ +import { useState } from 'react'; +import { useBackend } from 'tgui/backend'; +import { Box, Button, Divider, DmIcon, Icon, Input, NumberInput, Section, Stack, Tooltip } from 'tgui-core/components'; +import { Window } from '../layouts'; + +type DisciplineInfo = { + name: string; + desc: string; + max_level: number; + rarity: 'rare' | 'common'; + icon?: string; + icon_state?: string; +}; + +type DisciplineValidation = { + total: number; + additional: number; + additional_rare: number; + valid: boolean; + violations: string[]; +}; + +type WhitelistDefinition = { + name: string; + description: string; + category: 'splat' | 'access' | 'clan'; + is_default: boolean; +}; + +type Data = { + target_ckey: string; + selected_slot: number; + not_found: boolean; + is_trusted: boolean; + character_slots: { slot: number; name: string }[]; + discipline_levels: Record; + clan_disciplines: string[]; + disciplines: Record; + discipline_validation: DisciplineValidation | null; + connected_ckeys: string[]; + invalid_ckeys: string[]; + trusted_ckeys: string[]; + is_editing_character: boolean; + character_name: string | null; + character_age: number | null; + immortal_age: number | null; + clan_name: string | null; + generation: number | null; + splat_name: string | null; + flavor_text: string | null; + headshot: string | null; + player_whitelists: string[]; + whitelist_definitions: Record; +}; + +type DisciplineCardProps = { + path: string; + discipline: DisciplineInfo; + level: number; + isOwned: boolean; + isClanDiscipline: boolean; + clanName: string | null; + isAdditional: boolean; + onDotClick: (position: number) => void; + onRemove: (path: string) => void; +}; + +function DisciplineCard(props: DisciplineCardProps) { + const { discipline, level, isOwned, isClanDiscipline, clanName, path, isAdditional, onDotClick, onRemove } = props; + const isRare = discipline.rarity === 'rare'; + + return ( + + +
+ {isOwned && ( + onRemove(path)} + /> + )} + + + {discipline.icon && discipline.icon_state && ( + + + + )} + + + {isClanDiscipline && clanName && ( + + ({clanName} Clan Discipline) + + )} + + {isRare ? 'Rare Discipline' : 'Common Discipline'} + + {discipline.desc} + + } + > + + {discipline.name} + {isClanDiscipline && ( + + + + )} + + + + + + {!isAdditional && ( + + Clan + + )} + + {isRare ? 'Rare' : 'Common'} + + + + + + {Array.from({ length: discipline.max_level }, (_, i) => { + const position = i + 1; + const filled = position <= level; + return ( + + onDotClick(position)} + style={{ cursor: 'pointer', fontSize: '11px', margin: '0 1px' }} + /> + + ); + })} + + + +
+
+ ); +} + +const WHITELIST_CATEGORY_LABELS: Record = { + splat: 'Splat Access', + access: 'Access Level', + clan: 'Clan Access', +}; + +function WhitelistSection(props: { + playerWhitelists: string[]; + whitelistDefinitions: Record; + onToggle: (id: string, currentlyHas: boolean) => void; +}) { + const { playerWhitelists, whitelistDefinitions, onToggle } = props; + const whitelistSet = new Set(playerWhitelists); + const categories = ['splat', 'access', 'clan'] as const; + + return ( +
+ + {categories.map((category) => { + const entries = Object.entries(whitelistDefinitions).filter( + ([, wl]) => wl.category === category, + ); + if (!entries.length) return null; + return ( + + + {WHITELIST_CATEGORY_LABELS[category]} + + + {entries.map(([id, wl]) => { + const has = whitelistSet.has(id); + return ( + + + + + + ); + })} + + + ); + })} + +
+ ); +} + +export function AdminPreferenceEditor() { + const { act, data } = useBackend(); + const { + target_ckey, + selected_slot, + not_found, + is_trusted, + character_slots, + discipline_levels, + clan_disciplines, + disciplines, + discipline_validation, + connected_ckeys, + invalid_ckeys, + trusted_ckeys, + is_editing_character, + character_name, + character_age, + immortal_age, + clan_name, + splat_name, + generation, + flavor_text, + headshot, + player_whitelists, + whitelist_definitions, + } = data; + + const [ckeyInput, setCkeyInput] = useState(''); + const clanSet = new Set(clan_disciplines || []); + const allDisciplines = disciplines || {}; + const levels = discipline_levels || {}; + const connected = connected_ckeys || []; + const invalidSet = new Set(invalid_ckeys || []); + const trustedSet = new Set(trusted_ckeys || []); + + const handleDotClick = (path: string, position: number, currentLevel: number) => { + const newLevel = position <= currentLevel ? (position === 1 ? 0 : position) : position; + act('set_discipline_level', { discipline: path, level: newLevel }); + }; + + const handleRemove = (path: string) => { + act('set_discipline_level', { discipline: path, level: 0 }); + }; + + const handleWhitelistToggle = (id: string, currentlyHas: boolean) => { + act('set_whitelist', { id, grant: currentlyHas ? 0 : 1 }); + }; + + const disciplineEntries = Object.entries(allDisciplines); + + return ( + + + + +
+ {connected.length === 0 && No players online.} + {connected.map((ckey) => ( + + + + ))} +
+
+ +
+ + + setCkeyInput(value)} + onEnter={() => act('search_ckey', { ckey: ckeyInput })} + /> + + + + + + {!!not_found && ( + + No player found for ckey "{target_ckey}". + + )} +
+ + {target_ckey && !not_found && ( + + )} + + {target_ckey && !not_found && character_slots.length > 0 && ( +
+ + {character_slots.map(({ slot, name }) => ( + + + + ))} + +
+ )} + {selected_slot > 0 && character_name && ( + <> +
+ + + {character_name} + + + Living Age: + {character_age} + + + Immortal Age: + {immortal_age} + + + Clan: + {clan_name} + + + Splat: + {splat_name} + + {generation !== null && ( + + + Generation: + + + act('set_generation', { generation: value }) + } + /> + + )} + + {flavor_text && ( + + {flavor_text} + + )} + + + + {headshot && } + + + +
+ + {is_editing_character ? ( +
+ + + + + + + CHARACTER IS BEING EDITED + + + + +
For safety, you cannot make changes to {target_ckey}'s sheet while they have the character setup menu open.
+ You should probably ask {target_ckey} to spawn in, just to be safe.
+
+
+
+
+ ) : ( + <> + {discipline_validation && ( +
+ + + Total disciplines: + 5 ? 'red' : 'white'}> + {discipline_validation.total} / 5 + + + + Non-Clan Rares: + = 2 ? 'red' : 'white'}> + {discipline_validation.additional_rare} + + + + {is_trusted ? ( + + Trusted + + ) : ( + <> + + Untrusted + + + {discipline_validation.valid ? ( + In compliance + ) : ( + + + Invalid Configuration + + + )} + + + )} + + +
+ )} + {splat_name === 'Human' ? ( + + This character cannot be assigned disciplines or gifts. + + ) : ( + + {disciplineEntries.map(([path, discipline]) => { + const level = levels[path] ?? 0; + const isOwned = path in levels; + return ( + handleDotClick(path, position, level)} + onRemove={handleRemove} + /> + ); + })} + + )} + + )} + + )} +
+
+
+
+ ); +} diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferences/MainPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferences/MainPage.tsx index df6248910953..cd9a3ecb6c9c 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferences/MainPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferences/MainPage.tsx @@ -12,11 +12,13 @@ import { NumberInput, // DARKPACK EDIT Section, Stack, + Icon, // DARKPACK EDIT ADD } from 'tgui-core/components'; import { exhaustiveCheck } from 'tgui-core/exhaustive'; // DARKPACK EDIT ADD import { classes } from 'tgui-core/react'; import { createSearch } from 'tgui-core/string'; import { CharacterPreview } from '../../common/CharacterPreview'; +import { ConfirmModal } from '../components/ConfirmModal'; // DARKPACK EDIT ADD import { PageButton } from '../components/PageButton'; // DARKPACK EDIT ADD import { RandomizationButton } from '../components/RandomizationButton'; import { features } from '../preferences/features'; @@ -36,6 +38,7 @@ import { useServerPrefs } from '../useServerPrefs'; import { DeleteCharacterPopup } from './DeleteCharacterPopup'; import { MultiNameInput, NameInput } from './names'; import { VocalsInput, VoiceInput } from './darkpack_vocals'; // DARKPACK EDIT ADDITION +import { TRUSTED_CLAN_WHITELIST_IDS } from '../preferences/features/character_preferences/darkpack_trusted_whitelist'; // DARKPACK EDIT ADD const CLOTHING_CELL_SIZE = 48; const CLOTHING_SIDEBAR_ROWS = 12; // DARKPACK EDIT CHANGE - ORIGINAL: 9; @@ -105,10 +108,11 @@ type ChoicedSelectionProps = { supplementalFeature?: string; supplementalValue?: unknown; onSelect: (value: string) => void; + isLocked?: (choice: string) => boolean; // DARKPACK EDIT ADD }; function ChoicedSelection(props: ChoicedSelectionProps) { - const { catalog, supplementalFeature, supplementalValue } = props; + const { catalog, supplementalFeature, supplementalValue, isLocked } = props; // DARKPACK EDIT CHANGE - ORIGINAL: const { catalog, supplementalFeature, supplementalValue } = props; const [searchText, setSearchText] = useState(''); if (!catalog.icons) { @@ -154,6 +158,7 @@ function ChoicedSelection(props: ChoicedSelectionProps) { {searchInCatalog(searchText, catalog.icons).map( ([name, image], index) => { + const locked = isLocked?.(name) ?? false; // DARKPACK EDIT ADD return ( ); }, @@ -257,6 +280,7 @@ type MainFeatureProps = { handleSelect: (newClothing: string) => void; randomization?: RandomSetting; setRandomization: (newSetting: RandomSetting) => void; + isLocked?: (choice: string) => boolean; // DARKPACK EDIT ADD }; function MainFeature(props: MainFeatureProps) { @@ -267,6 +291,7 @@ function MainFeature(props: MainFeatureProps) { handleSelect, randomization, setRandomization, + isLocked, // DARKPACK EDIT ADD } = props; const supplementalFeature = catalog.supplemental_feature; @@ -288,6 +313,7 @@ function MainFeature(props: MainFeatureProps) { ] } onSelect={handleSelect} + isLocked={isLocked} // DARKPACK EDIT ADD /> } > @@ -464,6 +490,10 @@ export function MainPage(props: MainPageProps) { const [vocalsInputOpen, setVocalsInputOpen] = useState(false); // DARKPACK EDIT ADDITION const [randomToggleEnabled] = useRandomToggleState(); const [pendingConfirm, setPendingConfirm] = useState<(() => void) | null>(null); // DARKPACK EDIT ADD - for popups + // DARKPACK EDIT START + const whitelistSet = new Set(data.player_whitelists || []); + const isTrusted = whitelistSet.has('trusted'); + // DARKPACK EDIT END const serverData = useServerPrefs(); @@ -597,60 +627,13 @@ export function MainPage(props: MainPageProps) { )} {/* DARKPACK EDIT START - popup for clan, age, etc. changes */} {pendingConfirm !== null && ( - { + pendingConfirm?.(); + setPendingConfirm(null); }} - > - - - Change Character Details? - - - Changing significant character details (Clan, age, etc.) will wipe ALL of your existing disciplines. - This cannot be undone. Are you sure? - - - - - - - - - - - + onCancel={() => setPendingConfirm(null)} + /> )} {/* DARKPACK EDIT END*/} @@ -732,7 +715,14 @@ export function MainPage(props: MainPageProps) { } : baseSelect; // DARKPACK EDIT END - + // DARKPACK EDIT START - lock icons for whitelisted clans + const isChoiceLocked = clothingKey === 'vampire_clan' + ? (choice: string) => { + const whitelistId = TRUSTED_CLAN_WHITELIST_IDS[choice]; + return !!whitelistId && !isTrusted && !whitelistSet.has(whitelistId); + } + : undefined; + // DARKPACK EDIT END return ( {!catalog ? ( @@ -745,6 +735,7 @@ export function MainPage(props: MainPageProps) { handleSelect={handleSelect} randomization={randomizationOfMainFeatures[clothingKey]} setRandomization={createSetRandomization(clothingKey)} + isLocked={isChoiceLocked} // DARKPACK EDIT ADD /> )} diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferences/SplatsPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferences/SplatsPage.tsx index aa6cca155333..dad505685942 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferences/SplatsPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferences/SplatsPage.tsx @@ -1,6 +1,8 @@ // THIS IS A DARKPACK UI FILE +import { useState } from 'react'; // DARKPACK EDIT ADD import { useBackend } from 'tgui/backend'; +import { ConfirmModal } from '../components/ConfirmModal'; // DARKPACK EDIT ADD import { BlockQuote, Box, @@ -112,6 +114,10 @@ type SplatsPageInnerProps = { function SplatsPageInner(props: SplatsPageInnerProps) { const { act, data } = useBackend(); const setSplats = createSetPreference(act, 'splats'); + // DARKPACK EDIT START + const [pendingConfirm, setPendingConfirm] = useState<(() => void) | null>(null,); + const whitelistSet = new Set(data.player_whitelists || []); + // DARKPACK EDIT END const splats: [string, Splats][] = Object.entries(props.splats).map( ([splats, data]) => { @@ -142,24 +148,51 @@ function SplatsPageInner(props: SplatsPageInnerProps) { {splats.map(([splatsKey, splats]) => { + const isLocked = !!splatsKey && !whitelistSet.has(splatsKey); return ( ); })} @@ -214,6 +247,17 @@ function SplatsPageInner(props: SplatsPageInnerProps) { + {/* DARKPACK EDIT START - confirm dialog for splat changes */} + {pendingConfirm !== null && ( + { + pendingConfirm?.(); + setPendingConfirm(null); + }} + onCancel={() => setPendingConfirm(null)} + /> + )} + {/* DARKPACK EDIT END */} ); } diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/components/ConfirmModal.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/components/ConfirmModal.tsx new file mode 100644 index 000000000000..56b5726e027d --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/components/ConfirmModal.tsx @@ -0,0 +1,62 @@ +// THIS IS A DARKPACK UI FILE + +import { Box, Button, Stack } from 'tgui-core/components'; + +const DEFAULT_BODY = "Changing significant character details (Clan, age, etc.) will wipe ALL of your existing disciplines. This cannot be undone. Are you sure?"; + +type Props = { + title?: string; + body?: string; + onConfirm: () => void; + onCancel: () => void; +}; + +export function ConfirmModal(props: Props) { + const { title = 'Change Character Details?', body = DEFAULT_BODY, onConfirm, onCancel } = props; + + return ( + + + + {title} + + + {body} + + + + + + + + + + + + ); +} diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/darkpack_height.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/darkpack_height.tsx index def14e845419..9666f8bcce2e 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/darkpack_height.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/darkpack_height.tsx @@ -1,23 +1,62 @@ // THIS IS A DARKPACK UI FILE -import { type FeatureNumeric, type FeatureValueProps, type FeatureNumericData, FeatureNumberInput } from '../base'; -import { Stack, Box } from 'tgui-core/components'; - +// DARKPACK EDIT START +import { useState } from 'react'; +import { Button, Slider, Stack } from 'tgui-core/components'; +import { type FeatureNumeric, type FeatureValueProps, type FeatureNumericData } from '../base'; +// DARKPACK EDIT END type HeightServerData = FeatureNumericData & { height_labels: Record; }; +// DARKPACK EDIT START - height slider v2 +// on the back end, the height value 6 is around 4'10, which is 58 inches according to google so thats our baseline +const minimum_height = 58; // // DARKPACK EDIT - heights are integers on the back end, which sucks, but we cope by doing math on the front end i guess function HeightInput(props: FeatureValueProps) { + const { serverData, handleSetValue, value } = props; + const min = serverData?.minimum ?? 6; + const max = serverData?.maximum ?? 18; + const serverStep = serverData?.step ?? 2; // increments of 2. theres only 7 'real' heights, or height values that change the sprite + const inchSpan = serverData ? (max - min) * serverStep : 24; // all possible front end values, where only half are actual back end values. its written like this in-case some stinker goes and changes one of the height values, it doesnt brick saves + const [displayInch, setDisplayInch] = useState((value - min) * serverStep); + + // setting the (invisible, remove format() if you need this to make sense) slider value to 4 is floor(4/4)*2+6 and turns into a back end value of 8, or 5'2" on the front end. i think that's math:tm:. please dont judge me if im doing this wrong + // setting it to 8 is floor(8/4)*2+6, or 10 on the back end (which is a defined height value!!), front end is 5'6". bc theres a back end value on the new number, we update the sprite! yippee! + const snapToBackend = (inch: number) => min + Math.floor(inch / (serverStep * serverStep)) * serverStep; + const inches = minimum_height + displayInch; + const label = `${Math.floor(inches / 12)}'${inches % 12}"`; + const adjust = (delta: number) => { + const next = Math.max(0, Math.min(inchSpan, displayInch + delta)); + setDisplayInch(next); + handleSetValue(snapToBackend(next)); + }; return ( - - - - - - {props.serverData?.height_labels[props.value]} - - + <> + + +