"
- if(job.title in blacklisted)
+ if(job_blacklisted(job.title))
continue
dat += "[job.title] | "
dat += "[job.current_positions]/[job.total_positions] | "
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index bd74dfc38bc37..dff208fc90471 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -220,6 +220,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark)
var/datum/job/J = SSjob.GetJob(job)
J.total_positions += 1
J.spawn_positions += 1
+ SSjob.job_manager_blacklisted -= J.title
/obj/effect/landmark/start/randommaint/backalley_doc
name = "Barber"
diff --git a/code/modules/client/preferences/middleware/jobs.dm b/code/modules/client/preferences/middleware/jobs.dm
index 428dccd96b3d3..aee46d2ad040e 100644
--- a/code/modules/client/preferences/middleware/jobs.dm
+++ b/code/modules/client/preferences/middleware/jobs.dm
@@ -22,6 +22,9 @@
if (isnull(job))
return FALSE
+ if(job.lock_flags)
+ return FALSE
+
if (job.faction != "Station")
return FALSE
@@ -40,6 +43,8 @@
for (var/datum/job/job as anything in SSjob.occupations)
if(!job.show_in_prefs)
continue
+ if(job.lock_flags & ~JOB_LOCK_REASON_MAP) // anything but map reason shouldn't be visible
+ continue
var/department_flag = job.department_for_prefs
if (isnull(department_flag))
@@ -63,6 +68,7 @@
departments[department_name] = list()
jobs[job.title] = list(
+ "lock_reason" = job.get_lock_reason(),
"description" = job.description,
"department" = department_name,
)
diff --git a/code/modules/client/preferences/submodules/preference_jobs.dm b/code/modules/client/preferences/submodules/preference_jobs.dm
index e1785a6dfbee1..26deb7b3684b7 100644
--- a/code/modules/client/preferences/submodules/preference_jobs.dm
+++ b/code/modules/client/preferences/submodules/preference_jobs.dm
@@ -1,5 +1,5 @@
/datum/preferences/proc/set_job_preference_level(datum/job/job, level)
- if (!job)
+ if (!job || job.lock_flags)
return FALSE
log_preferences("[parent?.ckey]: Set [job.title] preference to level [level].")
diff --git a/code/modules/events/bureaucratic_error.dm b/code/modules/events/bureaucratic_error.dm
index d4c7512c27634..d980f3ddc5f5c 100644
--- a/code/modules/events/bureaucratic_error.dm
+++ b/code/modules/events/bureaucratic_error.dm
@@ -6,9 +6,21 @@
/datum/round_event/bureaucratic_error
announceWhen = 1
+ var/datum/job/chosen_job
+
+/datum/round_event/bureaucratic_error/setup()
+ var/error_count = 10
+ while(error_count--)
+ var/datum/job/J = SSjob.GetJob(pick(get_all_jobs()))
+ if(!J || J.lock_flags)
+ continue
+ chosen_job = J
+ break
+ if(!chosen_job)
+ return kill()
/datum/round_event/bureaucratic_error/announce(fake)
priority_announce("A recent bureaucratic error in the Organic Resources Department may result in personnel shortages in some departments and redundant staffing in others.", "Paperwork Mishap Alert", SSstation.announcer.get_rand_alert_sound())
/datum/round_event/bureaucratic_error/start()
- SSjob.set_overflow_role(pick(get_all_jobs()))
+ SSjob.set_overflow_role(chosen_job)
diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm
index 5edb7efb78ba6..480f35287ad05 100644
--- a/code/modules/jobs/job_types/_job.dm
+++ b/code/modules/jobs/job_types/_job.dm
@@ -20,6 +20,9 @@
var/flag = NONE //Deprecated //Except not really, still used throughout the codebase
var/auto_deadmin_role_flags = NONE
+ /// flags with the job lock reasons. If this flag exists, it's not available anyway.
+ var/lock_flags = NONE
+
/// If this job should show in the preferences menu
var/show_in_prefs = TRUE
@@ -120,6 +123,13 @@
lightup_areas = typecacheof(lightup_areas)
minimal_lightup_areas = typecacheof(minimal_lightup_areas)
+ if(!config_check())
+ lock_flags |= JOB_LOCK_REASON_CONFIG
+ if(!map_check())
+ lock_flags |= JOB_LOCK_REASON_MAP
+ if(lock_flags || gimmick)
+ SSjob.job_manager_blacklisted |= title
+
/// Only override this proc, unless altering loadout code. Loadouts act on H but get info from M
/// H is usually a human unless an /equip override transformed it
/// do actions on H but send messages to M as the key may not have been transferred_yet
@@ -331,6 +341,16 @@
/datum/job/proc/map_check()
return TRUE
+/datum/job/proc/get_lock_reason()
+ if(lock_flags & JOB_LOCK_REASON_ABSTRACT)
+ return "Not a real job"
+ else if(lock_flags & JOB_LOCK_REASON_CONFIG)
+ return "Disabled by server configuration"
+ else if(lock_flags & JOB_LOCK_REASON_MAP)
+ return "Not available on this map"
+ else if(lock_flags) // somehow flag exists
+ return "Unknown: [lock_flags]"
+
/datum/job/proc/radio_help_message(mob/M)
to_chat(M, "Prefix your message with :h to speak on your department's radio. To see other prefixes, look closely at your headset.")
diff --git a/code/modules/jobs/job_types/deputy.dm b/code/modules/jobs/job_types/deputy.dm
index 9859642dd8236..c214eda6a025b 100644
--- a/code/modules/jobs/job_types/deputy.dm
+++ b/code/modules/jobs/job_types/deputy.dm
@@ -1,6 +1,7 @@
/datum/job/deputy
title = JOB_NAME_DEPUTY
description = "Follow orders and do your best to maintain order on the station while following Space Law."
+ lock_flags = JOB_LOCK_REASON_ABSTRACT
department_for_prefs = DEPT_BITFLAG_SEC
department_head = list(JOB_NAME_HEADOFSECURITY)
supervisors = "the head of security"
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 260f899f5a3ba..5a7a0f96e91f9 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -261,12 +261,16 @@
return "Your account is not old enough for [jobtitle]."
if(JOB_UNAVAILABLE_SLOTFULL)
return "[jobtitle] is already filled to capacity."
+ if(JOB_UNAVAILABLE_LOCKED)
+ return "[jobtitle] is locked by the system."
return "Error: Unknown job availability."
/mob/dead/new_player/proc/IsJobUnavailable(rank, latejoin = FALSE)
var/datum/job/job = SSjob.GetJob(rank)
if(!job)
return JOB_UNAVAILABLE_GENERIC
+ if(job.lock_flags)
+ return JOB_UNAVAILABLE_LOCKED
if((job.current_positions >= job.total_positions) && job.total_positions != -1)
if(job.title == JOB_NAME_ASSISTANT)
if(isnum_safe(client.player_age) && client.player_age <= 14) //Newbies can always be assistants
diff --git a/code/modules/modular_computers/file_system/programs/jobmanagement.dm b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
index 808a953c51994..c6b428dc63da1 100644
--- a/code/modules/modular_computers/file_system/programs/jobmanagement.dm
+++ b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
@@ -10,22 +10,7 @@
tgui_id = "NtosJobManager"
program_icon = "address-book"
-
-
var/change_position_cooldown = 30
- //Jobs you cannot open new positions for
- var/list/blacklisted = list(
- JOB_NAME_AI,
- JOB_NAME_ASSISTANT,
- JOB_NAME_CYBORG,
- JOB_NAME_CAPTAIN,
- JOB_NAME_HEADOFPERSONNEL,
- JOB_NAME_HEADOFSECURITY,
- JOB_NAME_CHIEFENGINEER,
- JOB_NAME_RESEARCHDIRECTOR,
- JOB_NAME_CHIEFMEDICALOFFICER,
- JOB_NAME_BRIGPHYSICIAN,
- JOB_NAME_DEPUTY)
//The scaling factor of max total positions in relation to the total amount of people on board the station in %
var/max_relative_positions = 30 //30%: Seems reasonable, limit of 6 @ 20 players
@@ -38,8 +23,11 @@
..()
change_position_cooldown = CONFIG_GET(number/id_console_jobslot_delay)
+/datum/computer_file/program/proc/job_blacklisted(jobtitle)
+ return jobtitle == SSjob.overflow_role ? TRUE : (jobtitle in SSjob.job_manager_blacklisted)
+
/datum/computer_file/program/job_management/proc/can_open_job(datum/job/job)
- if(!(job?.title in blacklisted))
+ if(!job_blacklisted(job?.title))
if((job.total_positions <= length(GLOB.player_list) * (max_relative_positions / 100)))
var/delta = (world.time / 10) - GLOB.time_last_changed_position
if((change_position_cooldown < delta) || (opened_positions[job.title] < 0))
@@ -47,7 +35,7 @@
return FALSE
/datum/computer_file/program/job_management/proc/can_close_job(datum/job/job)
- if(!(job?.title in blacklisted))
+ if(!job_blacklisted(job?.title))
if(job.total_positions > length(GLOB.player_list) * (max_relative_positions / 100))
var/delta = (world.time / 10) - GLOB.time_last_changed_position
if((change_position_cooldown < delta) || (opened_positions[job.title] > 0))
@@ -120,7 +108,7 @@
var/list/pos = list()
for(var/j in SSjob.occupations)
var/datum/job/job = j
- if(job.title in blacklisted)
+ if(job_blacklisted(job.title))
continue
pos += list(list(
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx
index 0ee2532a74498..2f5818b2794eb 100644
--- a/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx
@@ -134,10 +134,19 @@ const JobRow = (
const experienceNeeded = data.job_required_experience && data.job_required_experience[name];
const daysLeft = data.job_days_left ? data.job_days_left[name] : 0;
+ const lockReason = job.lock_reason;
let rightSide: InfernoNode;
- if (experienceNeeded) {
+ if (lockReason) {
+ rightSide = (
+
+
+ {lockReason}
+
+
+ );
+ } else if (experienceNeeded) {
const { experience_type, required_playtime } = experienceNeeded;
const hoursNeeded = Math.ceil(required_playtime / 60);
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts
index b87cfd8c1e22a..86a88ad87de69 100644
--- a/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts
@@ -70,6 +70,7 @@ export type Department = {
export type Job = {
description: string;
department: string;
+ lock_reason: string;
};
export type Quirk = {