mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-23 03:57:13 +01:00
Datumize job assignment. (#28521)
* Datumize job assignment. * fix arg * correct argument type passing * lewc review 1 * Apply suggestions from code review Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com> Signed-off-by: warriorstar-orion <orion@snowfrost.garden> * Apply suggestions from code review Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com> Signed-off-by: warriorstar-orion <orion@snowfrost.garden> * microptimizations for iterations * shit, not this one * make logging more consistent * copy over head/antag roll logic * okay we're done with dumb boutique logging sinks * this is the same proc * this is the same proc * fix build * more logging and check command position availability * tighten up some logic * mostly tiny tweaks * tweak logs * add step type for latejoin --------- Signed-off-by: warriorstar-orion <orion@snowfrost.garden> Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,231 @@
|
||||
RESTRICT_TYPE(/datum/job_candidate)
|
||||
|
||||
/// A datum representing all the information needed to perform roundstart job
|
||||
/// selection, without actually needing clients. This allows for the creation
|
||||
/// of fake candidates, allowing testing of the role selection system independent
|
||||
/// of the number of actual players.
|
||||
/datum/job_candidate
|
||||
/// The character being considered for assignment. Is a default character
|
||||
/// save but can have player characters loaded into it via
|
||||
/// [/datum/job_candidate/proc/load_from_player].
|
||||
VAR_PRIVATE/datum/character_save/active_character
|
||||
/// The jobbans of the current player or simulated candidate.
|
||||
VAR_PRIVATE/datum/job_ban_holder/jbh
|
||||
/// The list of special roles the candidate has enabled.
|
||||
VAR_PRIVATE/list/be_special = list()
|
||||
/// The jobs that the candidate cannot play, usually populated as a result
|
||||
/// of the candidate being assigned a special/antag role.
|
||||
VAR_PRIVATE/list/restricted_roles = list()
|
||||
/// The account age in days.
|
||||
VAR_PRIVATE/account_age_in_days
|
||||
/// The bitfield of admin rights the candidate has. Used to allow bypasses
|
||||
/// of playtime requirements for admins if
|
||||
/// [/datum/configuration_section/job_configuration/var/enable_exp_admin_bypass]
|
||||
/// is set.
|
||||
VAR_PRIVATE/admin_rights = 0
|
||||
/// The name of the assigned job, once one has been assigned.
|
||||
VAR_PRIVATE/assigned_job_title = null
|
||||
/// A query parameterized list of jobs to hours played.
|
||||
VAR_PRIVATE/exp
|
||||
/// Whether or not the player represented by the candidate is an admin.
|
||||
VAR_PRIVATE/is_admin = FALSE
|
||||
/// Whether or not the player represented by the candidate is logged in on a
|
||||
/// guest account.
|
||||
VAR_PRIVATE/is_guest_key = FALSE
|
||||
/// Whether or not the candidate is a late join candidate, as opposed to a
|
||||
/// roundstart one.
|
||||
VAR_PRIVATE/latejoin = FALSE
|
||||
/// The player's ckey, if the candidate is representing a real player.
|
||||
VAR_PRIVATE/real_ckey
|
||||
/// Whether or not the player is being returned to the lobby due to a failed
|
||||
/// job selection.
|
||||
VAR_PRIVATE/return_to_lobby = FALSE
|
||||
/// What, if any, special/antag role has been assigned to the candidate
|
||||
/// ahead of job selection.
|
||||
VAR_PRIVATE/special_role
|
||||
/// Whether this candidate failed a head roll as an antag.
|
||||
VAR_PRIVATE/failed_head_antag_roll_
|
||||
|
||||
/datum/job_candidate/New()
|
||||
active_character = new()
|
||||
jbh = new()
|
||||
|
||||
/datum/job_candidate/Destroy(force, ...)
|
||||
active_character = null
|
||||
jbh = null
|
||||
return ..()
|
||||
|
||||
/datum/job_candidate/proc/get_ckey()
|
||||
return real_ckey ? real_ckey : "(simulated) CKEY"
|
||||
|
||||
/datum/job_candidate/proc/is_assigned()
|
||||
return assigned_job_title != null
|
||||
|
||||
/datum/job_candidate/proc/assign_title(title)
|
||||
assigned_job_title = title
|
||||
|
||||
/datum/job_candidate/proc/return_to_lobby()
|
||||
return_to_lobby = TRUE
|
||||
|
||||
/datum/job_candidate/proc/restricted_from(datum/job/job)
|
||||
return job.title in restricted_roles
|
||||
|
||||
/datum/job_candidate/proc/has_special_role()
|
||||
return special_role
|
||||
|
||||
/datum/job_candidate/proc/alternate_spawn_option()
|
||||
return active_character.alternate_option
|
||||
|
||||
/datum/job_candidate/proc/has_restricted_roles()
|
||||
return length(restricted_roles)
|
||||
|
||||
/datum/job_candidate/proc/is_jobbanned(datum/job/job)
|
||||
if(GLOB.configuration.jobs.guest_job_ban && is_guest_key)
|
||||
return "Guest Job-ban"
|
||||
|
||||
if(job.title in jbh.job_bans)
|
||||
var/datum/job_ban/ban = jbh.job_bans[job.title]
|
||||
return ban.reason
|
||||
|
||||
/datum/job_candidate/proc/wants_job(datum/job/job, level)
|
||||
return active_character.GetJobDepartment(job, level) & job.flag
|
||||
|
||||
/datum/job_candidate/proc/is_account_old_enough(datum/job/job)
|
||||
if(GLOB.configuration.jobs.restrict_jobs_on_account_age && isnum(account_age_in_days) && isnum(job.minimal_player_age))
|
||||
return max(0, job.minimal_player_age - account_age_in_days) == 0
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/job_candidate/proc/has_special(flag)
|
||||
return flag in be_special
|
||||
|
||||
/datum/job_candidate/proc/failed_head_antag_roll()
|
||||
return failed_head_antag_roll_
|
||||
|
||||
/datum/job_candidate/proc/fail_head_antag_roll()
|
||||
failed_head_antag_roll_ = TRUE
|
||||
|
||||
/datum/job_candidate/proc/load_from_player(mob/new_player/player)
|
||||
if(!istype(player))
|
||||
log_debug("asked to load job candidate from null player")
|
||||
return
|
||||
if(!istype(player.client))
|
||||
log_debug("asked to load job candidate from player [player] with no attached client")
|
||||
return
|
||||
active_character = player.client.prefs.active_character
|
||||
jbh.reload_jobbans(player.client)
|
||||
account_age_in_days = player.client.player_age
|
||||
be_special = player.client.prefs.be_special
|
||||
restricted_roles = player.mind.restricted_roles
|
||||
special_role = player.mind.special_role
|
||||
exp = player.client.prefs.exp
|
||||
if(player.client.holder)
|
||||
is_admin = TRUE
|
||||
admin_rights = player.client.holder.rights
|
||||
real_ckey = player.ckey
|
||||
|
||||
/datum/job_candidate/proc/apply_to_player(mob/new_player/player)
|
||||
if(return_to_lobby)
|
||||
player.ready = FALSE
|
||||
return
|
||||
|
||||
var/datum/job/job = SSjobs.GetJob(assigned_job_title)
|
||||
if(job)
|
||||
player.mind.assigned_role = assigned_job_title
|
||||
player.mind.role_alt_title = active_character.GetPlayerAltTitle(job)
|
||||
player.mind.job_objectives.Cut()
|
||||
|
||||
for(var/objective_type in job.required_objectives)
|
||||
new objective_type(player.mind)
|
||||
|
||||
/datum/job_candidate/proc/get_job_eligibility(datum/job/job)
|
||||
if(!job)
|
||||
return FALSE
|
||||
if(job.job_banned_gamemode)
|
||||
return FALSE
|
||||
if(is_jobbanned(job))
|
||||
return FALSE
|
||||
if(!is_account_old_enough(job))
|
||||
return FALSE
|
||||
if(get_job_exp_restrictions(job))
|
||||
return FALSE
|
||||
if(is_barred_by_disability(job))
|
||||
return FALSE
|
||||
if(is_barred_by_missing_limbs(job))
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/job_candidate/proc/is_barred_by_missing_limbs(datum/job/job)
|
||||
if(!job.missing_limbs_allowed)
|
||||
return FALSE
|
||||
|
||||
var/organ_status
|
||||
var/list/active_character_organs = active_character.organ_data
|
||||
|
||||
for(var/organ_name in active_character_organs)
|
||||
organ_status = active_character_organs[organ_name]
|
||||
if(organ_status == "amputated")
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/job_candidate/proc/is_barred_by_disability(datum/job/job)
|
||||
if(!length(job.blacklisted_disabilities))
|
||||
return FALSE
|
||||
for(var/disability in job.blacklisted_disabilities)
|
||||
if(active_character.disabilities & disability)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/job_candidate/proc/get_job_exp_restrictions(datum/job/job)
|
||||
if(is_job_playable(job))
|
||||
return null
|
||||
|
||||
var/list/play_records = params2list(exp)
|
||||
var/list/innertext = list()
|
||||
|
||||
for(var/exp_type in job.exp_map)
|
||||
if(!(exp_type in play_records))
|
||||
innertext += "[get_exp_format(job.exp_map[exp_type])] as [exp_type]"
|
||||
continue
|
||||
// You may be saying "Jeez why so many text2num()"
|
||||
// The DB loads these as strings for some reason, and I also dont trust coders to use ints in the job lists properly
|
||||
if(text2num(job.exp_map[exp_type]) > text2num(play_records[exp_type]))
|
||||
var/diff = text2num(job.exp_map[exp_type]) - text2num(play_records[exp_type])
|
||||
innertext += "[get_exp_format(diff)] as [exp_type]"
|
||||
|
||||
if(length(innertext))
|
||||
return innertext.Join(", ")
|
||||
|
||||
return null
|
||||
|
||||
/datum/job_candidate/proc/check_admin_rights(rights_required)
|
||||
if(rights_required)
|
||||
if(rights_required & admin_rights)
|
||||
return TRUE
|
||||
else if(is_admin)
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
|
||||
/datum/job_candidate/proc/is_job_playable(datum/job/job)
|
||||
if(!length(job.exp_map))
|
||||
return TRUE // No EXP map, playable
|
||||
if(!GLOB.configuration.jobs.enable_exp_restrictions)
|
||||
return TRUE // No restrictions, playable
|
||||
if(GLOB.configuration.jobs.enable_exp_admin_bypass && check_admin_rights(R_ADMIN))
|
||||
return TRUE // Admin user, playable
|
||||
|
||||
// Now look through their EXP
|
||||
var/list/play_records = params2list(exp)
|
||||
var/success = TRUE
|
||||
|
||||
// Check their requirements
|
||||
for(var/exp_type in job.exp_map)
|
||||
if(!(exp_type in play_records))
|
||||
success = FALSE
|
||||
continue
|
||||
if(text2num(job.exp_map[exp_type]) > text2num(play_records[exp_type]))
|
||||
success = FALSE
|
||||
|
||||
return success
|
||||
@@ -0,0 +1,326 @@
|
||||
RESTRICT_TYPE(/datum/job_selector)
|
||||
|
||||
/datum/job_selector
|
||||
var/list/candidates = list()
|
||||
var/list/assigned_candidates = list()
|
||||
var/probability_of_antag_role_restriction = 100
|
||||
|
||||
/datum/job_selector/proc/add_candidate(mob/new_player/player = null)
|
||||
var/datum/job_candidate/candidate = new()
|
||||
if(player)
|
||||
candidate.load_from_player(player)
|
||||
|
||||
candidates[candidate] = player
|
||||
|
||||
/datum/job_selector/proc/assign_role(datum/job_candidate/candidate, datum/job/job, latejoin = FALSE, step = "unknown_step")
|
||||
if(!job)
|
||||
return FALSE
|
||||
|
||||
var/eligible = candidate.get_job_eligibility(job)
|
||||
var/available = latejoin ? job.is_position_available() : job.is_spawn_position_available()
|
||||
|
||||
if(eligible && available)
|
||||
candidate.assign_title(job.title)
|
||||
assigned_candidates[candidate] = candidates[candidate]
|
||||
candidates -= candidate
|
||||
job.current_positions++
|
||||
SSblackbox.record_feedback("nested tally", "manifest", 1, list(job.title, (latejoin ? "latejoin" : "roundstart")))
|
||||
log_chat_debug("jobs/[step]: candidate=[candidate.UID()] job=[job] [latejoin ? "latejoin" : "roundstart"] assigned")
|
||||
return TRUE
|
||||
|
||||
log_chat_debug("jobs/[step]: candidate=[candidate.UID()] job=[job] [latejoin ? "latejoin" : "roundstart"] ineligible or unavailable")
|
||||
return FALSE
|
||||
|
||||
/// Convenience proc for handling a single latejoin player
|
||||
/datum/job_selector/proc/latejoin_assign(mob/new_player/player, datum/job/job)
|
||||
var/datum/job_candidate/candidate = new()
|
||||
candidate.load_from_player(player)
|
||||
if(assign_role(candidate, job, latejoin = TRUE, step = "latejoin"))
|
||||
candidate.apply_to_player(player)
|
||||
else
|
||||
to_chat(player, "<span class='warning'>You are unable to join the round as [job.title]. Please try another job.</span>")
|
||||
|
||||
/datum/job_selector/proc/apply_roles_to_players()
|
||||
for(var/datum/job_candidate/candidate in assigned_candidates)
|
||||
var/mob/new_player/player = assigned_candidates[candidate]
|
||||
if(player)
|
||||
candidate.apply_to_player(player)
|
||||
|
||||
/datum/job_selector/proc/return_to_lobby(datum/job_candidate/candidate)
|
||||
candidate.return_to_lobby()
|
||||
assigned_candidates[candidate] = candidates[candidate]
|
||||
candidates -= candidate
|
||||
|
||||
/datum/job_selector/proc/find_job_candidates(datum/job/job, level, flag)
|
||||
var/list/job_candidates = list()
|
||||
for(var/datum/job_candidate/candidate as anything in candidates)
|
||||
// If the candidate doesn't want the job don't bother checking further, duh
|
||||
if(!candidate.wants_job(job, level))
|
||||
continue
|
||||
if(!candidate.get_job_eligibility(job))
|
||||
log_chat_debug("jobs/find_job_candidates: candidate=[candidate.UID()] job=[job] level=[level] ineligible")
|
||||
continue
|
||||
if(flag && !candidate.has_special(flag))
|
||||
log_chat_debug("jobs/find_job_candidates: candidate=[candidate.UID()] job=[job] level=[level] missing flag=[flag]")
|
||||
continue
|
||||
if(candidate.restricted_from(job))
|
||||
log_chat_debug("jobs/find_job_candidates: candidate=[candidate.UID()] job=[job] level=[level] restricted")
|
||||
continue
|
||||
if(candidate.has_special_role() && (job.title in SSticker.mode.single_antag_positions))
|
||||
if(candidate.failed_head_antag_roll() || !prob(probability_of_antag_role_restriction))
|
||||
log_chat_debug("jobs/find_job_candidates: candidate=[candidate.UID()] job=[job] special_role=[candidate.has_special_role()] failed head antag roll")
|
||||
candidate.fail_head_antag_roll()
|
||||
continue
|
||||
else
|
||||
probability_of_antag_role_restriction /= 10
|
||||
|
||||
job_candidates += candidate
|
||||
|
||||
log_chat_debug("jobs/find_job_candidates: job=[job] level=[level] flag=[flag] returned [length(job_candidates)] candidates")
|
||||
return job_candidates
|
||||
|
||||
/datum/job_selector/proc/assign_random_job(datum/job_candidate/candidate)
|
||||
for(var/datum/job/job as anything in shuffle(SSjobs.occupations))
|
||||
if(istype(job, /datum/job/assistant)) // We don't want to give him assistant, that's boring!
|
||||
continue
|
||||
|
||||
if(job.is_command_position()) // If you want a command position, select it!
|
||||
continue
|
||||
|
||||
if(job.admin_only || job.mentor_only) // No admin/mentor-only positions either.
|
||||
continue
|
||||
|
||||
if(!candidate.get_job_eligibility(job))
|
||||
continue
|
||||
|
||||
if(candidate.restricted_from(job))
|
||||
continue
|
||||
|
||||
if(candidate.has_special_role() && (job.title in SSticker.mode.single_antag_positions))
|
||||
if(candidate.failed_head_antag_roll() || !prob(probability_of_antag_role_restriction))
|
||||
log_chat_debug("jobs/assign_random_job: candidate=[candidate.UID()] job=[job] special_role=[candidate.has_special_role()] failed head antag roll")
|
||||
candidate.fail_head_antag_roll()
|
||||
continue
|
||||
else
|
||||
probability_of_antag_role_restriction /= 10
|
||||
if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1)
|
||||
assign_role(candidate, job, step = "assign_random_job")
|
||||
return
|
||||
|
||||
log_chat_debug("jobs/assign_random_job candidate=[candidate.UID()] could not assign")
|
||||
|
||||
/// This proc is called before the level loop of assign_all_roles() and will try
|
||||
/// to select a head, ignoring ALL non-head preferences for every level until it
|
||||
/// locates a head or runs out of levels to check
|
||||
/datum/job_selector/proc/fill_head_position()
|
||||
for(var/level = 1 to 3)
|
||||
for(var/command_position in GLOB.command_positions)
|
||||
var/datum/job/job = SSjobs.GetJob(command_position)
|
||||
if(!job)
|
||||
continue
|
||||
if(!job.is_spawn_position_available())
|
||||
continue
|
||||
var/list/job_candidates = find_job_candidates(job, level)
|
||||
if(!length(job_candidates))
|
||||
continue
|
||||
|
||||
var/list/filtered_candidates = list()
|
||||
|
||||
for(var/datum/job_candidate/candidate as anything in job_candidates)
|
||||
var/mob/new_player/real_player = candidates[candidate]
|
||||
if(real_player && !real_player.client)
|
||||
// Log-out during round-start?
|
||||
// What a bad boy, no head position for you!
|
||||
continue
|
||||
filtered_candidates += candidate
|
||||
|
||||
if(!length(filtered_candidates))
|
||||
continue
|
||||
|
||||
var/datum/job_candidate/candidate = pick(filtered_candidates)
|
||||
if(assign_role(candidate, job, step = "fill_head_position"))
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
|
||||
/// This proc is called at the start of the level loop
|
||||
/// of assign_all_roles() and will cause head jobs to be
|
||||
/// checked before any other jobs of the same level.
|
||||
/datum/job_selector/proc/check_command_positions(level)
|
||||
for(var/command_position in GLOB.command_positions)
|
||||
var/datum/job/job = SSjobs.GetJob(command_position)
|
||||
if(!job)
|
||||
continue
|
||||
if(!job.is_spawn_position_available())
|
||||
continue
|
||||
var/list/job_candidates = find_job_candidates(job, level)
|
||||
if(!length(job_candidates))
|
||||
continue
|
||||
var/datum/job_candidate/candidate = pick(job_candidates)
|
||||
assign_role(candidate, job, step = "check_command_positions")
|
||||
|
||||
/datum/job_selector/proc/fill_ai_position()
|
||||
if(!GLOB.configuration.jobs.allow_ai)
|
||||
return FALSE
|
||||
|
||||
var/ai_selected = FALSE
|
||||
var/datum/job/job = SSjobs.GetJob("AI")
|
||||
if(!job)
|
||||
return FALSE
|
||||
|
||||
for(var/i = job.total_positions, i > 0, i--)
|
||||
for(var/level = 1 to 3)
|
||||
var/list/job_candidates = list()
|
||||
job_candidates = find_job_candidates(job, level)
|
||||
if(length(job_candidates))
|
||||
var/mob/new_player/candidate = pick(job_candidates)
|
||||
if(assign_role(candidate, "AI", step = "fill_ai_position"))
|
||||
ai_selected++
|
||||
break
|
||||
|
||||
if(ai_selected)
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
|
||||
/// Fills var "assigned_role" for all ready players.
|
||||
/datum/job_selector/proc/assign_all_roles()
|
||||
// Lets roughly time this
|
||||
var/watch = start_watch()
|
||||
//Setup new player list and get the jobs list
|
||||
SSjobs.SetupOccupations()
|
||||
|
||||
//Holder for Triumvirate is stored in the ticker, this just processes it
|
||||
if(SSticker)
|
||||
for(var/datum/job/ai/A in SSjobs.occupations)
|
||||
if(SSticker.triai)
|
||||
A.spawn_positions = 3
|
||||
|
||||
//Get the players who are ready
|
||||
for(var/mob/new_player/player in GLOB.player_list)
|
||||
if(player.ready && player.mind && !player.mind.assigned_role)
|
||||
add_candidate(player)
|
||||
|
||||
if(!length(candidates))
|
||||
return FALSE
|
||||
|
||||
candidates = shuffle(candidates)
|
||||
|
||||
handle_feedback_gathering()
|
||||
|
||||
// People who want to be assistants, sure, go on.
|
||||
var/datum/job/ast = SSjobs.GetJob("Assistant")
|
||||
var/list/assistant_candidates = find_job_candidates(ast, 3)
|
||||
for(var/datum/job_candidate/candidate as anything in assistant_candidates)
|
||||
assign_role(candidate, ast, step = "assign_all_roles/assistants")
|
||||
|
||||
fill_head_position()
|
||||
fill_ai_position()
|
||||
|
||||
// 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
|
||||
for(var/level = 1 to 3)
|
||||
//Check the head jobs first each level
|
||||
check_command_positions(level)
|
||||
|
||||
// Loop through all unassigned players
|
||||
for(var/datum/job_candidate/candidate as anything in candidates)
|
||||
var/list/shuffledoccupations = shuffle(SSjobs.occupations)
|
||||
|
||||
for(var/datum/job/job as anything in shuffledoccupations)
|
||||
if(!job.is_spawn_position_available())
|
||||
continue
|
||||
|
||||
// If the player wants that job on this level, then try give it to him.
|
||||
if(!candidate.wants_job(job, level))
|
||||
continue
|
||||
|
||||
if(!candidate.get_job_eligibility(job))
|
||||
log_chat_debug("jobs/assign_all_roles: candidate=[candidate.UID()] job=[job] wanted but ineligible")
|
||||
continue
|
||||
|
||||
if(candidate.restricted_from(job))
|
||||
log_chat_debug("jobs/assign_all_roles: candidate=[candidate.UID()] job=[job] incompatible with antagonist role")
|
||||
continue
|
||||
|
||||
if(candidate.has_special_role() && (job.title in SSticker.mode.single_antag_positions))
|
||||
if(candidate.failed_head_antag_roll() || !prob(probability_of_antag_role_restriction))
|
||||
candidate.fail_head_antag_roll()
|
||||
log_chat_debug("jobs/assign_all_roles: candidate=[candidate.UID()] job=[job] special_role=[candidate.has_special_role()] failed head antag roll")
|
||||
continue
|
||||
else
|
||||
probability_of_antag_role_restriction /= 10
|
||||
|
||||
assign_role(candidate, job, step = "assign_all_roles")
|
||||
break
|
||||
|
||||
// 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/datum/job_candidate/candidate as anything in candidates)
|
||||
if(candidate.alternate_spawn_option() == GET_RANDOM_JOB)
|
||||
assign_random_job(candidate)
|
||||
|
||||
// Antags, who have to get in, come first
|
||||
for(var/datum/job_candidate/candidate as anything in candidates)
|
||||
if(candidate.has_special_role())
|
||||
if(candidate.alternate_spawn_option() != BE_ASSISTANT)
|
||||
assign_random_job(candidate)
|
||||
if(!candidate.is_assigned())
|
||||
assign_role(candidate, ast, step = "assign_all_roles/special")
|
||||
else
|
||||
assign_role(candidate, ast, step = "assign_all_roles/antag")
|
||||
else if(candidate.has_restricted_roles())
|
||||
stack_trace("A player with `restricted_roles` had no `special_role`. They are likely an antagonist, but failed to spawn in.") // this can be fixed by assigning a special_role in pre_setup of the gamemode
|
||||
message_admins("A player ([key_name_admin(candidate.get_ckey())]) is likely an antagonist, but may have failed to spawn in! Please report this to coders.")
|
||||
|
||||
// Then we assign what we can to everyone else.
|
||||
for(var/datum/job_candidate/candidate as anything in candidates)
|
||||
if(candidate.alternate_spawn_option() == BE_ASSISTANT)
|
||||
assign_role(candidate, ast, step = "assign_all_roles/assistant")
|
||||
else if(candidate.alternate_spawn_option() == RETURN_TO_LOBBY)
|
||||
return_to_lobby(candidate)
|
||||
|
||||
log_chat_debug("jobs/assign_all_roles completed in [stop_watch(watch)]s")
|
||||
return TRUE
|
||||
|
||||
/datum/job_selector/proc/handle_feedback_gathering()
|
||||
for(var/datum/job/job as anything in SSjobs.occupations)
|
||||
var/high = 0
|
||||
var/medium = 0
|
||||
var/low = 0
|
||||
var/never = 0
|
||||
var/banned = 0
|
||||
var/young = 0 //account too young
|
||||
var/disabled = FALSE //has disability rendering them ineligible
|
||||
for(var/datum/job_candidate/candidate as anything in candidates)
|
||||
if(candidate.is_jobbanned(job))
|
||||
banned++
|
||||
continue
|
||||
if(!candidate.is_account_old_enough(job))
|
||||
young++
|
||||
continue
|
||||
if(candidate.get_job_exp_restrictions(job))
|
||||
young++
|
||||
continue
|
||||
if(candidate.is_barred_by_disability(job) || candidate.is_barred_by_missing_limbs(job))
|
||||
disabled++
|
||||
continue
|
||||
if(candidate.wants_job(job, 1))
|
||||
high++
|
||||
else if(candidate.wants_job(job, 2))
|
||||
medium++
|
||||
else if(candidate.wants_job(job, 3))
|
||||
low++
|
||||
else
|
||||
never++ //not selected
|
||||
|
||||
SSblackbox.record_feedback("nested tally", "job_preferences", high, list("[job.title]", "high"))
|
||||
SSblackbox.record_feedback("nested tally", "job_preferences", medium, list("[job.title]", "medium"))
|
||||
SSblackbox.record_feedback("nested tally", "job_preferences", low, list("[job.title]", "low"))
|
||||
SSblackbox.record_feedback("nested tally", "job_preferences", never, list("[job.title]", "never"))
|
||||
SSblackbox.record_feedback("nested tally", "job_preferences", banned, list("[job.title]", "banned"))
|
||||
SSblackbox.record_feedback("nested tally", "job_preferences", young, list("[job.title]", "young"))
|
||||
SSblackbox.record_feedback("nested tally", "job_preferences", disabled, list("[job.title]", "disabled"))
|
||||
Reference in New Issue
Block a user