|
|
|
@@ -22,7 +22,7 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
|
|
|
|
|
var/list/unassigned = list() //Players who need jobs
|
|
|
|
|
var/initial_players_to_assign = 0 //used for checking against population caps
|
|
|
|
|
// Whether to run divide_occupations pure so that there are no side-effects from calling it other than
|
|
|
|
|
// Whether to run DivideOccupations pure so that there are no side-effects from calling it other than
|
|
|
|
|
// a player's assigned_role being set to some value.
|
|
|
|
|
var/run_divide_occupation_pure = FALSE
|
|
|
|
|
|
|
|
|
@@ -31,7 +31,7 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
|
|
|
|
|
var/overflow_role = /datum/job/assistant
|
|
|
|
|
|
|
|
|
|
var/list/level_order = list(JP_HIGH, JP_MEDIUM, JP_LOW)
|
|
|
|
|
var/list/level_order = list(JP_HIGH,JP_MEDIUM,JP_LOW)
|
|
|
|
|
|
|
|
|
|
/// Lazylist of mob:occupation_string pairs.
|
|
|
|
|
var/list/dynamic_forced_occupations
|
|
|
|
@@ -88,7 +88,7 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
setup_job_lists()
|
|
|
|
|
job_config_datum_singletons = generate_config_singletons() // we set this up here regardless in case someone wants to use the verb to generate the config file.
|
|
|
|
|
if(!length(all_occupations))
|
|
|
|
|
setup_occupations()
|
|
|
|
|
SetupOccupations()
|
|
|
|
|
if(CONFIG_GET(flag/load_jobs_from_txt))
|
|
|
|
|
load_jobs_from_config()
|
|
|
|
|
set_overflow_role(CONFIG_GET(string/overflow_job)) // this must always go after load_jobs_from_config() due to how the legacy systems operate, this always takes precedent.
|
|
|
|
@@ -108,9 +108,9 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
return overflow_jobs
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/job/proc/set_overflow_role(new_overflow_role)
|
|
|
|
|
var/datum/job/new_overflow = ispath(new_overflow_role) ? get_job_type(new_overflow_role) : get_job(new_overflow_role)
|
|
|
|
|
var/datum/job/new_overflow = ispath(new_overflow_role) ? GetJobType(new_overflow_role) : GetJob(new_overflow_role)
|
|
|
|
|
if(!new_overflow)
|
|
|
|
|
job_debug("SET_OVRFLW: Failed to set new overflow role: [new_overflow_role]")
|
|
|
|
|
JobDebug("Failed to set new overflow role: [new_overflow_role]")
|
|
|
|
|
CRASH("set_overflow_role failed | new_overflow_role: [isnull(new_overflow_role) ? "null" : new_overflow_role]")
|
|
|
|
|
var/cap = CONFIG_GET(number/overflow_cap)
|
|
|
|
|
|
|
|
|
@@ -121,16 +121,17 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
|
|
|
|
|
if(new_overflow.type == overflow_role)
|
|
|
|
|
return
|
|
|
|
|
var/datum/job/old_overflow = get_job_type(overflow_role)
|
|
|
|
|
var/datum/job/old_overflow = GetJobType(overflow_role)
|
|
|
|
|
old_overflow.allow_bureaucratic_error = initial(old_overflow.allow_bureaucratic_error)
|
|
|
|
|
old_overflow.spawn_positions = initial(old_overflow.spawn_positions)
|
|
|
|
|
old_overflow.total_positions = initial(old_overflow.total_positions)
|
|
|
|
|
if(!(initial(old_overflow.job_flags) & JOB_CANNOT_OPEN_SLOTS))
|
|
|
|
|
old_overflow.job_flags &= ~JOB_CANNOT_OPEN_SLOTS
|
|
|
|
|
overflow_role = new_overflow.type
|
|
|
|
|
job_debug("SET_OVRFLW: Overflow role set to: [new_overflow.type]")
|
|
|
|
|
JobDebug("Overflow role set to : [new_overflow.type]")
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/job/proc/setup_occupations()
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/job/proc/SetupOccupations()
|
|
|
|
|
name_occupations = list()
|
|
|
|
|
type_occupations = list()
|
|
|
|
|
|
|
|
|
@@ -204,20 +205,20 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
return TRUE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/job/proc/get_job(rank)
|
|
|
|
|
/datum/controller/subsystem/job/proc/GetJob(rank)
|
|
|
|
|
if(!length(all_occupations))
|
|
|
|
|
setup_occupations()
|
|
|
|
|
SetupOccupations()
|
|
|
|
|
return name_occupations[rank]
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/job/proc/get_job_type(jobtype)
|
|
|
|
|
/datum/controller/subsystem/job/proc/GetJobType(jobtype)
|
|
|
|
|
RETURN_TYPE(/datum/job)
|
|
|
|
|
if(!length(all_occupations))
|
|
|
|
|
setup_occupations()
|
|
|
|
|
SetupOccupations()
|
|
|
|
|
return type_occupations[jobtype]
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/job/proc/get_department_type(department_type)
|
|
|
|
|
if(!length(all_occupations))
|
|
|
|
|
setup_occupations()
|
|
|
|
|
SetupOccupations()
|
|
|
|
|
return joinable_departments_by_type[department_type]
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@@ -229,92 +230,89 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
* * latejoin - Set to TRUE if this is a latejoin role assignment.
|
|
|
|
|
* * do_eligibility_checks - Set to TRUE to conduct all job eligibility tests and reject on failure. Set to FALSE if job eligibility has been tested elsewhere and they can be safely skipped.
|
|
|
|
|
*/
|
|
|
|
|
/datum/controller/subsystem/job/proc/assign_role(mob/dead/new_player/player, datum/job/job, latejoin = FALSE, do_eligibility_checks = TRUE)
|
|
|
|
|
job_debug("AR: Running, Player: [player], Job: [isnull(job) ? "null" : job], LateJoin: [latejoin]")
|
|
|
|
|
/datum/controller/subsystem/job/proc/AssignRole(mob/dead/new_player/player, datum/job/job, latejoin = FALSE, do_eligibility_checks = TRUE)
|
|
|
|
|
JobDebug("Running AR, Player: [player], Job: [isnull(job) ? "null" : job], LateJoin: [latejoin]")
|
|
|
|
|
if(!player?.mind || !job)
|
|
|
|
|
job_debug("AR: Failed, player has no mind or job is null. Player: [player], Rank: [isnull(job) ? "null" : job.type]")
|
|
|
|
|
JobDebug("AR has failed, player has no mind or job is null, Player: [player], Rank: [isnull(job) ? "null" : job.type]")
|
|
|
|
|
return FALSE
|
|
|
|
|
|
|
|
|
|
if(do_eligibility_checks && (check_job_eligibility(player, job, "AR", add_job_to_log = TRUE) != JOB_AVAILABLE))
|
|
|
|
|
return FALSE
|
|
|
|
|
|
|
|
|
|
job_debug("AR: Role now set and assigned - [player] is [job.title], JCP:[job.current_positions], JPL:[latejoin ? job.total_positions : job.spawn_positions]")
|
|
|
|
|
JobDebug("Player: [player] is now Rank: [job.title], JCP:[job.current_positions], JPL:[latejoin ? job.total_positions : job.spawn_positions]")
|
|
|
|
|
player.mind.set_assigned_role(job)
|
|
|
|
|
unassigned -= player
|
|
|
|
|
job.current_positions++
|
|
|
|
|
return TRUE
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/job/proc/find_occupation_candidates(datum/job/job, level = 0)
|
|
|
|
|
job_debug("FOC: Now running, Job: [job], Level: [job_priority_level_to_string(level)]")
|
|
|
|
|
/datum/controller/subsystem/job/proc/FindOccupationCandidates(datum/job/job, level)
|
|
|
|
|
JobDebug("Running FOC, Job: [job], Level: [job_priority_level_to_string(level)]")
|
|
|
|
|
var/list/candidates = list()
|
|
|
|
|
for(var/mob/dead/new_player/player in unassigned)
|
|
|
|
|
if(!player)
|
|
|
|
|
job_debug("FOC: Player no longer exists.")
|
|
|
|
|
JobDebug("FOC player no longer exists.")
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
if(!player.client)
|
|
|
|
|
job_debug("FOC: Player client no longer exists, Player: [player]")
|
|
|
|
|
JobDebug("FOC player client no longer exists, Player: [player]")
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
// Initial screening check. Does the player even have the job enabled, if they do - Is it at the correct priority level?
|
|
|
|
|
var/player_job_level = player.client?.prefs.job_preferences[job.title]
|
|
|
|
|
if(isnull(player_job_level))
|
|
|
|
|
job_debug("FOC: Player job not enabled, Player: [player]")
|
|
|
|
|
JobDebug("FOC player job not enabled, Player: [player]")
|
|
|
|
|
continue
|
|
|
|
|
else if(player_job_level != level)
|
|
|
|
|
JobDebug("FOC player job enabled at wrong level, Player: [player], TheirLevel: [job_priority_level_to_string(player_job_level)], ReqLevel: [job_priority_level_to_string(level)]")
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
if(level && (player_job_level != level))
|
|
|
|
|
job_debug("FOC: Player job enabled at wrong level, Player: [player], TheirLevel: [job_priority_level_to_string(player_job_level)], ReqLevel: [job_priority_level_to_string(level)]")
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
// This check handles its own output to job_debug.
|
|
|
|
|
// This check handles its own output to JobDebug.
|
|
|
|
|
if(check_job_eligibility(player, job, "FOC", add_job_to_log = FALSE) != JOB_AVAILABLE)
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
// They have the job enabled, at this priority level, with no restrictions applying to them.
|
|
|
|
|
job_debug("FOC: Player eligible, Player: [player], Level: [job_priority_level_to_string(level)]")
|
|
|
|
|
JobDebug("FOC pass, Player: [player], Level: [job_priority_level_to_string(level)]")
|
|
|
|
|
candidates += player
|
|
|
|
|
return candidates
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/job/proc/give_random_job(mob/dead/new_player/player)
|
|
|
|
|
job_debug("GRJ: Giving random job, Player: [player]")
|
|
|
|
|
/datum/controller/subsystem/job/proc/GiveRandomJob(mob/dead/new_player/player)
|
|
|
|
|
JobDebug("GRJ Giving random job, Player: [player]")
|
|
|
|
|
. = FALSE
|
|
|
|
|
for(var/datum/job/job as anything in shuffle(joinable_occupations))
|
|
|
|
|
if(QDELETED(player))
|
|
|
|
|
job_debug("GRJ: Player is deleted, aborting")
|
|
|
|
|
JobDebug("GRJ player is deleted, aborting")
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
if((job.current_positions >= job.spawn_positions) && job.spawn_positions != -1)
|
|
|
|
|
job_debug("GRJ: Job lacks spawn positions to be eligible, Player: [player], Job: [job]")
|
|
|
|
|
JobDebug("GRJ job lacks spawn positions to be eligible, Player: [player], Job: [job]")
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
if(istype(job, get_job_type(overflow_role))) // We don't want to give him assistant, that's boring!
|
|
|
|
|
job_debug("GRJ: Skipping overflow role, Player: [player], Job: [job]")
|
|
|
|
|
if(istype(job, GetJobType(overflow_role))) // We don't want to give him assistant, that's boring!
|
|
|
|
|
JobDebug("GRJ skipping overflow role, Player: [player], Job: [job]")
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
if(job.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND) //If you want a command position, select it!
|
|
|
|
|
job_debug("GRJ: Skipping command role, Player: [player], Job: [job]")
|
|
|
|
|
JobDebug("GRJ skipping command role, Player: [player], Job: [job]")
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
// This check handles its own output to job_debug.
|
|
|
|
|
// This check handles its own output to JobDebug.
|
|
|
|
|
if(check_job_eligibility(player, job, "GRJ", add_job_to_log = TRUE) != JOB_AVAILABLE)
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
if(assign_role(player, job, do_eligibility_checks = FALSE))
|
|
|
|
|
job_debug("GRJ: Random job given, Player: [player], Job: [job]")
|
|
|
|
|
if(AssignRole(player, job, do_eligibility_checks = FALSE))
|
|
|
|
|
JobDebug("GRJ Random job given, Player: [player], Job: [job]")
|
|
|
|
|
return TRUE
|
|
|
|
|
|
|
|
|
|
job_debug("GRJ: Player eligible but assign_role failed, Player: [player], Job: [job]")
|
|
|
|
|
JobDebug("GRJ Player eligible but AssignRole failed, Player: [player], Job: [job]")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/job/proc/reset_occupations()
|
|
|
|
|
job_debug("RO: Occupations reset.")
|
|
|
|
|
/datum/controller/subsystem/job/proc/ResetOccupations()
|
|
|
|
|
JobDebug("Occupations reset.")
|
|
|
|
|
for(var/mob/dead/new_player/player as anything in GLOB.new_player_list)
|
|
|
|
|
if(!player?.mind)
|
|
|
|
|
continue
|
|
|
|
|
player.mind.set_assigned_role(get_job_type(/datum/job/unassigned))
|
|
|
|
|
player.mind.set_assigned_role(GetJobType(/datum/job/unassigned))
|
|
|
|
|
player.mind.special_role = null
|
|
|
|
|
setup_occupations()
|
|
|
|
|
SetupOccupations()
|
|
|
|
|
unassigned = list()
|
|
|
|
|
if(CONFIG_GET(flag/load_jobs_from_txt))
|
|
|
|
|
// Any errors with the configs has already been said, we don't need to repeat them here.
|
|
|
|
@@ -323,11 +321,12 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Forces a random Head of Staff role to be assigned to a random eligible player.
|
|
|
|
|
* Returns TRUE if a player was selected and assigned the role. FALSE otherwise.
|
|
|
|
|
/**
|
|
|
|
|
* Will try to select a head, ignoring ALL non-head preferences for every level until.
|
|
|
|
|
*
|
|
|
|
|
* Basically tries to ensure there is at least one head in every shift if anyone has that job preference enabled at all.
|
|
|
|
|
*/
|
|
|
|
|
/datum/controller/subsystem/job/proc/force_one_head_assignment()
|
|
|
|
|
/datum/controller/subsystem/job/proc/FillHeadPosition()
|
|
|
|
|
var/datum/job_department/command_department = get_department_type(/datum/job_department/command)
|
|
|
|
|
if(!command_department)
|
|
|
|
|
return FALSE
|
|
|
|
@@ -335,65 +334,60 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
for(var/datum/job/job as anything in command_department.department_jobs)
|
|
|
|
|
if((job.current_positions >= job.total_positions) && job.total_positions != -1)
|
|
|
|
|
continue
|
|
|
|
|
var/list/candidates = find_occupation_candidates(job, level)
|
|
|
|
|
var/list/candidates = FindOccupationCandidates(job, level)
|
|
|
|
|
if(!candidates.len)
|
|
|
|
|
continue
|
|
|
|
|
var/mob/dead/new_player/candidate = pick(candidates)
|
|
|
|
|
// Eligibility checks done as part of find_occupation_candidates.
|
|
|
|
|
if(assign_role(candidate, job, do_eligibility_checks = FALSE))
|
|
|
|
|
// Eligibility checks done as part of FindOccupationCandidates.
|
|
|
|
|
if(AssignRole(candidate, job, do_eligibility_checks = FALSE))
|
|
|
|
|
return TRUE
|
|
|
|
|
return FALSE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Attempts to fill out all possible head positions for players with that job at a a given job priority level.
|
|
|
|
|
* Returns the number of Head positions assigned.
|
|
|
|
|
*
|
|
|
|
|
* Arguments:
|
|
|
|
|
* * level - One of the JP_LOW, JP_MEDIUM, JP_HIGH or JP_ANY defines. Attempts to find candidates with head jobs at that priority only.
|
|
|
|
|
* * level - One of the JP_LOW, JP_MEDIUM or JP_HIGH defines. Attempts to find candidates with head jobs at this priority only.
|
|
|
|
|
*/
|
|
|
|
|
/datum/controller/subsystem/job/proc/fill_all_head_positions_at_priority(level)
|
|
|
|
|
. = 0
|
|
|
|
|
/datum/controller/subsystem/job/proc/CheckHeadPositions(level)
|
|
|
|
|
var/datum/job_department/command_department = get_department_type(/datum/job_department/command)
|
|
|
|
|
|
|
|
|
|
if(!command_department)
|
|
|
|
|
return .
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
for(var/datum/job/job as anything in command_department.department_jobs)
|
|
|
|
|
if((job.current_positions >= job.total_positions) && job.total_positions != -1)
|
|
|
|
|
continue
|
|
|
|
|
var/list/candidates = find_occupation_candidates(job, level)
|
|
|
|
|
var/list/candidates = FindOccupationCandidates(job, level)
|
|
|
|
|
if(!candidates.len)
|
|
|
|
|
continue
|
|
|
|
|
var/mob/dead/new_player/candidate = pick(candidates)
|
|
|
|
|
// Eligibility checks done as part of find_occupation_candidates
|
|
|
|
|
if(assign_role(candidate, job, do_eligibility_checks = FALSE))
|
|
|
|
|
.++
|
|
|
|
|
// Eligibility checks done as part of FindOccupationCandidates
|
|
|
|
|
AssignRole(candidate, job, do_eligibility_checks = FALSE)
|
|
|
|
|
|
|
|
|
|
/// Attempts to fill out all available AI positions.
|
|
|
|
|
/datum/controller/subsystem/job/proc/fill_ai_positions()
|
|
|
|
|
var/datum/job/ai_job = get_job(JOB_AI)
|
|
|
|
|
var/datum/job/ai_job = GetJob(JOB_AI)
|
|
|
|
|
if(!ai_job)
|
|
|
|
|
return
|
|
|
|
|
// In byond for(in to) loops, the iteration is inclusive so we need to stop at ai_job.total_positions - 1
|
|
|
|
|
for(var/i in ai_job.current_positions to ai_job.total_positions - 1)
|
|
|
|
|
for(var/level in level_order)
|
|
|
|
|
var/list/candidates = list()
|
|
|
|
|
candidates = find_occupation_candidates(ai_job, level)
|
|
|
|
|
candidates = FindOccupationCandidates(ai_job, level)
|
|
|
|
|
if(candidates.len)
|
|
|
|
|
var/mob/dead/new_player/candidate = pick(candidates)
|
|
|
|
|
// Eligibility checks done as part of find_occupation_candidates
|
|
|
|
|
if(assign_role(candidate, get_job_type(/datum/job/ai), do_eligibility_checks = FALSE))
|
|
|
|
|
// Eligibility checks done as part of FindOccupationCandidates
|
|
|
|
|
if(AssignRole(candidate, GetJobType(/datum/job/ai), do_eligibility_checks = FALSE))
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Proc divide_occupations
|
|
|
|
|
/** Proc DivideOccupations
|
|
|
|
|
* fills var "assigned_role" for all ready players.
|
|
|
|
|
* This proc must not have any side effect besides of modifying "assigned_role".
|
|
|
|
|
**/
|
|
|
|
|
/datum/controller/subsystem/job/proc/divide_occupations(pure = FALSE, allow_all = FALSE)
|
|
|
|
|
/datum/controller/subsystem/job/proc/DivideOccupations(pure = FALSE, allow_all = FALSE)
|
|
|
|
|
//Setup new player list and get the jobs list
|
|
|
|
|
job_debug("DO: Running, allow_all = [allow_all], pure = [pure]")
|
|
|
|
|
JobDebug("Running DO, allow_all = [allow_all], pure = [pure]")
|
|
|
|
|
run_divide_occupation_pure = pure
|
|
|
|
|
SEND_SIGNAL(src, COMSIG_OCCUPATIONS_DIVIDED, pure, allow_all)
|
|
|
|
|
|
|
|
|
@@ -403,161 +397,162 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
if(player.ready == PLAYER_READY_TO_PLAY && player.check_preferences() && player.mind && is_unassigned_job(player.mind.assigned_role))
|
|
|
|
|
unassigned += player
|
|
|
|
|
|
|
|
|
|
initial_players_to_assign = length(unassigned)
|
|
|
|
|
initial_players_to_assign = unassigned.len
|
|
|
|
|
|
|
|
|
|
job_debug("DO: Player count to assign roles to: [initial_players_to_assign]")
|
|
|
|
|
JobDebug("DO, Len: [unassigned.len]")
|
|
|
|
|
|
|
|
|
|
//Scale number of open security officer slots to population
|
|
|
|
|
setup_officer_positions()
|
|
|
|
|
|
|
|
|
|
//Jobs will have fewer access permissions if the number of players exceeds the threshold defined in game_options.txt
|
|
|
|
|
var/min_access_threshold = CONFIG_GET(number/minimal_access_threshold)
|
|
|
|
|
if(min_access_threshold)
|
|
|
|
|
if(min_access_threshold > initial_players_to_assign)
|
|
|
|
|
var/mat = CONFIG_GET(number/minimal_access_threshold)
|
|
|
|
|
if(mat)
|
|
|
|
|
if(mat > unassigned.len)
|
|
|
|
|
CONFIG_SET(flag/jobs_have_minimal_access, FALSE)
|
|
|
|
|
else
|
|
|
|
|
CONFIG_SET(flag/jobs_have_minimal_access, TRUE)
|
|
|
|
|
|
|
|
|
|
//Shuffle player list.
|
|
|
|
|
shuffle_inplace(unassigned)
|
|
|
|
|
//Shuffle players and jobs
|
|
|
|
|
unassigned = shuffle(unassigned)
|
|
|
|
|
|
|
|
|
|
handle_feedback_gathering()
|
|
|
|
|
HandleFeedbackGathering()
|
|
|
|
|
|
|
|
|
|
// Assign any priority positions before all other standard job selections.
|
|
|
|
|
job_debug("DO: Assigning priority positions")
|
|
|
|
|
// Dynamic has picked a ruleset that requires enforcing some jobs before others.
|
|
|
|
|
JobDebug("DO, Assigning Priority Positions: [length(dynamic_forced_occupations)]")
|
|
|
|
|
assign_priority_positions()
|
|
|
|
|
job_debug("DO: Priority assignment complete")
|
|
|
|
|
|
|
|
|
|
// The overflow role has limitless slots, plus having the Overflow box ticked in prefs should (with one exception) set the priority to JP_HIGH.
|
|
|
|
|
// So everyone with overflow enabled will get that job. Thus we can assign it immediately to all players that have it enabled.
|
|
|
|
|
job_debug("DO: Assigning early overflow roles")
|
|
|
|
|
assign_all_overflow_positions()
|
|
|
|
|
job_debug("DO: Early overflow roles assigned.")
|
|
|
|
|
//People who wants to be the overflow role, sure, go on.
|
|
|
|
|
JobDebug("DO, Running Overflow Check 1")
|
|
|
|
|
var/datum/job/overflow_datum = GetJobType(overflow_role)
|
|
|
|
|
var/list/overflow_candidates = FindOccupationCandidates(overflow_datum, JP_LOW)
|
|
|
|
|
JobDebug("AC1, Candidates: [overflow_candidates.len]")
|
|
|
|
|
for(var/mob/dead/new_player/player in overflow_candidates)
|
|
|
|
|
JobDebug("AC1 pass, Player: [player]")
|
|
|
|
|
// Eligibility checks done as part of FindOccupationCandidates
|
|
|
|
|
AssignRole(player, GetJobType(overflow_role), do_eligibility_checks = FALSE)
|
|
|
|
|
overflow_candidates -= player
|
|
|
|
|
JobDebug("DO, AC1 end")
|
|
|
|
|
|
|
|
|
|
// At this point we can assume the following:
|
|
|
|
|
// From assign_priority_positions()
|
|
|
|
|
// 1. If possible, any necessary job roles to allow Dynamic rulesets to execute (such as an AI for malf AI) are satisfied.
|
|
|
|
|
// 2. All Head of Staff roles with any player pref set to JP_HIGH are filled out.
|
|
|
|
|
// 3. If any player not selected by the above has any Head of Staff preference enabled at any JP_ level, there is at least one Head of Staff.
|
|
|
|
|
//
|
|
|
|
|
// From assign_all_overflow_positions()
|
|
|
|
|
// 4. Anyone with the overflow role enabled has been given the overflow role.
|
|
|
|
|
//Select one head
|
|
|
|
|
JobDebug("DO, Running Head Check")
|
|
|
|
|
FillHeadPosition()
|
|
|
|
|
JobDebug("DO, Head Check end")
|
|
|
|
|
|
|
|
|
|
// Shuffle the joinable occupation list and filter out ineligible occupations due to above job assignments.
|
|
|
|
|
var/list/available_occupations = joinable_occupations.Copy()
|
|
|
|
|
for(var/datum/job/job in available_occupations)
|
|
|
|
|
// Make sure the job isn't filled. If it is, remove it from the list so it doesn't get checked.
|
|
|
|
|
if((job.current_positions >= job.spawn_positions) && job.spawn_positions != -1)
|
|
|
|
|
job_debug("DO: Job is now filled, Job: [job], Current: [job.current_positions], Limit: [job.spawn_positions]")
|
|
|
|
|
available_occupations -= job
|
|
|
|
|
// Fill out any remaining AI positions.
|
|
|
|
|
JobDebug("DO, Running AI Check")
|
|
|
|
|
fill_ai_positions()
|
|
|
|
|
JobDebug("DO, AI Check end")
|
|
|
|
|
|
|
|
|
|
job_debug("DO: Running standard job assignment")
|
|
|
|
|
//Other jobs are now checked
|
|
|
|
|
JobDebug("DO, Running standard job assignment")
|
|
|
|
|
// New job giving system by Donkie
|
|
|
|
|
// This will cause lots of more loops, but since it's only done once it shouldn't really matter much at all.
|
|
|
|
|
// Hopefully this will add more randomness and fairness to job giving.
|
|
|
|
|
|
|
|
|
|
// Loop through all levels from high to low
|
|
|
|
|
var/list/shuffledoccupations = shuffle(joinable_occupations)
|
|
|
|
|
for(var/level in level_order)
|
|
|
|
|
job_debug("JOBS: Filling in head roles, Level: [job_priority_level_to_string(level)]")
|
|
|
|
|
// Fill the head jobs first each level
|
|
|
|
|
fill_all_head_positions_at_priority(level)
|
|
|
|
|
//Check the head jobs first each level
|
|
|
|
|
CheckHeadPositions(level)
|
|
|
|
|
|
|
|
|
|
// Loop through all unassigned players
|
|
|
|
|
for(var/mob/dead/new_player/player in unassigned)
|
|
|
|
|
if(!allow_all)
|
|
|
|
|
if(popcap_reached())
|
|
|
|
|
job_debug("JOBS: Popcap reached, trying to reject player: [player]")
|
|
|
|
|
try_reject_player(player)
|
|
|
|
|
if(PopcapReached())
|
|
|
|
|
RejectPlayer(player)
|
|
|
|
|
|
|
|
|
|
job_debug("JOBS: Finding a job for player: [player], at job priority pref: [job_priority_level_to_string(level)]")
|
|
|
|
|
// Loop through all jobs
|
|
|
|
|
for(var/datum/job/job in shuffledoccupations) // SHUFFLE ME BABY
|
|
|
|
|
if(!job)
|
|
|
|
|
JobDebug("FOC invalid/null job in occupations, Player: [player], Job: [job]")
|
|
|
|
|
shuffledoccupations -= job
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
// Make sure the job isn't filled. If it is, remove it from the list so it doesn't get checked again.
|
|
|
|
|
if((job.current_positions >= job.spawn_positions) && job.spawn_positions != -1)
|
|
|
|
|
JobDebug("FOC job filled and not overflow, Player: [player], Job: [job], Current: [job.current_positions], Limit: [job.spawn_positions]")
|
|
|
|
|
shuffledoccupations -= job
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
// Loop through all jobs and build a list of jobs this player could be eligible for.
|
|
|
|
|
var/list/possible_jobs = list()
|
|
|
|
|
for(var/datum/job/job in available_occupations)
|
|
|
|
|
// Filter any job that doesn't fit the current level.
|
|
|
|
|
var/player_job_level = player.client?.prefs.job_preferences[job.title]
|
|
|
|
|
if(isnull(player_job_level))
|
|
|
|
|
job_debug("JOBS: Job not enabled, Job: [job]")
|
|
|
|
|
JobDebug("FOC player job not enabled, Player: [player]")
|
|
|
|
|
continue
|
|
|
|
|
if(player_job_level != level)
|
|
|
|
|
job_debug("JOBS: Job enabled at different priority pref, TheirLevel: [job_priority_level_to_string(player_job_level)], ReqLevel: [job_priority_level_to_string(level)]")
|
|
|
|
|
else if(player_job_level != level)
|
|
|
|
|
JobDebug("FOC player job enabled but at different level, Player: [player], TheirLevel: [job_priority_level_to_string(player_job_level)], ReqLevel: [job_priority_level_to_string(level)]")
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
if(check_job_eligibility(player, job, "JOBS", add_job_to_log = TRUE) != JOB_AVAILABLE)
|
|
|
|
|
if(check_job_eligibility(player, job, "DO", add_job_to_log = TRUE) != JOB_AVAILABLE)
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
possible_jobs += job
|
|
|
|
|
JobDebug("DO pass, Player: [player], Level:[level], Job:[job.title]")
|
|
|
|
|
AssignRole(player, job, do_eligibility_checks = FALSE)
|
|
|
|
|
unassigned -= player
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
// Pick one of those jobs at random.
|
|
|
|
|
var/datum/job/picked_job = pick(possible_jobs)
|
|
|
|
|
JobDebug("DO, Ending standard job assignment")
|
|
|
|
|
|
|
|
|
|
job_debug("JOBS: Now assigning role to player: [player], Job:[picked_job.title]")
|
|
|
|
|
assign_role(player, picked_job, do_eligibility_checks = FALSE)
|
|
|
|
|
if((picked_job.current_positions >= picked_job.spawn_positions) && picked_job.spawn_positions != -1)
|
|
|
|
|
job_debug("JOBS: Job is now full, Job: [picked_job], Positions: [picked_job.current_positions], Limit: [picked_job.spawn_positions]")
|
|
|
|
|
available_occupations -= picked_job
|
|
|
|
|
|
|
|
|
|
job_debug("DO: Ending standard job assignment")
|
|
|
|
|
|
|
|
|
|
job_debug("DO: Handle unassigned")
|
|
|
|
|
// For any players that didn't get a job, fall back on their pref setting for what to do.
|
|
|
|
|
JobDebug("DO, Handle unassigned.")
|
|
|
|
|
// Hand out random jobs to the people who didn't get any in the last check
|
|
|
|
|
// Also makes sure that they got their preference correct
|
|
|
|
|
for(var/mob/dead/new_player/player in unassigned)
|
|
|
|
|
handle_unassigned(player, allow_all)
|
|
|
|
|
job_debug("DO: Ending handle unassigned")
|
|
|
|
|
HandleUnassigned(player, allow_all)
|
|
|
|
|
JobDebug("DO, Ending handle unassigned.")
|
|
|
|
|
|
|
|
|
|
job_debug("DO: Handle unrejectable unassigned")
|
|
|
|
|
JobDebug("DO, Handle unrejectable unassigned")
|
|
|
|
|
//Mop up people who can't leave.
|
|
|
|
|
for(var/mob/dead/new_player/player in unassigned) //Players that wanted to back out but couldn't because they're antags (can you feel the edge case?)
|
|
|
|
|
if(!give_random_job(player))
|
|
|
|
|
if(!assign_role(player, get_job_type(overflow_role))) //If everything is already filled, make them an assistant
|
|
|
|
|
job_debug("DO: Forced antagonist could not be assigned any random job or the overflow role. divide_occupations failed.")
|
|
|
|
|
job_debug("---------------------------------------------------")
|
|
|
|
|
if(!GiveRandomJob(player))
|
|
|
|
|
if(!AssignRole(player, GetJobType(overflow_role))) //If everything is already filled, make them an assistant
|
|
|
|
|
JobDebug("DO, Forced antagonist could not be assigned any random job or the overflow role. DivideOccupations failed.")
|
|
|
|
|
JobDebug("---------------------------------------------------")
|
|
|
|
|
run_divide_occupation_pure = FALSE
|
|
|
|
|
return FALSE //Living on the edge, the forced antagonist couldn't be assigned to overflow role (bans, client age) - just reroll
|
|
|
|
|
job_debug("DO: Ending handle unrejectable unassigned")
|
|
|
|
|
JobDebug("DO, Ending handle unrejectable unassigned")
|
|
|
|
|
|
|
|
|
|
job_debug("All divide occupations tasks completed.")
|
|
|
|
|
job_debug("---------------------------------------------------")
|
|
|
|
|
JobDebug("All divide occupations tasks completed.")
|
|
|
|
|
JobDebug("---------------------------------------------------")
|
|
|
|
|
run_divide_occupation_pure = FALSE
|
|
|
|
|
return TRUE
|
|
|
|
|
|
|
|
|
|
//We couldn't find a job from prefs for this guy.
|
|
|
|
|
/datum/controller/subsystem/job/proc/handle_unassigned(mob/dead/new_player/player, allow_all = FALSE)
|
|
|
|
|
/datum/controller/subsystem/job/proc/HandleUnassigned(mob/dead/new_player/player, allow_all = FALSE)
|
|
|
|
|
var/jobless_role = player.client.prefs.read_preference(/datum/preference/choiced/jobless_role)
|
|
|
|
|
|
|
|
|
|
if(!allow_all)
|
|
|
|
|
if(popcap_reached())
|
|
|
|
|
job_debug("HU: Popcap reached, trying to reject player: [player]")
|
|
|
|
|
try_reject_player(player)
|
|
|
|
|
if(PopcapReached())
|
|
|
|
|
RejectPlayer(player)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
switch (jobless_role)
|
|
|
|
|
if (BEOVERFLOW)
|
|
|
|
|
var/datum/job/overflow_role_datum = get_job_type(overflow_role)
|
|
|
|
|
var/datum/job/overflow_role_datum = GetJobType(overflow_role)
|
|
|
|
|
|
|
|
|
|
if(check_job_eligibility(player, overflow_role_datum, debug_prefix = "HU", add_job_to_log = TRUE) != JOB_AVAILABLE)
|
|
|
|
|
job_debug("HU: Player cannot be overflow, trying to reject: [player]")
|
|
|
|
|
try_reject_player(player)
|
|
|
|
|
RejectPlayer(player)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if(!assign_role(player, overflow_role_datum, do_eligibility_checks = FALSE))
|
|
|
|
|
job_debug("HU: Player could not be assigned overflow role, trying to reject: [player]")
|
|
|
|
|
try_reject_player(player)
|
|
|
|
|
if(!AssignRole(player, overflow_role_datum, do_eligibility_checks = FALSE))
|
|
|
|
|
RejectPlayer(player)
|
|
|
|
|
return
|
|
|
|
|
if (BERANDOMJOB)
|
|
|
|
|
if(!give_random_job(player))
|
|
|
|
|
job_debug("HU: Player cannot be given a random job, trying to reject: [player]")
|
|
|
|
|
try_reject_player(player)
|
|
|
|
|
if(!GiveRandomJob(player))
|
|
|
|
|
RejectPlayer(player)
|
|
|
|
|
return
|
|
|
|
|
if (RETURNTOLOBBY)
|
|
|
|
|
job_debug("HU: Player unable to be assigned job, return to lobby enabled: [player]")
|
|
|
|
|
try_reject_player(player)
|
|
|
|
|
RejectPlayer(player)
|
|
|
|
|
return
|
|
|
|
|
else //Something gone wrong if we got here.
|
|
|
|
|
job_debug("HU: [player] has an invalid jobless_role var: [jobless_role]")
|
|
|
|
|
log_game("[player] has an invalid jobless_role var: [jobless_role]")
|
|
|
|
|
message_admins("[player] has an invalid jobless_role, this shouldn't happen.")
|
|
|
|
|
try_reject_player(player)
|
|
|
|
|
var/message = "HU: [player] fell through handling unassigned"
|
|
|
|
|
JobDebug(message)
|
|
|
|
|
log_game(message)
|
|
|
|
|
message_admins(message)
|
|
|
|
|
RejectPlayer(player)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Gives the player the stuff he should have with his rank
|
|
|
|
|
/datum/controller/subsystem/job/proc/equip_rank(mob/living/equipping, datum/job/job, client/player_client)
|
|
|
|
|
/datum/controller/subsystem/job/proc/EquipRank(mob/living/equipping, datum/job/job, client/player_client)
|
|
|
|
|
equipping.job = job.title
|
|
|
|
|
|
|
|
|
|
SEND_SIGNAL(equipping, COMSIG_JOB_RECEIVED, job)
|
|
|
|
@@ -577,7 +572,7 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
/datum/controller/subsystem/job/proc/handle_auto_deadmin_roles(client/C, rank)
|
|
|
|
|
if(!C?.holder)
|
|
|
|
|
return TRUE
|
|
|
|
|
var/datum/job/job = get_job(rank)
|
|
|
|
|
var/datum/job/job = GetJob(rank)
|
|
|
|
|
|
|
|
|
|
var/timegate_expired = FALSE
|
|
|
|
|
// allow only forcing deadminning in the first X seconds of the round if auto_deadmin_timegate is set in config
|
|
|
|
@@ -595,7 +590,7 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
return C.holder.auto_deadmin()
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/job/proc/setup_officer_positions()
|
|
|
|
|
var/datum/job/J = SSjob.get_job(JOB_SECURITY_OFFICER)
|
|
|
|
|
var/datum/job/J = SSjob.GetJob(JOB_SECURITY_OFFICER)
|
|
|
|
|
if(!J)
|
|
|
|
|
CRASH("setup_officer_positions(): Security officer job is missing")
|
|
|
|
|
|
|
|
|
@@ -603,7 +598,7 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
if(ssc > 0)
|
|
|
|
|
if(J.spawn_positions > 0)
|
|
|
|
|
var/officer_positions = min(12, max(J.spawn_positions, round(unassigned.len / ssc))) //Scale between configured minimum and 12 officers
|
|
|
|
|
job_debug("SOP: Setting open security officer positions to [officer_positions]")
|
|
|
|
|
JobDebug("Setting open security officer positions to [officer_positions]")
|
|
|
|
|
J.total_positions = officer_positions
|
|
|
|
|
J.spawn_positions = officer_positions
|
|
|
|
|
|
|
|
|
@@ -619,7 +614,7 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
else //We ran out of spare locker spawns!
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/job/proc/handle_feedback_gathering()
|
|
|
|
|
/datum/controller/subsystem/job/proc/HandleFeedbackGathering()
|
|
|
|
|
for(var/datum/job/job as anything in joinable_occupations)
|
|
|
|
|
var/high = 0 //high
|
|
|
|
|
var/medium = 0 //medium
|
|
|
|
@@ -658,7 +653,7 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
SSblackbox.record_feedback("nested tally", "job_preferences", young, list("[job.title]", "young"))
|
|
|
|
|
SSblackbox.record_feedback("nested tally", "job_preferences", newbie, list("[job.title]", "newbie"))
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/job/proc/popcap_reached()
|
|
|
|
|
/datum/controller/subsystem/job/proc/PopcapReached()
|
|
|
|
|
var/hpc = CONFIG_GET(number/hard_popcap)
|
|
|
|
|
var/epc = CONFIG_GET(number/extreme_popcap)
|
|
|
|
|
if(hpc || epc)
|
|
|
|
@@ -667,12 +662,12 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
return 1
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/job/proc/try_reject_player(mob/dead/new_player/player)
|
|
|
|
|
/datum/controller/subsystem/job/proc/RejectPlayer(mob/dead/new_player/player)
|
|
|
|
|
if(player.mind && player.mind.special_role)
|
|
|
|
|
job_debug("RJCT: Player unable to be rejected due to special_role, Player: [player], SpecialRole: [player.mind.special_role]")
|
|
|
|
|
return FALSE
|
|
|
|
|
|
|
|
|
|
job_debug("RJCT: Player rejected, Player: [player]")
|
|
|
|
|
return
|
|
|
|
|
if(PopcapReached())
|
|
|
|
|
JobDebug("Popcap overflow Check observer located, Player: [player]")
|
|
|
|
|
JobDebug("Player rejected :[player]")
|
|
|
|
|
unassigned -= player
|
|
|
|
|
if(!run_divide_occupation_pure)
|
|
|
|
|
to_chat(player, "<span class='infoplain'><b>You have failed to qualify for any job you desired.</b></span>")
|
|
|
|
@@ -684,10 +679,10 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
var/oldjobs = SSjob.all_occupations
|
|
|
|
|
sleep(2 SECONDS)
|
|
|
|
|
for (var/datum/job/job as anything in oldjobs)
|
|
|
|
|
INVOKE_ASYNC(src, PROC_REF(recover_job), job)
|
|
|
|
|
INVOKE_ASYNC(src, PROC_REF(RecoverJob), job)
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/job/proc/recover_job(datum/job/J)
|
|
|
|
|
var/datum/job/newjob = get_job(J.title)
|
|
|
|
|
/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
|
|
|
|
@@ -704,7 +699,7 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
if(buckle && isliving(joining_mob))
|
|
|
|
|
buckle_mob(joining_mob, FALSE, FALSE)
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/job/proc/send_to_late_join(mob/M, buckle = TRUE)
|
|
|
|
|
/datum/controller/subsystem/job/proc/SendToLateJoin(mob/M, buckle = TRUE)
|
|
|
|
|
var/atom/destination
|
|
|
|
|
if(M.mind && !is_unassigned_job(M.mind.assigned_role) && length(GLOB.jobspawn_overrides[M.mind.assigned_role.title])) //We're doing something special today.
|
|
|
|
|
destination = pick(GLOB.jobspawn_overrides[M.mind.assigned_role.title])
|
|
|
|
@@ -739,6 +734,19 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
stack_trace("Unable to find last resort spawn point.")
|
|
|
|
|
return GET_ERROR_ROOM
|
|
|
|
|
|
|
|
|
|
///Lands specified mob at a random spot in the hallways
|
|
|
|
|
/datum/controller/subsystem/job/proc/DropLandAtRandomHallwayPoint(mob/living/living_mob)
|
|
|
|
|
var/turf/spawn_turf = get_safe_random_station_turf(typesof(/area/station/hallway))
|
|
|
|
|
|
|
|
|
|
if(!spawn_turf)
|
|
|
|
|
SendToLateJoin(living_mob)
|
|
|
|
|
else
|
|
|
|
|
podspawn(list(
|
|
|
|
|
"target" = spawn_turf,
|
|
|
|
|
"path" = /obj/structure/closet/supplypod/centcompod,
|
|
|
|
|
"spawn" = living_mob
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
/// Returns a list of minds of all heads of staff who are alive
|
|
|
|
|
/datum/controller/subsystem/job/proc/get_living_heads()
|
|
|
|
|
. = list()
|
|
|
|
@@ -773,7 +781,7 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
if(sec.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_SECURITY)
|
|
|
|
|
. += sec
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/job/proc/job_debug(message)
|
|
|
|
|
/datum/controller/subsystem/job/proc/JobDebug(message)
|
|
|
|
|
log_job_debug(message)
|
|
|
|
|
|
|
|
|
|
/// Builds various lists of jobs based on station, centcom and additional jobs with icons associated with them.
|
|
|
|
@@ -838,47 +846,12 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
safe_code_timer_id = null
|
|
|
|
|
safe_code_request_loc = null
|
|
|
|
|
|
|
|
|
|
/// Assigns roles that are considered high priority, either due to dynamic needing to force a specific role for a specific ruleset
|
|
|
|
|
/// or making sure roles critical to round progression exist where possible every shift.
|
|
|
|
|
/// Blindly assigns the required roles to every player in the dynamic_forced_occupations list.
|
|
|
|
|
/datum/controller/subsystem/job/proc/assign_priority_positions()
|
|
|
|
|
job_debug("APP: Assigning Dynamic ruleset forced occupations: [length(dynamic_forced_occupations)]")
|
|
|
|
|
for(var/mob/new_player in dynamic_forced_occupations)
|
|
|
|
|
// Eligibility checks already carried out as part of the dynamic ruleset trim_candidates proc.
|
|
|
|
|
// However no guarantee of game state between then and now, so don't skip eligibility checks on assign_role.
|
|
|
|
|
assign_role(new_player, get_job(dynamic_forced_occupations[new_player]))
|
|
|
|
|
|
|
|
|
|
// Get JP_HIGH department Heads of Staff in place. Indirectly useful for the Revolution ruleset to have as many Heads as possible.
|
|
|
|
|
job_debug("APP: Assigning all JP_HIGH head of staff roles.")
|
|
|
|
|
var/head_count = fill_all_head_positions_at_priority(JP_HIGH)
|
|
|
|
|
|
|
|
|
|
// If nobody has JP_HIGH on a Head role, try to force at least one Head of Staff so every shift has the best chance
|
|
|
|
|
// of having at least one leadership role.
|
|
|
|
|
if(head_count == 0)
|
|
|
|
|
force_one_head_assignment()
|
|
|
|
|
|
|
|
|
|
// Fill out all AI positions.
|
|
|
|
|
job_debug("APP: Filling all AI positions")
|
|
|
|
|
fill_ai_positions()
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/job/proc/assign_all_overflow_positions()
|
|
|
|
|
job_debug("OVRFLW: Assigning all overflow roles.")
|
|
|
|
|
job_debug("OVRFLW: This shift's overflow role: [overflow_role]")
|
|
|
|
|
var/datum/job/overflow_datum = get_job_type(overflow_role)
|
|
|
|
|
|
|
|
|
|
// When the Overflow role changes for any reason, this allows players to set otherwise invalid job priority pref states.
|
|
|
|
|
// So if Assistant is the "usual" Overflow but it gets changed to Clown for a shift, players can set the Assistant role's priorities
|
|
|
|
|
// to JP_MEDIUM and JP_LOW. When the "usual" Overflow role comes back, it returns to an On option in the prefs menu but still
|
|
|
|
|
// keeps its old JP_MEDIUM or JP_LOW value in the background.
|
|
|
|
|
|
|
|
|
|
// Due to this prefs quirk, we actually don't want to find JP_HIGH candidates as it may exclude people with abnormal pref states that
|
|
|
|
|
// appear normal from the UI. By passing in JP_ANY, it will return all players that have the overflow job pref (which should be a toggle)
|
|
|
|
|
// set to any level.
|
|
|
|
|
var/list/overflow_candidates = find_occupation_candidates(overflow_datum, JP_ANY)
|
|
|
|
|
for(var/mob/dead/new_player/player in overflow_candidates)
|
|
|
|
|
// Eligibility checks done as part of find_occupation_candidates, so skip them.
|
|
|
|
|
assign_role(player, get_job_type(overflow_role), do_eligibility_checks = FALSE)
|
|
|
|
|
job_debug("OVRFLW: Assigned overflow to player: [player]")
|
|
|
|
|
job_debug("OVRFLW: All overflow roles assigned.")
|
|
|
|
|
// Eligibility checks already carried out as part of the dynamic ruleset trim_candidates proc.area
|
|
|
|
|
// However no guarantee of game state between then and now, so don't skip eligibility checks on AssignRole.
|
|
|
|
|
AssignRole(new_player, GetJob(dynamic_forced_occupations[new_player]))
|
|
|
|
|
|
|
|
|
|
/// Takes a job priority #define such as JP_LOW and gets its string representation for logging.
|
|
|
|
|
/datum/controller/subsystem/job/proc/job_priority_level_to_string(priority)
|
|
|
|
@@ -896,40 +869,40 @@ SUBSYSTEM_DEF(job)
|
|
|
|
|
* Arguments:
|
|
|
|
|
* * player - The player to check for job eligibility.
|
|
|
|
|
* * possible_job - The job to check for eligibility against.
|
|
|
|
|
* * debug_prefix - Logging prefix for the job_debug log entries. For example, GRJ during give_random_job or DO during divide_occupations.
|
|
|
|
|
* * debug_prefix - Logging prefix for the JobDebug log entries. For example, GRJ during GiveRandomJob or DO during DivideOccupations.
|
|
|
|
|
* * add_job_to_log - If TRUE, appends the job type to the log entry. If FALSE, does not. Set to FALSE when check is part of iterating over players for a specific job, set to TRUE when check is part of iterating over jobs for a specific player and you don't want extra log entry spam.
|
|
|
|
|
*/
|
|
|
|
|
/datum/controller/subsystem/job/proc/check_job_eligibility(mob/dead/new_player/player, datum/job/possible_job, debug_prefix = "", add_job_to_log = FALSE)
|
|
|
|
|
if(!player.mind)
|
|
|
|
|
job_debug("[debug_prefix]: Player has no mind, Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
|
|
|
|
|
JobDebug("[debug_prefix] player has no mind, Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
|
|
|
|
|
return JOB_UNAVAILABLE_GENERIC
|
|
|
|
|
|
|
|
|
|
if(possible_job.title in player.mind.restricted_roles)
|
|
|
|
|
job_debug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_ANTAG_INCOMPAT, possible_job.title)], Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
|
|
|
|
|
JobDebug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_ANTAG_INCOMPAT, possible_job.title)], Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
|
|
|
|
|
return JOB_UNAVAILABLE_ANTAG_INCOMPAT
|
|
|
|
|
|
|
|
|
|
if(!possible_job.player_old_enough(player.client))
|
|
|
|
|
job_debug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_ACCOUNTAGE, possible_job.title)], Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
|
|
|
|
|
JobDebug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_ACCOUNTAGE, possible_job.title)], Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
|
|
|
|
|
return JOB_UNAVAILABLE_ACCOUNTAGE
|
|
|
|
|
|
|
|
|
|
var/required_playtime_remaining = possible_job.required_playtime_remaining(player.client)
|
|
|
|
|
if(required_playtime_remaining)
|
|
|
|
|
job_debug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_PLAYTIME, possible_job.title)], Player: [player], MissingTime: [required_playtime_remaining][add_job_to_log ? ", Job: [possible_job]" : ""]")
|
|
|
|
|
JobDebug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_PLAYTIME, possible_job.title)], Player: [player], MissingTime: [required_playtime_remaining][add_job_to_log ? ", Job: [possible_job]" : ""]")
|
|
|
|
|
return JOB_UNAVAILABLE_PLAYTIME
|
|
|
|
|
|
|
|
|
|
// Run the banned check last since it should be the rarest check to fail and can access the database.
|
|
|
|
|
if(is_banned_from(player.ckey, possible_job.title))
|
|
|
|
|
job_debug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_BANNED, possible_job.title)], Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
|
|
|
|
|
JobDebug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_BANNED, possible_job.title)], Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
|
|
|
|
|
return JOB_UNAVAILABLE_BANNED
|
|
|
|
|
|
|
|
|
|
// Check for character age
|
|
|
|
|
if(possible_job.required_character_age > player.client.prefs.read_preference(/datum/preference/numeric/age) && possible_job.required_character_age != null)
|
|
|
|
|
job_debug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_AGE)], Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
|
|
|
|
|
JobDebug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_AGE)], Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
|
|
|
|
|
return JOB_UNAVAILABLE_AGE
|
|
|
|
|
|
|
|
|
|
// Need to recheck the player exists after is_banned_from since it can query the DB which may sleep.
|
|
|
|
|
if(QDELETED(player))
|
|
|
|
|
job_debug("[debug_prefix]: Player is qdeleted, Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
|
|
|
|
|
JobDebug("[debug_prefix] player is qdeleted, Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
|
|
|
|
|
return JOB_UNAVAILABLE_GENERIC
|
|
|
|
|
|
|
|
|
|
return JOB_AVAILABLE
|
|
|
|
|