From 38bbfae31b1fe845bfb709e81184ed516c503a9c Mon Sep 17 00:00:00 2001 From: EvilDragonfiend <87972842+EvilDragonfiend@users.noreply.github.com> Date: Sun, 19 May 2024 15:38:13 +0900 Subject: [PATCH] Fixes a blacklisted job is forcefully removed from the game (#10976) * fixes * Don't show config option * changes var name to job_manager_blacklisted * Forgot to remove * Minor touch to lock_reason proc --- code/__DEFINES/jobs.dm | 7 ++ code/controllers/subsystem/job.dm | 77 +++++++++++-------- code/game/machinery/computer/card.dm | 26 ++----- code/game/objects/effects/landmarks.dm | 1 + .../client/preferences/middleware/jobs.dm | 6 ++ .../preferences/submodules/preference_jobs.dm | 2 +- code/modules/events/bureaucratic_error.dm | 14 +++- code/modules/jobs/job_types/_job.dm | 20 +++++ code/modules/jobs/job_types/deputy.dm | 1 + .../modules/mob/dead/new_player/new_player.dm | 4 + .../file_system/programs/jobmanagement.dm | 24 ++---- .../interfaces/PreferencesMenu/JobsPage.tsx | 11 ++- .../tgui/interfaces/PreferencesMenu/data.ts | 1 + 13 files changed, 123 insertions(+), 71 deletions(-) diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm index 7cacff57a2a97..7188b23913772 100644 --- a/code/__DEFINES/jobs.dm +++ b/code/__DEFINES/jobs.dm @@ -4,6 +4,13 @@ #define JOB_UNAVAILABLE_PLAYTIME 3 #define JOB_UNAVAILABLE_ACCOUNTAGE 4 #define JOB_UNAVAILABLE_SLOTFULL 5 +#define JOB_UNAVAILABLE_LOCKED 6 + +// reasons why you can't play this job +#define JOB_LOCK_REASON_ABSTRACT (1<<0) +#define JOB_LOCK_REASON_MAP (1<<1) +#define JOB_LOCK_REASON_CONFIG (1<<2) + #define DEFAULT_RELIGION "Christianity" #define DEFAULT_DEITY "Space Jesus" diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index 9b32998314a0b..4a6f27c1bd316 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -30,6 +30,21 @@ SUBSYSTEM_DEF(job) var/list/crew_obj_list = list() var/list/crew_obj_jobs = list() + /// jobs that are not allowed in HoP job manager + var/list/job_manager_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, + JOB_NAME_GIMMICK) + /datum/controller/subsystem/job/Initialize(timeofday) SSmapping.HACK_LoadMapConfig() if(!occupations.len) @@ -53,8 +68,28 @@ SUBSYSTEM_DEF(job) return ..() +/datum/controller/subsystem/job/Recover() + occupations = SSjob.occupations + name_occupations = SSjob.name_occupations + type_occupations = SSjob.type_occupations + unassigned = SSjob.unassigned + initial_players_to_assign = SSjob.initial_players_to_assign + + prioritized_jobs = SSjob.prioritized_jobs + latejoin_trackers = SSjob.latejoin_trackers + + overflow_role = SSjob.overflow_role + + spare_id_safe_code = SSjob.spare_id_safe_code + crew_obj_list = SSjob.crew_obj_list + crew_obj_jobs = SSjob.crew_obj_jobs + + job_manager_blacklisted = SSjob.job_manager_blacklisted + /datum/controller/subsystem/job/proc/set_overflow_role(new_overflow_role) var/datum/job/new_overflow = GetJob(new_overflow_role) + if(!new_overflow || new_overflow.lock_flags) + CRASH("[new_overflow_role] was used for an overflow role, but it's not allowed. BITFLAG: [new_overflow?.lock_flags]") var/cap = CONFIG_GET(number/overflow_cap) new_overflow.allow_bureaucratic_error = FALSE @@ -76,20 +111,13 @@ SUBSYSTEM_DEF(job) to_chat(world, "Error setting up jobs, no job datums found.") return 0 - for(var/J in all_jobs) - var/datum/job/job = new J() - if(!job) - continue - if(job.faction != faction) + for(var/datum/job/each_job as anything in all_jobs) + each_job = new each_job() + if(each_job.faction != faction) continue - if(!job.config_check()) - continue - if(!job.map_check()) //Even though we initialize before mapping, this is fine because the config is loaded at new - testing("Removed [job.type] due to map config") - continue - occupations += job - name_occupations[job.title] = job - type_occupations[J] = job + occupations += each_job + name_occupations[each_job.title] = each_job + type_occupations[each_job.type] = each_job return 1 @@ -128,7 +156,7 @@ SUBSYSTEM_DEF(job) JobDebug("Running AR, Player: [player], Rank: [rank], LJ: [latejoin]") if(player?.mind && rank) var/datum/job/job = GetJob(rank) - if(!job) + if(!job || job.lock_flags) return FALSE if(QDELETED(player) || is_banned_from(player.ckey, rank)) return FALSE @@ -181,7 +209,7 @@ SUBSYSTEM_DEF(job) JobDebug("GRJ Giving random job, Player: [player]") . = FALSE for(var/datum/job/job in shuffle(occupations)) - if(!job) + if(!job || job.lock_flags) continue if(istype(job, GetJob(SSjob.overflow_role))) // We don't want to give him assistant, that's boring! @@ -368,7 +396,7 @@ SUBSYSTEM_DEF(job) // Loop through all jobs for(var/datum/job/job in shuffledoccupations) // SHUFFLE ME BABY - if(!job) + if(!job || job.lock_flags) continue if(is_banned_from(player.ckey, job.title)) @@ -612,6 +640,8 @@ SUBSYSTEM_DEF(job) var/banned = 0 //banned var/young = 0 //account too young for(var/mob/dead/new_player/player in GLOB.player_list) + if(job.lock_flags) + continue if(!(player.ready == PLAYER_READY_TO_PLAY && player.mind && !player.mind.assigned_role)) continue //This player is not ready if(is_banned_from(player.ckey, job.title) || QDELETED(player)) @@ -659,21 +689,6 @@ SUBSYSTEM_DEF(job) player.ready = PLAYER_NOT_READY -/datum/controller/subsystem/job/Recover() - set waitfor = FALSE - var/oldjobs = SSjob.occupations - sleep(20) - for (var/datum/job/J in oldjobs) - INVOKE_ASYNC(src, PROC_REF(RecoverJob), J) - -/datum/controller/subsystem/job/proc/RecoverJob(datum/job/J) - var/datum/job/newjob = GetJob(J.title) - if (!istype(newjob)) - return - newjob.total_positions = J.total_positions - newjob.spawn_positions = J.spawn_positions - newjob.current_positions = J.current_positions - /atom/proc/JoinPlayerHere(mob/M, buckle) // By default, just place the mob on the same turf as the marker or whatever. M.forceMove(get_turf(src)) diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm index 61beeb791139b..1c5b455e1a9df 100644 --- a/code/game/machinery/computer/card.dm +++ b/code/game/machinery/computer/card.dm @@ -29,26 +29,13 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) //if set to -1: No cooldown... probably a bad idea //if set to 0: Not able to close "original" positions. You can only close positions that you have opened before 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 //This is used to keep track of opened positions for jobs to allow instant closing //Assoc array: "JobName" = (int) - var/list/opened_positions = list(); + var/list/opened_positions = list() var/obj/item/card/id/inserted_scan_id var/obj/item/card/id/inserted_modify_id var/list/region_access = null @@ -60,9 +47,6 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) /obj/machinery/computer/card/Initialize(mapload) . = ..() change_position_cooldown = CONFIG_GET(number/id_console_jobslot_delay) - for(var/G in typesof(/datum/job/gimmick)) - var/datum/job/gimmick/J = new G - blacklisted += J.title // This determines which department payment list the console will show to you. if(!target_dept) @@ -127,7 +111,11 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) //Check if you can't open a new position for a certain job /obj/machinery/computer/card/proc/job_blacklisted(jobtitle) - return (jobtitle in blacklisted) + return jobtitle == SSjob.overflow_role ? TRUE : (jobtitle in SSjob.job_manager_blacklisted) + +// CentCom is powerful +/obj/machinery/computer/card/centcom/job_blacklisted(jobtitle) + return jobtitle == SSjob.overflow_role ? TRUE : FALSE //Logic check for Topic() if you can open the job /obj/machinery/computer/card/proc/can_open_job(datum/job/job) @@ -241,7 +229,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) ID = 0 for(var/datum/job/job in SSjob.occupations) dat += "" - 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 = {