diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 287ef02a02a..b6253ef6c4c 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -57,27 +57,28 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G // Subsystem init_order, from highest priority to lowest priority // Subsystems shutdown in the reverse of the order they initialize in // The numbers just define the ordering, they are meaningless otherwise. -#define INIT_ORDER_FAIL2TOPIC 101 -#define INIT_ORDER_SOUNDS 95 -#define INIT_ORDER_GARBAGE 70 -#define INIT_ORDER_TIMER 60 -#define INIT_ORDER_INSTRUMENTS 50 -#define INIT_ORDER_MAPPING 20 // VOREStation Edit +#define INIT_ORDER_FAIL2TOPIC 101 +#define INIT_ORDER_SOUNDS 95 +#define INIT_ORDER_JOBS 85 +#define INIT_ORDER_GARBAGE 70 +#define INIT_ORDER_TIMER 60 +#define INIT_ORDER_INSTRUMENTS 50 +#define INIT_ORDER_MAPPING 20 // VOREStation Edit #define INIT_ORDER_SERVER_MAINT 17 -#define INIT_ORDER_DECALS 16 -#define INIT_ORDER_ATOMS 15 -#define INIT_ORDER_MACHINES 10 -#define INIT_ORDER_SHUTTLES 3 -#define INIT_ORDER_DEFAULT 0 -#define INIT_ORDER_LIGHTING 0 -#define INIT_ORDER_AIR -1 -#define INIT_ORDER_PLANETS -4 -#define INIT_ORDER_HOLOMAPS -5 -#define INIT_ORDER_OVERLAY -6 -#define INIT_ORDER_XENOARCH -20 -#define INIT_ORDER_CIRCUIT -21 -#define INIT_ORDER_CHEMISTRY 18 -#define INIT_ORDER_AI -22 +#define INIT_ORDER_DECALS 16 +#define INIT_ORDER_ATOMS 15 +#define INIT_ORDER_MACHINES 10 +#define INIT_ORDER_SHUTTLES 3 +#define INIT_ORDER_DEFAULT 0 +#define INIT_ORDER_LIGHTING 0 +#define INIT_ORDER_AIR -1 +#define INIT_ORDER_PLANETS -4 +#define INIT_ORDER_HOLOMAPS -5 +#define INIT_ORDER_OVERLAY -6 +#define INIT_ORDER_XENOARCH -20 +#define INIT_ORDER_CIRCUIT -21 +#define INIT_ORDER_CHEMISTRY 18 +#define INIT_ORDER_AI -22 // Subsystem fire priority, from lowest to highest priority diff --git a/code/controllers/subsystems/emergency_shuttle.dm b/code/controllers/subsystems/emergency_shuttle.dm index aaeb8343c60..a4d8e20667e 100644 --- a/code/controllers/subsystems/emergency_shuttle.dm +++ b/code/controllers/subsystems/emergency_shuttle.dm @@ -1,3 +1,5 @@ + + /datum/controller/process/emergencyShuttle/setup() name = "emergency shuttle" schedule_interval = 20 // every 2 seconds diff --git a/code/controllers/subsystems/game_master.dm b/code/controllers/subsystems/game_master.dm index 7f89f3ab135..7ae19937922 100644 --- a/code/controllers/subsystems/game_master.dm +++ b/code/controllers/subsystems/game_master.dm @@ -1,6 +1,157 @@ -/datum/controller/process/game_master/setup() - name = "\improper GM controller" - schedule_interval = 600 // every 60 seconds +// This is a sort of successor to the various event systems created over the years. It is designed to be just a tad smarter than the +// previous ones, checking various things like player count, department size and composition, individual player activity, +// individual player (IC) skill, and such, in order to try to choose the best actions to take in order to add spice or variety to +// the round. +SUBSYSTEM_DEF(gamemaster) + name = "Game Master" + wait = 600 + var/suspended = TRUE // If true, it will not do anything. + var/ignore_time_restrictions = FALSE// Useful for debugging without needing to wait 20 minutes each time. + var/list/available_actions = list() // A list of 'actions' that the GM has access to, to spice up a round, such as events. + var/danger = 0 // The GM's best guess at how chaotic the round is. High danger makes it hold back. + var/staleness = -20 // Determines liklihood of the GM doing something, increases over time. + var/danger_modifier = 1 // Multiplier for how much 'danger' is accumulated. + var/staleness_modifier = 1 // Ditto. Higher numbers generally result in more events occuring in a round. + var/ticks_completed = 0 // Counts amount of ticks completed. Note that this ticks once a minute. + var/next_action = 0 // Minimum amount of time of nothingness until the GM can pick something again. + var/last_department_used = null // If an event was done for a specific department, it is written here, so it doesn't do it again. + +/datum/controller/subsystem/gamemaster/Initialize() + available_actions = init_subtypes(/datum/gm_action) + for(var/datum/gm_action/action in available_actions) + action.gm = src + + var/config_setup_delay = TRUE + spawn(0) + while(config_setup_delay) + if(config) + config_setup_delay = FALSE + if(config_legacy.enable_game_master) + suspended = FALSE + else + sleep(30 SECONDS) + return ..() + +/datum/controller/subsystem/gamemaster/fire(resumed) + if(ticker && ticker.current_state == GAME_STATE_PLAYING && !suspended) + adjust_staleness(1) + adjust_danger(-1) + ticks_completed++ + + var/global_afk = metric.assess_all_living_mobs() + global_afk -= 100 + global_afk = abs(global_afk) + global_afk = round(global_afk / 100, 0.1) + adjust_staleness(global_afk) // Staleness increases faster if more people are less active. + + if(world.time < next_action && prob(staleness * 2) ) + log_debug("Game Master going to start something.") + start_action() + +// This is run before committing to an action/event. +/datum/controller/subsystem/gamemaster/proc/pre_action_checks() + if(!ticker || ticker.current_state != GAME_STATE_PLAYING) + log_debug("Game Master unable to start event: Ticker is nonexistant, or the game is not ongoing.") + return FALSE + if(suspended) + return FALSE + if(ignore_time_restrictions) + return TRUE + // Last minute antagging is bad for humans to do, so the GM will respect the start and end of the round. + var/mills = round_duration_in_ticks + var/mins = round((mills % 36000) / 600) + var/hours = round(mills / 36000) + + if(hours < 1 && mins <= 20) // Don't do anything for the first twenty minutes of the round. + log_debug("Game Master unable to start event: It is too early.") + return FALSE + if(hours >= 2 && mins >= 40) // Don't do anything in the last twenty minutes of the round, as well. + log_debug("Game Master unable to start event: It is too late.") + return FALSE + return TRUE + +/datum/controller/subsystem/gamemaster/proc/start_action() + if(!pre_action_checks()) // Make sure we're not doing last minute events, or early events. + return + log_debug("Game Master now starting action decision.") + var/list/most_active_departments = metric.assess_all_departments(3, list(last_department_used)) + var/list/best_actions = decide_best_action(most_active_departments) + + if(best_actions && best_actions.len) + var/list/weighted_actions = list() + for(var/datum/gm_action/action in best_actions) + if(action.chaotic > danger) + continue // We skip dangerous events when bad stuff is already occuring. + weighted_actions[action] = action.get_weight() + + var/datum/gm_action/choice = pickweight(weighted_actions) + if(choice) + log_debug("[choice.name] was chosen by the Game Master, and is now being ran.") + run_action(choice) + +/datum/controller/subsystem/gamemaster/proc/run_action(var/datum/gm_action/action) + action.set_up() + action.start() + action.announce() + if(action.chaotic) + danger += action.chaotic + if(action.length) + spawn(action.length) + action.end() + next_action = world.time + rand(15 MINUTES, 30 MINUTES) + last_department_used = action.departments[1] + + +/datum/controller/subsystem/gamemaster/proc/decide_best_action(var/list/most_active_departments) + if(!most_active_departments.len) // Server's empty? + log_debug("Game Master failed to find any active departments.") + return list() + + var/list/best_actions = list() // List of actions which involve the most active departments. + if(most_active_departments.len >= 2) + for(var/datum/gm_action/action in available_actions) + if(!action.enabled) + continue + // Try to incorporate an action with the top two departments first. + if(most_active_departments[1] in action.departments && most_active_departments[2] in action.departments) + best_actions.Add(action) + log_debug("[action.name] is being considered because both most active departments are involved.") + + if(best_actions.len) // We found something for those two, let's do it. + return best_actions + + // Otherwise we probably couldn't find something for the second highest group, so let's ignore them. + for(var/datum/gm_action/action in available_actions) + if(!action.enabled) + continue + if(most_active_departments[1] in action.departments) + best_actions.Add(action) + log_debug("[action.name] is being considered because the most active department is involved.") + + if(best_actions.len) // Found something for the one guy. + return best_actions + + // At this point we should expand our horizons. + for(var/datum/gm_action/action in available_actions) + if(!action.enabled) + continue + if(ROLE_EVERYONE in action.departments) + best_actions.Add(action) + log_debug("[action.name] is being considered because it involves everyone.") + + if(best_actions.len) // Finally, perhaps? + return best_actions + + // Just give a random event if for some reason it still can't make up its mind. + for(var/datum/gm_action/action in available_actions) + if(!action.enabled) + continue + best_actions.Add(action) + log_debug("[action.name] is being considered because everything else failed.") + + if(best_actions.len) // Finally, perhaps? + return best_actions + else + log_debug("Game Master failed to find a suitable event, something very wrong is going on.") + -/datum/controller/process/game_master/doWork() - game_master.process() \ No newline at end of file diff --git a/code/controllers/subsystems/jobs.dm b/code/controllers/subsystems/jobs.dm index 271bf63c383..ee46e2945d9 100644 --- a/code/controllers/subsystems/jobs.dm +++ b/code/controllers/subsystems/jobs.dm @@ -1,673 +1,678 @@ -var/global/datum/controller/occupations/job_master - -#define GET_RANDOM_JOB 0 -#define BE_ASSISTANT 1 -#define RETURN_TO_LOBBY 2 - -/datum/controller/occupations - //List of all jobs +SUBSYSTEM_DEF(jobs) + name = "Jobs" + flags = SS_NO_FIRE + init_order = INIT_ORDER_JOBS var/list/occupations = list() //Players who need jobs var/list/unassigned = list() //Debug info var/list/job_debug = list() + //List of all jobs - proc/SetupOccupations(var/faction = "Station") - occupations = list() - //var/list/all_jobs = typesof(/datum/job) - var/list/all_jobs = list(/datum/job/assistant) | GLOB.using_map.allowed_jobs - if(!all_jobs.len) - 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) continue - occupations += job - sortTim(occupations, /proc/cmp_job_datums) +#define GET_RANDOM_JOB 0 +#define BE_ASSISTANT 1 +#define RETURN_TO_LOBBY 2 - return 1 + +/datum/controller/subsystem/jobs/proc/SetupOccupations(var/faction = "Station") + occupations = list() + //var/list/all_jobs = typesof(/datum/job) + var/list/all_jobs = list(/datum/job/assistant) | GLOB.using_map.allowed_jobs + if(!all_jobs.len) + 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) continue + occupations += job + sortTim(occupations, /proc/cmp_job_datums) + return 1 - proc/Debug(var/text) - if(!Debug2) return 0 - job_debug.Add(text) - return 1 +/datum/controller/subsystem/jobs/proc/Debug(var/text) + if(!Debug2) + return 0 + job_debug.Add(text) + return 1 - proc/GetJob(var/rank) - if(!rank) return null - for(var/datum/job/J in occupations) - if(!J) continue - if(J.title == rank) return J +/datum/controller/subsystem/jobs/proc/GetJob(var/rank) + if(!rank) return null + for(var/datum/job/J in occupations) + if(!J) + continue + if(J.title == rank) + return J + return null - proc/GetPlayerAltTitle(mob/new_player/player, rank) - return player.client.prefs.GetPlayerAltTitle(GetJob(rank)) +/datum/controller/subsystem/jobs/proc/GetPlayerAltTitle(mob/new_player/player, rank) + return player.client.prefs.GetPlayerAltTitle(GetJob(rank)) - proc/AssignRole(var/mob/new_player/player, var/rank, var/latejoin = 0) - Debug("Running AR, Player: [player], Rank: [rank], LJ: [latejoin]") - if(player && player.mind && rank) - var/datum/job/job = GetJob(rank) - if(!job) - return 0 - if(job.minimum_character_age && (player.client.prefs.age < job.minimum_character_age)) - return 0 - if(jobban_isbanned(player, rank)) - return 0 - if(!job.player_old_enough(player.client)) - return 0 - if(!is_job_whitelisted(player, rank)) //VOREStation Code - return 0 - - var/position_limit = job.total_positions - if(!latejoin) - position_limit = job.spawn_positions - if((job.current_positions < position_limit) || position_limit == -1) - Debug("Player: [player] is now Rank: [rank], JCP:[job.current_positions], JPL:[position_limit]") - player.mind.assigned_role = rank - player.mind.role_alt_title = GetPlayerAltTitle(player, rank) - unassigned -= player - job.current_positions++ - return 1 - Debug("AR has failed, Player: [player], Rank: [rank]") - return 0 - - proc/FreeRole(var/rank) //making additional slot on the fly +/datum/controller/subsystem/jobs/proc/AssignRole(var/mob/new_player/player, var/rank, var/latejoin = 0) + Debug("Running AR, Player: [player], Rank: [rank], LJ: [latejoin]") + if(player && player.mind && rank) var/datum/job/job = GetJob(rank) - if(job && job.total_positions != -1) - job.total_positions++ + if(!job) + return 0 + if(job.minimum_character_age && (player.client.prefs.age < job.minimum_character_age)) + return 0 + if(jobban_isbanned(player, rank)) + return 0 + if(!job.player_old_enough(player.client)) + return 0 + if(!is_job_whitelisted(player, rank)) //VOREStation Code + return 0 + + var/position_limit = job.total_positions + if(!latejoin) + position_limit = job.spawn_positions + if((job.current_positions < position_limit) || position_limit == -1) + Debug("Player: [player] is now Rank: [rank], JCP:[job.current_positions], JPL:[position_limit]") + player.mind.assigned_role = rank + player.mind.role_alt_title = GetPlayerAltTitle(player, rank) + unassigned -= player + job.current_positions++ return 1 - return 0 + Debug("AR has failed, Player: [player], Rank: [rank]") + return 0 - proc/FindOccupationCandidates(datum/job/job, level, flag) - Debug("Running FOC, Job: [job], Level: [level], Flag: [flag]") - var/list/candidates = list() - for(var/mob/new_player/player in unassigned) - if(jobban_isbanned(player, job.title)) - Debug("FOC isbanned failed, Player: [player]") - continue - if(!job.player_old_enough(player.client)) - Debug("FOC player not old enough, Player: [player]") - continue - if(job.minimum_character_age && (player.client.prefs.age < job.minimum_character_age)) - Debug("FOC character not old enough, Player: [player]") - continue - //VOREStation Code Start - if(!is_job_whitelisted(player, job.title)) - Debug("FOC is_job_whitelisted failed, Player: [player]") - continue - //VOREStation Code End - if(flag && (!player.client.prefs.be_special & flag)) - Debug("FOC flag failed, Player: [player], Flag: [flag], ") - continue - if(player.client.prefs.GetJobDepartment(job, level) & job.flag) - Debug("FOC pass, Player: [player], Level:[level]") - candidates += player - return candidates +/datum/controller/subsystem/jobs/proc/FreeRole(var/rank) //making additional slot on the fly + var/datum/job/job = GetJob(rank) + if(job && job.total_positions != -1) + job.total_positions++ + return 1 + return 0 - proc/GiveRandomJob(var/mob/new_player/player) - Debug("GRJ Giving random job, Player: [player]") - for(var/datum/job/job in shuffle(occupations)) - if(!job) - continue +/datum/controller/subsystem/jobs/proc/FindOccupationCandidates(datum/job/job, level, flag) + Debug("Running FOC, Job: [job], Level: [level], Flag: [flag]") + var/list/candidates = list() + for(var/mob/new_player/player in unassigned) + if(jobban_isbanned(player, job.title)) + Debug("FOC isbanned failed, Player: [player]") + continue + if(!job.player_old_enough(player.client)) + Debug("FOC player not old enough, Player: [player]") + continue + if(job.minimum_character_age && (player.client.prefs.age < job.minimum_character_age)) + Debug("FOC character not old enough, Player: [player]") + continue + //VOREStation Code Start + if(!is_job_whitelisted(player, job.title)) + Debug("FOC is_job_whitelisted failed, Player: [player]") + continue + //VOREStation Code End + if(flag && (!player.client.prefs.be_special & flag)) + Debug("FOC flag failed, Player: [player], Flag: [flag], ") + continue + if(player.client.prefs.GetJobDepartment(job, level) & job.flag) + Debug("FOC pass, Player: [player], Level:[level]") + candidates += player + return candidates - if(job.minimum_character_age && (player.client.prefs.age < job.minimum_character_age)) - continue +/datum/controller/subsystem/jobs/proc/GiveRandomJob(var/mob/new_player/player) + Debug("GRJ Giving random job, Player: [player]") + for(var/datum/job/job in shuffle(occupations)) + if(!job) + continue - if(istype(job, GetJob(USELESS_JOB))) // We don't want to give him assistant, that's boring! //VOREStation Edit - Visitor not Assistant - continue + if(job.minimum_character_age && (player.client.prefs.age < job.minimum_character_age)) + continue - if(job.title in command_positions) //If you want a command position, select it! - continue + if(istype(job, GetJob(USELESS_JOB))) // We don't want to give him assistant, that's boring! //VOREStation Edit - Visitor not Assistant + continue - if(jobban_isbanned(player, job.title)) - Debug("GRJ isbanned failed, Player: [player], Job: [job.title]") - continue + if(job.title in command_positions) //If you want a command position, select it! + continue - if(!job.player_old_enough(player.client)) - Debug("GRJ player not old enough, Player: [player]") - continue + if(jobban_isbanned(player, job.title)) + Debug("GRJ isbanned failed, Player: [player], Job: [job.title]") + continue - //VOREStation Code Start - if(!is_job_whitelisted(player, job.title)) - Debug("GRJ player not whitelisted for this job, Player: [player], Job: [job.title]") - continue - //VOREStation Code End + if(!job.player_old_enough(player.client)) + Debug("GRJ player not old enough, Player: [player]") + continue - if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1) - Debug("GRJ Random job given, Player: [player], Job: [job]") - AssignRole(player, job.title) - unassigned -= player - break + //VOREStation Code Start + if(!is_job_whitelisted(player, job.title)) + Debug("GRJ player not whitelisted for this job, Player: [player], Job: [job.title]") + continue + //VOREStation Code End - proc/ResetOccupations() - for(var/mob/new_player/player in player_list) - if((player) && (player.mind)) - player.mind.assigned_role = null - player.mind.special_role = null - SetupOccupations() - unassigned = list() - return + if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1) + Debug("GRJ Random job given, Player: [player], Job: [job]") + AssignRole(player, job.title) + unassigned -= player + break + +/datum/controller/subsystem/jobs/proc/ResetOccupations() + for(var/mob/new_player/player in player_list) + if((player) && (player.mind)) + player.mind.assigned_role = null + player.mind.special_role = null + SetupOccupations() + unassigned = list() + return - ///This proc is called before the level loop of DivideOccupations() 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 - proc/FillHeadPosition() - for(var/level = 1 to 3) - for(var/command_position in command_positions) - var/datum/job/job = GetJob(command_position) - if(!job) continue - var/list/candidates = FindOccupationCandidates(job, level) - if(!candidates.len) continue - - // Build a weighted list, weight by age. - var/list/weightedCandidates = list() - for(var/mob/V in candidates) - // Log-out during round-start? What a bad boy, no head position for you! - if(!V.client) continue - var/age = V.client.prefs.age - - if(age < job.minimum_character_age) // Nope. - continue - - switch(age) - if(job.minimum_character_age to (job.minimum_character_age+10)) - weightedCandidates[V] = 3 // Still a bit young. - if((job.minimum_character_age+10) to (job.ideal_character_age-10)) - weightedCandidates[V] = 6 // Better. - if((job.ideal_character_age-10) to (job.ideal_character_age+10)) - weightedCandidates[V] = 10 // Great. - if((job.ideal_character_age+10) to (job.ideal_character_age+20)) - weightedCandidates[V] = 6 // Still good. - if((job.ideal_character_age+20) to INFINITY) - weightedCandidates[V] = 3 // Geezer. - else - // If there's ABSOLUTELY NOBODY ELSE - if(candidates.len == 1) weightedCandidates[V] = 1 - - - var/mob/new_player/candidate = pickweight(weightedCandidates) - if(AssignRole(candidate, command_position)) - return 1 - return 0 - - - ///This proc is called at the start of the level loop of DivideOccupations() and will cause head jobs to be checked before any other jobs of the same level - proc/CheckHeadPositions(var/level) +///This proc is called before the level loop of DivideOccupations() 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/controller/subsystem/jobs/proc/FillHeadPosition() + for(var/level = 1 to 3) for(var/command_position in command_positions) var/datum/job/job = GetJob(command_position) if(!job) continue var/list/candidates = FindOccupationCandidates(job, level) if(!candidates.len) continue - var/mob/new_player/candidate = pick(candidates) - AssignRole(candidate, command_position) - return + + // Build a weighted list, weight by age. + var/list/weightedCandidates = list() + for(var/mob/V in candidates) + // Log-out during round-start? What a bad boy, no head position for you! + if(!V.client) continue + var/age = V.client.prefs.age + + if(age < job.minimum_character_age) // Nope. + continue + + switch(age) + if(job.minimum_character_age to (job.minimum_character_age+10)) + weightedCandidates[V] = 3 // Still a bit young. + if((job.minimum_character_age+10) to (job.ideal_character_age-10)) + weightedCandidates[V] = 6 // Better. + if((job.ideal_character_age-10) to (job.ideal_character_age+10)) + weightedCandidates[V] = 10 // Great. + if((job.ideal_character_age+10) to (job.ideal_character_age+20)) + weightedCandidates[V] = 6 // Still good. + if((job.ideal_character_age+20) to INFINITY) + weightedCandidates[V] = 3 // Geezer. + else + // If there's ABSOLUTELY NOBODY ELSE + if(candidates.len == 1) weightedCandidates[V] = 1 + + + var/mob/new_player/candidate = pickweight(weightedCandidates) + if(AssignRole(candidate, command_position)) + return 1 + return 0 + + +///This proc is called at the start of the level loop of DivideOccupations() and will cause head jobs to be checked before any other jobs of the same level +/datum/controller/subsystem/jobs/proc/CheckHeadPositions(var/level) + for(var/command_position in command_positions) + var/datum/job/job = GetJob(command_position) + if(!job) continue + var/list/candidates = FindOccupationCandidates(job, level) + if(!candidates.len) continue + var/mob/new_player/candidate = pick(candidates) + AssignRole(candidate, command_position) + return /** Proc DivideOccupations * fills var "assigned_role" for all ready players. * This proc must not have any side effect besides of modifying "assigned_role". **/ - proc/DivideOccupations() - //Setup new player list and get the jobs list - Debug("Running DO") - SetupOccupations() +/datum/controller/subsystem/jobs/proc/DivideOccupations() + //Setup new player list and get the jobs list + Debug("Running DO") + SetupOccupations() - //Holder for Triumvirate is stored in the ticker, this just processes it - if(ticker && ticker.triai) - for(var/datum/job/A in occupations) - if(A.title == "AI") - A.spawn_positions = 3 - break - - //Get the players who are ready - for(var/mob/new_player/player in player_list) - if(player.ready && player.mind && !player.mind.assigned_role) - unassigned += player - - Debug("DO, Len: [unassigned.len]") - if(unassigned.len == 0) return 0 - - //Shuffle players and jobs - unassigned = shuffle(unassigned) - - HandleFeedbackGathering() - - //People who wants to be assistants, sure, go on. - Debug("DO, Running Assistant Check 1") - var/datum/job/assist = new DEFAULT_JOB_TYPE () - var/list/assistant_candidates = FindOccupationCandidates(assist, 3) - Debug("AC1, Candidates: [assistant_candidates.len]") - for(var/mob/new_player/player in assistant_candidates) - Debug("AC1 pass, Player: [player]") - AssignRole(player, USELESS_JOB) //VOREStation Edit - Visitor not Assistant - assistant_candidates -= player - Debug("DO, AC1 end") - - //Select one head - Debug("DO, Running Head Check") - FillHeadPosition() - Debug("DO, Head Check end") - - //Other jobs are now checked - Debug("DO, Running Standard Check") - - - // 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(occupations) - // var/list/disabled_jobs = ticker.mode.disabled_jobs // So we can use .Find down below without a colon. - for(var/level = 1 to 3) - //Check the head jobs first each level - CheckHeadPositions(level) - - // Loop through all unassigned players - for(var/mob/new_player/player in unassigned) - - // Loop through all jobs - for(var/datum/job/job in shuffledoccupations) // SHUFFLE ME BABY - if(!job || ticker.mode.disabled_jobs.Find(job.title) ) - continue - - if(jobban_isbanned(player, job.title)) - Debug("DO isbanned failed, Player: [player], Job:[job.title]") - continue - - if(!job.player_old_enough(player.client)) - Debug("DO player not old enough, Player: [player], Job:[job.title]") - continue - - // If the player wants that job on this level, then try give it to him. - if(player.client.prefs.GetJobDepartment(job, level) & job.flag) - - // If the job isn't filled - if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1) - Debug("DO pass, Player: [player], Level:[level], Job:[job.title]") - AssignRole(player, job.title) - unassigned -= player - 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/mob/new_player/player in unassigned) - if(player.client.prefs.alternate_option == GET_RANDOM_JOB) - GiveRandomJob(player) - /* - Old job system - for(var/level = 1 to 3) - for(var/datum/job/job in occupations) - Debug("Checking job: [job]") - if(!job) - continue - if(!unassigned.len) - break - if((job.current_positions >= job.spawn_positions) && job.spawn_positions != -1) - continue - var/list/candidates = FindOccupationCandidates(job, level) - while(candidates.len && ((job.current_positions < job.spawn_positions) || job.spawn_positions == -1)) - var/mob/new_player/candidate = pick(candidates) - Debug("Selcted: [candidate], for: [job.title]") - AssignRole(candidate, job.title) - candidates -= candidate*/ - - Debug("DO, Standard Check end") - - Debug("DO, Running AC2") - - // For those who wanted to be assistant if their preferences were filled, here you go. - for(var/mob/new_player/player in unassigned) - if(player.client.prefs.alternate_option == BE_ASSISTANT) - Debug("AC2 Assistant located, Player: [player]") - AssignRole(player, USELESS_JOB) //VOREStation Edit - Visitor not Assistant - - //For ones returning to lobby - for(var/mob/new_player/player in unassigned) - if(player.client.prefs.alternate_option == RETURN_TO_LOBBY) - player.ready = 0 - player.new_player_panel_proc() - unassigned -= player - return 1 - - - proc/EquipRank(var/mob/living/carbon/human/H, var/rank, var/joined_late = 0) - if(!H) - return - - var/datum/job/job = GetJob(rank) - var/list/spawn_in_storage = list() - - if(!joined_late) - var/obj/S = null - var/list/possible_spawns = list() - for(var/obj/effect/landmark/start/sloc in landmarks_list) - if(sloc.name != rank) continue - if(locate(/mob/living) in sloc.loc) continue - possible_spawns.Add(sloc) - if(possible_spawns.len) - S = pick(possible_spawns) - if(!S) - S = locate("start*[rank]") // use old stype - if(istype(S, /obj/effect/landmark/start) && istype(S.loc, /turf)) - H.forceMove(S.loc) - else - var/list/spawn_props = LateSpawn(H.client, rank) - var/turf/T = spawn_props["turf"] - H.forceMove(T) - - // Moving wheelchair if they have one - if(H.buckled && istype(H.buckled, /obj/structure/bed/chair/wheelchair)) - H.buckled.forceMove(H.loc) - H.buckled.setDir(H.dir) - - if(job) - - //Equip custom gear loadout. - var/list/custom_equip_slots = list() //If more than one item takes the same slot, all after the first one spawn in storage. - var/list/custom_equip_leftovers = list() - if(H.client.prefs.gear && H.client.prefs.gear.len && job.title != "Cyborg" && job.title != "AI") - for(var/thing in H.client.prefs.gear) - var/datum/gear/G = gear_datums[thing] - if(G) - var/permitted - if(G.allowed_roles) - for(var/job_name in G.allowed_roles) - if(job.title == job_name) - permitted = 1 - else - permitted = 1 - - if(G.whitelisted && !is_alien_whitelisted(H, all_species[G.whitelisted])) - - //if(G.whitelisted && (G.whitelisted != H.species.name || !is_alien_whitelisted(H, G.whitelisted))) - permitted = 0 - - if(!permitted) - H << "Your current species, job or whitelist status does not permit you to spawn with [thing]!" - continue - - if(G.slot == "implant") - var/obj/item/implant/I = G.spawn_item(H, H.client.prefs.gear[G.display_name]) - I.invisibility = 100 - I.implant_loadout(H) - continue - - if(G.slot && !(G.slot in custom_equip_slots)) - // This is a miserable way to fix the loadout overwrite bug, but the alternative requires - // adding an arg to a bunch of different procs. Will look into it after this merge. ~ Z - var/metadata = H.client.prefs.gear[G.display_name] - if(G.slot == slot_wear_mask || G.slot == slot_wear_suit || G.slot == slot_head) - custom_equip_leftovers += thing - else if(H.equip_to_slot_or_del(G.spawn_item(H, metadata), G.slot)) - H << "Equipping you with \the [thing]!" - custom_equip_slots.Add(G.slot) - else - custom_equip_leftovers.Add(thing) - else - spawn_in_storage += thing - //Equip job items. - job.setup_account(H) - job.equip(H, H.mind ? H.mind.role_alt_title : "") - job.equip_backpack(H) -// job.equip_survival(H) - job.apply_fingerprints(H) - if(job.title != "Cyborg" && job.title != "AI") - H.equip_post_job() - - //If some custom items could not be equipped before, try again now. - for(var/thing in custom_equip_leftovers) - var/datum/gear/G = gear_datums[thing] - if(G.slot in custom_equip_slots) - spawn_in_storage += thing - else - var/metadata = H.client.prefs.gear[G.display_name] - if(H.equip_to_slot_or_del(G.spawn_item(H, metadata), G.slot)) - H << "Equipping you with \the [thing]!" - custom_equip_slots.Add(G.slot) - else - spawn_in_storage += thing - else - H << "Your job is [rank] and the game just can't handle it! Please report this bug to an administrator." - - H.job = rank - log_game("JOINED [key_name(H)] as \"[rank]\"") - log_game("SPECIES [key_name(H)] is a: \"[H.species.name]\"") //VOREStation Add - - // If they're head, give them the account info for their department - if(H.mind && job.head_position) - var/remembered_info = "" - var/datum/money_account/department_account = department_accounts[job.department] - - if(department_account) - remembered_info += "Your department's account number is: #[department_account.account_number]
" - remembered_info += "Your department's account pin is: [department_account.remote_access_pin]
" - remembered_info += "Your department's account funds are: $[department_account.money]
" - - H.mind.store_memory(remembered_info) - - var/alt_title = null - if(H.mind) - H.mind.assigned_role = rank - alt_title = H.mind.role_alt_title - - switch(rank) - if("Cyborg") - return H.Robotize() - if("AI") - return H - if("Colony Director") - var/sound/announce_sound = (ticker.current_state <= GAME_STATE_SETTING_UP)? null : sound('sound/misc/boatswain.ogg', volume=20) - captain_announcement.Announce("All hands, [alt_title ? alt_title : "Colony Director"] [H.real_name] on deck!", new_sound=announce_sound) - - //Deferred item spawning. - if(spawn_in_storage && spawn_in_storage.len) - var/obj/item/storage/B - for(var/obj/item/storage/S in H.contents) - B = S - break - - if(!isnull(B)) - for(var/thing in spawn_in_storage) - var/datum/gear/G = gear_datums[thing] - var/obj/item/I = G.spawn_item(H, H.client.prefs.gear[G.display_name]) //Create the item... - if(B.can_be_inserted(I, 1)) //Try putting it in their backpack. - H << "Placing \the [I] in your [B.name]!" - I.forceMove(B) - continue - if(H.equip_to_appropriate_slot(I)) //Other slots? - H << "Equipping you with \the [I]!" - continue - if(H.put_in_hands(I)) //Well, hands? - H << "Placing \the [I] in your hand!" - continue - //Throw a tantrum, having exhausted all other options. - H << "Inventory space exhausted. Putting \the [I] on the ground!" - I.forceMove(get_turf(H)) - - else - H << "Failed to locate storage on your mob. Please report this at our Github. Dumping your loadout at your feet..." - for(var/thing in spawn_in_storage) - var/datum/gear/G = gear_datums[thing] - var/obj/item/I = G.spawn_item(H, H.client.prefs.gear[G.display_name]) - I.forceMove(get_turf(H)) - H << "Putting \the [I] on the ground!" - - - if(istype(H)) //give humans wheelchairs, if they need them. - var/obj/item/organ/external/l_foot = H.get_organ("l_foot") - var/obj/item/organ/external/r_foot = H.get_organ("r_foot") - var/obj/item/storage/S = locate() in H.contents - var/obj/item/wheelchair/R = null - if(S) - R = locate() in S.contents - if(!l_foot || !r_foot || R) - var/obj/structure/bed/chair/wheelchair/W = new /obj/structure/bed/chair/wheelchair(H.loc) - W.buckle_mob(H) - H.update_canmove() - W.setDir(H.dir) - W.add_fingerprint(H) - if(R) - W.color = R.color - qdel(R) - - H << "You are [job.total_positions == 1 ? "the" : "a"] [alt_title ? alt_title : rank]." - - if(job.supervisors) - H << "As the [alt_title ? alt_title : rank] you answer directly to [job.supervisors]. Special circumstances may change this." - - if(job.idtype) - spawnId(H, rank, alt_title) - H.equip_to_slot_or_del(new /obj/item/radio/headset(H), slot_l_ear) - H << "To speak on your department's radio channel use :h. For the use of other channels, examine your headset." - - if(job.req_admin_notify) - H << "You are playing a job that is important for Game Progression. If you have to disconnect, please notify the admins via adminhelp." - - // EMAIL GENERATION - // Email addresses will be created under this domain name. Mostly for the looks. - var/domain = "freemail.nt" - var/sanitized_name = sanitize(replacetext(replacetext(lowertext(H.real_name), " ", "."), "'", "")) - var/complete_login = "[sanitized_name]@[domain]" - - // It is VERY unlikely that we'll have two players, in the same round, with the same name and branch, but still, this is here. - // If such conflict is encountered, a random number will be appended to the email address. If this fails too, no email account will be created. - if(ntnet_global.does_email_exist(complete_login)) - complete_login = "[sanitized_name][random_id(/datum/computer_file/data/email_account/, 100, 999)]@[domain]" - - // If even fallback login generation failed, just don't give them an email. The chance of this happening is astronomically low. - if(ntnet_global.does_email_exist(complete_login)) - to_chat(H, "You were not assigned an email address.") - H.mind.store_memory("You were not assigned an email address.") - else - var/datum/computer_file/data/email_account/EA = new/datum/computer_file/data/email_account() - EA.password = GenerateKey() - EA.login = complete_login - to_chat(H, "Your email account address is [EA.login] and the password is [EA.password]. This information has also been placed into your notes.") - H.mind.store_memory("Your email account address is [EA.login] and the password is [EA.password].") - // END EMAIL GENERATION - - //Gives glasses to the vision impaired - if(H.disabilities & NEARSIGHTED) - var/equipped = H.equip_to_slot_or_del(new /obj/item/clothing/glasses/regular(H), slot_glasses) - if(equipped != 1) - var/obj/item/clothing/glasses/G = H.glasses - G.prescription = 1 - - ENABLE_BITFIELD(H.hud_updateflag, ID_HUD) - ENABLE_BITFIELD(H.hud_updateflag, IMPLOYAL_HUD) - ENABLE_BITFIELD(H.hud_updateflag, SPECIALROLE_HUD) - return H - - - proc/spawnId(var/mob/living/carbon/human/H, rank, title) - if(!H) return 0 - var/obj/item/card/id/C = H.get_equipped_item(slot_wear_id) - if(istype(C)) return 0 - - var/datum/job/job = null - for(var/datum/job/J in occupations) - if(J.title == rank) - job = J + //Holder for Triumvirate is stored in the ticker, this just processes it + if(ticker && ticker.triai) + for(var/datum/job/A in occupations) + if(A.title == "AI") + A.spawn_positions = 3 break - if(job) - if(job.title == "Cyborg") - return - else - C = new job.idtype(H) - C.access = job.get_access() - else - C = new /obj/item/card/id(H) - if(C) - C.rank = rank - C.assignment = title ? title : rank - H.set_id_info(C) + //Get the players who are ready + for(var/mob/new_player/player in player_list) + if(player.ready && player.mind && !player.mind.assigned_role) + unassigned += player - //put the player's account number onto the ID - if(H.mind && H.mind.initial_account) - C.associated_account_number = H.mind.initial_account.account_number + Debug("DO, Len: [unassigned.len]") + if(unassigned.len == 0) return 0 - H.equip_to_slot_or_del(C, slot_wear_id) + //Shuffle players and jobs + unassigned = shuffle(unassigned) -// H.equip_to_slot_or_del(new /obj/item/pda(H), slot_belt) - if(locate(/obj/item/pda,H)) - var/obj/item/pda/pda = locate(/obj/item/pda,H) - pda.owner = H.real_name - pda.ownjob = C.assignment - pda.ownrank = C.rank - pda.name = "PDA-[H.real_name] ([pda.ownjob])" + HandleFeedbackGathering() - return 1 + //People who wants to be assistants, sure, go on. + Debug("DO, Running Assistant Check 1") + var/datum/job/assist = new DEFAULT_JOB_TYPE () + var/list/assistant_candidates = FindOccupationCandidates(assist, 3) + Debug("AC1, Candidates: [assistant_candidates.len]") + for(var/mob/new_player/player in assistant_candidates) + Debug("AC1 pass, Player: [player]") + AssignRole(player, USELESS_JOB) //VOREStation Edit - Visitor not Assistant + assistant_candidates -= player + Debug("DO, AC1 end") + + //Select one head + Debug("DO, Running Head Check") + FillHeadPosition() + Debug("DO, Head Check end") + + //Other jobs are now checked + Debug("DO, Running Standard Check") - proc/LoadJobs(jobsfile) //ran during round setup, reads info from jobs.txt -- Urist - if(!config_legacy.load_jobs_from_txt) - return 0 + // 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. - var/list/jobEntries = file2list(jobsfile) + // Loop through all levels from high to low + var/list/shuffledoccupations = shuffle(occupations) + // var/list/disabled_jobs = ticker.mode.disabled_jobs // So we can use .Find down below without a colon. + for(var/level = 1 to 3) + //Check the head jobs first each level + CheckHeadPositions(level) - for(var/job in jobEntries) + // Loop through all unassigned players + for(var/mob/new_player/player in unassigned) + + // Loop through all jobs + for(var/datum/job/job in shuffledoccupations) // SHUFFLE ME BABY + if(!job || ticker.mode.disabled_jobs.Find(job.title) ) + continue + + if(jobban_isbanned(player, job.title)) + Debug("DO isbanned failed, Player: [player], Job:[job.title]") + continue + + if(!job.player_old_enough(player.client)) + Debug("DO player not old enough, Player: [player], Job:[job.title]") + continue + + // If the player wants that job on this level, then try give it to him. + if(player.client.prefs.GetJobDepartment(job, level) & job.flag) + + // If the job isn't filled + if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1) + Debug("DO pass, Player: [player], Level:[level], Job:[job.title]") + AssignRole(player, job.title) + unassigned -= player + 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/mob/new_player/player in unassigned) + if(player.client.prefs.alternate_option == GET_RANDOM_JOB) + GiveRandomJob(player) + /* + Old job system + for(var/level = 1 to 3) + for(var/datum/job/job in occupations) + Debug("Checking job: [job]") if(!job) continue - - job = trim(job) - if (!length(job)) + if(!unassigned.len) + break + if((job.current_positions >= job.spawn_positions) && job.spawn_positions != -1) continue + var/list/candidates = FindOccupationCandidates(job, level) + while(candidates.len && ((job.current_positions < job.spawn_positions) || job.spawn_positions == -1)) + var/mob/new_player/candidate = pick(candidates) + Debug("Selcted: [candidate], for: [job.title]") + AssignRole(candidate, job.title) + candidates -= candidate*/ - var/pos = findtext(job, "=") - var/name = null - var/value = null + Debug("DO, Standard Check end") - if(pos) - name = copytext(job, 1, pos) - value = copytext(job, pos + 1) + Debug("DO, Running AC2") + + // For those who wanted to be assistant if their preferences were filled, here you go. + for(var/mob/new_player/player in unassigned) + if(player.client.prefs.alternate_option == BE_ASSISTANT) + Debug("AC2 Assistant located, Player: [player]") + AssignRole(player, USELESS_JOB) //VOREStation Edit - Visitor not Assistant + + //For ones returning to lobby + for(var/mob/new_player/player in unassigned) + if(player.client.prefs.alternate_option == RETURN_TO_LOBBY) + player.ready = 0 + player.new_player_panel_proc() + unassigned -= player + return 1 + + +/datum/controller/subsystem/jobs/proc/EquipRank(var/mob/living/carbon/human/H, var/rank, var/joined_late = 0) + if(!H) + return + + var/datum/job/job = GetJob(rank) + var/list/spawn_in_storage = list() + + if(!joined_late) + var/obj/S = null + var/list/possible_spawns = list() + for(var/obj/effect/landmark/start/sloc in landmarks_list) + if(sloc.name != rank) continue + if(locate(/mob/living) in sloc.loc) continue + possible_spawns.Add(sloc) + if(possible_spawns.len) + S = pick(possible_spawns) + if(!S) + S = locate("start*[rank]") // use old stype + if(istype(S, /obj/effect/landmark/start) && istype(S.loc, /turf)) + H.forceMove(S.loc) + else + var/list/spawn_props = LateSpawn(H.client, rank) + var/turf/T = spawn_props["turf"] + H.forceMove(T) + + // Moving wheelchair if they have one + if(H.buckled && istype(H.buckled, /obj/structure/bed/chair/wheelchair)) + H.buckled.forceMove(H.loc) + H.buckled.setDir(H.dir) + + if(job) + + //Equip custom gear loadout. + var/list/custom_equip_slots = list() //If more than one item takes the same slot, all after the first one spawn in storage. + var/list/custom_equip_leftovers = list() + if(H.client.prefs.gear && H.client.prefs.gear.len && job.title != "Cyborg" && job.title != "AI") + for(var/thing in H.client.prefs.gear) + var/datum/gear/G = gear_datums[thing] + if(G) + var/permitted + if(G.allowed_roles) + for(var/job_name in G.allowed_roles) + if(job.title == job_name) + permitted = 1 + else + permitted = 1 + + if(G.whitelisted && !is_alien_whitelisted(H, all_species[G.whitelisted])) + + //if(G.whitelisted && (G.whitelisted != H.species.name || !is_alien_whitelisted(H, G.whitelisted))) + permitted = 0 + + if(!permitted) + H << "Your current species, job or whitelist status does not permit you to spawn with [thing]!" + continue + + if(G.slot == "implant") + var/obj/item/implant/I = G.spawn_item(H, H.client.prefs.gear[G.display_name]) + I.invisibility = 100 + I.implant_loadout(H) + continue + + if(G.slot && !(G.slot in custom_equip_slots)) + // This is a miserable way to fix the loadout overwrite bug, but the alternative requires + // adding an arg to a bunch of different procs. Will look into it after this merge. ~ Z + var/metadata = H.client.prefs.gear[G.display_name] + if(G.slot == slot_wear_mask || G.slot == slot_wear_suit || G.slot == slot_head) + custom_equip_leftovers += thing + else if(H.equip_to_slot_or_del(G.spawn_item(H, metadata), G.slot)) + H << "Equipping you with \the [thing]!" + custom_equip_slots.Add(G.slot) + else + custom_equip_leftovers.Add(thing) + else + spawn_in_storage += thing + //Equip job items. + job.setup_account(H) + job.equip(H, H.mind ? H.mind.role_alt_title : "") + job.equip_backpack(H) +// job.equip_survival(H) + job.apply_fingerprints(H) + if(job.title != "Cyborg" && job.title != "AI") + H.equip_post_job() + + //If some custom items could not be equipped before, try again now. + for(var/thing in custom_equip_leftovers) + var/datum/gear/G = gear_datums[thing] + if(G.slot in custom_equip_slots) + spawn_in_storage += thing else + var/metadata = H.client.prefs.gear[G.display_name] + if(H.equip_to_slot_or_del(G.spawn_item(H, metadata), G.slot)) + H << "Equipping you with \the [thing]!" + custom_equip_slots.Add(G.slot) + else + spawn_in_storage += thing + else + H << "Your job is [rank] and the game just can't handle it! Please report this bug to an administrator." + + H.job = rank + log_game("JOINED [key_name(H)] as \"[rank]\"") + log_game("SPECIES [key_name(H)] is a: \"[H.species.name]\"") //VOREStation Add + + // If they're head, give them the account info for their department + if(H.mind && job.head_position) + var/remembered_info = "" + var/datum/money_account/department_account = department_accounts[job.department] + + if(department_account) + remembered_info += "Your department's account number is: #[department_account.account_number]
" + remembered_info += "Your department's account pin is: [department_account.remote_access_pin]
" + remembered_info += "Your department's account funds are: $[department_account.money]
" + + H.mind.store_memory(remembered_info) + + var/alt_title = null + if(H.mind) + H.mind.assigned_role = rank + alt_title = H.mind.role_alt_title + + switch(rank) + if("Cyborg") + return H.Robotize() + if("AI") + return H + if("Colony Director") + var/sound/announce_sound = (ticker.current_state <= GAME_STATE_SETTING_UP)? null : sound('sound/misc/boatswain.ogg', volume=20) + captain_announcement.Announce("All hands, [alt_title ? alt_title : "Colony Director"] [H.real_name] on deck!", new_sound=announce_sound) + + //Deferred item spawning. + if(spawn_in_storage && spawn_in_storage.len) + var/obj/item/storage/B + for(var/obj/item/storage/S in H.contents) + B = S + break + + if(!isnull(B)) + for(var/thing in spawn_in_storage) + var/datum/gear/G = gear_datums[thing] + var/obj/item/I = G.spawn_item(H, H.client.prefs.gear[G.display_name]) //Create the item... + if(B.can_be_inserted(I, 1)) //Try putting it in their backpack. + H << "Placing \the [I] in your [B.name]!" + I.forceMove(B) + continue + if(H.equip_to_appropriate_slot(I)) //Other slots? + H << "Equipping you with \the [I]!" + continue + if(H.put_in_hands(I)) //Well, hands? + H << "Placing \the [I] in your hand!" + continue + //Throw a tantrum, having exhausted all other options. + H << "Inventory space exhausted. Putting \the [I] on the ground!" + I.forceMove(get_turf(H)) + + else + H << "Failed to locate storage on your mob. Please report this at our Github. Dumping your loadout at your feet..." + for(var/thing in spawn_in_storage) + var/datum/gear/G = gear_datums[thing] + var/obj/item/I = G.spawn_item(H, H.client.prefs.gear[G.display_name]) + I.forceMove(get_turf(H)) + H << "Putting \the [I] on the ground!" + + + if(istype(H)) //give humans wheelchairs, if they need them. + var/obj/item/organ/external/l_foot = H.get_organ("l_foot") + var/obj/item/organ/external/r_foot = H.get_organ("r_foot") + var/obj/item/storage/S = locate() in H.contents + var/obj/item/wheelchair/R = null + if(S) + R = locate() in S.contents + if(!l_foot || !r_foot || R) + var/obj/structure/bed/chair/wheelchair/W = new /obj/structure/bed/chair/wheelchair(H.loc) + W.buckle_mob(H) + H.update_canmove() + W.setDir(H.dir) + W.add_fingerprint(H) + if(R) + W.color = R.color + qdel(R) + + H << "You are [job.total_positions == 1 ? "the" : "a"] [alt_title ? alt_title : rank]." + + if(job.supervisors) + H << "As the [alt_title ? alt_title : rank] you answer directly to [job.supervisors]. Special circumstances may change this." + + if(job.idtype) + spawnId(H, rank, alt_title) + H.equip_to_slot_or_del(new /obj/item/radio/headset(H), slot_l_ear) + H << "To speak on your department's radio channel use :h. For the use of other channels, examine your headset." + + if(job.req_admin_notify) + H << "You are playing a job that is important for Game Progression. If you have to disconnect, please notify the admins via adminhelp." + + // EMAIL GENERATION + // Email addresses will be created under this domain name. Mostly for the looks. + var/domain = "freemail.nt" + var/sanitized_name = sanitize(replacetext(replacetext(lowertext(H.real_name), " ", "."), "'", "")) + var/complete_login = "[sanitized_name]@[domain]" + + // It is VERY unlikely that we'll have two players, in the same round, with the same name and branch, but still, this is here. + // If such conflict is encountered, a random number will be appended to the email address. If this fails too, no email account will be created. + if(ntnet_global.does_email_exist(complete_login)) + complete_login = "[sanitized_name][random_id(/datum/computer_file/data/email_account/, 100, 999)]@[domain]" + + // If even fallback login generation failed, just don't give them an email. The chance of this happening is astronomically low. + if(ntnet_global.does_email_exist(complete_login)) + to_chat(H, "You were not assigned an email address.") + H.mind.store_memory("You were not assigned an email address.") + else + var/datum/computer_file/data/email_account/EA = new/datum/computer_file/data/email_account() + EA.password = GenerateKey() + EA.login = complete_login + to_chat(H, "Your email account address is [EA.login] and the password is [EA.password]. This information has also been placed into your notes.") + H.mind.store_memory("Your email account address is [EA.login] and the password is [EA.password].") + // END EMAIL GENERATION + + //Gives glasses to the vision impaired + if(H.disabilities & NEARSIGHTED) + var/equipped = H.equip_to_slot_or_del(new /obj/item/clothing/glasses/regular(H), slot_glasses) + if(equipped != 1) + var/obj/item/clothing/glasses/G = H.glasses + G.prescription = 1 + + ENABLE_BITFIELD(H.hud_updateflag, ID_HUD) + ENABLE_BITFIELD(H.hud_updateflag, IMPLOYAL_HUD) + ENABLE_BITFIELD(H.hud_updateflag, SPECIALROLE_HUD) + return H + + +/datum/controller/subsystem/jobs/proc/spawnId(var/mob/living/carbon/human/H, rank, title) + if(!H) return 0 + var/obj/item/card/id/C = H.get_equipped_item(slot_wear_id) + if(istype(C)) return 0 + + var/datum/job/job = null + for(var/datum/job/J in occupations) + if(J.title == rank) + job = J + break + + if(job) + if(job.title == "Cyborg") + return + else + C = new job.idtype(H) + C.access = job.get_access() + else + C = new /obj/item/card/id(H) + if(C) + C.rank = rank + C.assignment = title ? title : rank + H.set_id_info(C) + + //put the player's account number onto the ID + if(H.mind && H.mind.initial_account) + C.associated_account_number = H.mind.initial_account.account_number + + H.equip_to_slot_or_del(C, slot_wear_id) + +// H.equip_to_slot_or_del(new /obj/item/pda(H), slot_belt) + if(locate(/obj/item/pda,H)) + var/obj/item/pda/pda = locate(/obj/item/pda,H) + pda.owner = H.real_name + pda.ownjob = C.assignment + pda.ownrank = C.rank + pda.name = "PDA-[H.real_name] ([pda.ownjob])" + + return 1 + + +/datum/controller/subsystem/jobs/proc/LoadJobs(jobsfile) //ran during round setup, reads info from jobs.txt -- Urist + if(!config_legacy.load_jobs_from_txt) + return 0 + + var/list/jobEntries = file2list(jobsfile) + + for(var/job in jobEntries) + if(!job) + continue + + job = trim(job) + if (!length(job)) + continue + + var/pos = findtext(job, "=") + var/name = null + var/value = null + + if(pos) + name = copytext(job, 1, pos) + value = copytext(job, pos + 1) + else + continue + + if(name && value) + var/datum/job/J = GetJob(name) + if(!J) continue + J.total_positions = text2num(value) + J.spawn_positions = text2num(value) + if(name == "AI" || name == "Cyborg")//I dont like this here but it will do for now + J.total_positions = 0 + + return 1 + + +/datum/controller/subsystem/jobs/proc/HandleFeedbackGathering() + for(var/datum/job/job in occupations) + var/tmp_str = "|[job.title]|" + + var/level1 = 0 //high + var/level2 = 0 //medium + var/level3 = 0 //low + var/level4 = 0 //never + var/level5 = 0 //banned + var/level6 = 0 //account too young + for(var/mob/new_player/player in player_list) + if(!(player.ready && player.mind && !player.mind.assigned_role)) + continue //This player is not ready + if(jobban_isbanned(player, job.title)) + level5++ continue + if(!job.player_old_enough(player.client)) + level6++ + continue + if(player.client.prefs.GetJobDepartment(job, 1) & job.flag) + level1++ + else if(player.client.prefs.GetJobDepartment(job, 2) & job.flag) + level2++ + else if(player.client.prefs.GetJobDepartment(job, 3) & job.flag) + level3++ + else level4++ //not selected - if(name && value) - var/datum/job/J = GetJob(name) - if(!J) continue - J.total_positions = text2num(value) - J.spawn_positions = text2num(value) - if(name == "AI" || name == "Cyborg")//I dont like this here but it will do for now - J.total_positions = 0 - - return 1 - - - proc/HandleFeedbackGathering() - for(var/datum/job/job in occupations) - var/tmp_str = "|[job.title]|" - - var/level1 = 0 //high - var/level2 = 0 //medium - var/level3 = 0 //low - var/level4 = 0 //never - var/level5 = 0 //banned - var/level6 = 0 //account too young - for(var/mob/new_player/player in player_list) - if(!(player.ready && player.mind && !player.mind.assigned_role)) - continue //This player is not ready - if(jobban_isbanned(player, job.title)) - level5++ - continue - if(!job.player_old_enough(player.client)) - level6++ - continue - if(player.client.prefs.GetJobDepartment(job, 1) & job.flag) - level1++ - else if(player.client.prefs.GetJobDepartment(job, 2) & job.flag) - level2++ - else if(player.client.prefs.GetJobDepartment(job, 3) & job.flag) - level3++ - else level4++ //not selected - - tmp_str += "HIGH=[level1]|MEDIUM=[level2]|LOW=[level3]|NEVER=[level4]|BANNED=[level5]|YOUNG=[level6]|-" - feedback_add_details("job_preferences",tmp_str) + tmp_str += "HIGH=[level1]|MEDIUM=[level2]|LOW=[level3]|NEVER=[level4]|BANNED=[level5]|YOUNG=[level6]|-" + feedback_add_details("job_preferences",tmp_str) /datum/controller/occupations/proc/LateSpawn(var/client/C, var/rank) diff --git a/code/controllers/subsystems/master_controller.dm b/code/controllers/subsystems/master_controller.dm index 8b3a39e536b..0bc1c199f75 100644 --- a/code/controllers/subsystems/master_controller.dm +++ b/code/controllers/subsystems/master_controller.dm @@ -24,10 +24,10 @@ datum/controller/game_controller/New() qdel(master_controller) master_controller = src - if(!job_master) - job_master = new /datum/controller/occupations() - job_master.SetupOccupations() - job_master.LoadJobs("config/jobs.txt") + if(!SSjobs) + SSjobs = new /datum/controller/occupations() + SSjobs.SetupOccupations() + SSjobs.LoadJobs("config/jobs.txt") admin_notice("Job setup complete", R_DEBUG) if(!syndicate_code_phrase) syndicate_code_phrase = generate_code_phrase() diff --git a/code/controllers/subsystems/persist_vr.dm b/code/controllers/subsystems/persist_vr.dm index f30cc86b216..f65c612d051 100644 --- a/code/controllers/subsystems/persist_vr.dm +++ b/code/controllers/subsystems/persist_vr.dm @@ -72,10 +72,10 @@ SUBSYSTEM_DEF(persist) if(R) // We found someone with a record. var/recorded_rank = R.fields["real_rank"] if(recorded_rank) - . = job_master.GetJob(recorded_rank) + . = SSjobs.GetJob(recorded_rank) if(.) return // They have a custom title, aren't crew, or someone deleted their record, so we need a fallback method. // Let's check the mind. if(M.mind && M.mind.assigned_role) - . = job_master.GetJob(M.mind.assigned_role) + . = SSjobs.GetJob(M.mind.assigned_role) diff --git a/code/game/gamemodes/gameticker.dm b/code/game/gamemodes/gameticker.dm index 3e13fac8804..9d21d8ca7cb 100644 --- a/code/game/gamemodes/gameticker.dm +++ b/code/game/gamemodes/gameticker.dm @@ -95,10 +95,10 @@ var/global/datum/controller/gameticker/ticker to_chat(world, "Serious error in mode setup! Reverting to pregame lobby.") //Uses setup instead of set up due to computational context. return 0 - job_master.ResetOccupations() + SSjobs.ResetOccupations() src.mode.create_antagonists() src.mode.pre_setup() - job_master.DivideOccupations() // Apparently important for new antagonist system to register specific job antags properly. + SSjobs.DivideOccupations() // Apparently important for new antagonist system to register specific job antags properly. if(!src.mode.can_start()) world << "Unable to start [mode.name]. Not enough players readied, [config_legacy.player_requirements[mode.config_tag]] players needed. Reverting to pregame lobby." @@ -106,7 +106,7 @@ var/global/datum/controller/gameticker/ticker Master.SetRunLevel(RUNLEVEL_LOBBY) mode.fail_setup() mode = null - job_master.ResetOccupations() + SSjobs.ResetOccupations() return 0 if(hide_mode) @@ -302,7 +302,7 @@ var/global/datum/controller/gameticker/ticker if(player.mind.assigned_role == "Colony Director") captainless=0 if(!player_is_antag(player.mind, only_offstation_roles = 1)) - job_master.EquipRank(player, player.mind.assigned_role, 0) + SSjobs.EquipRank(player, player.mind.assigned_role, 0) UpdateFactionList(player) //equip_custom_items(player) //VOREStation Removal //player.apply_traits() //VOREStation Removal diff --git a/code/game/jobs/whitelist_vr.dm b/code/game/jobs/whitelist_vr.dm index 058a9c0dc17..d4809aa9f03 100644 --- a/code/game/jobs/whitelist_vr.dm +++ b/code/game/jobs/whitelist_vr.dm @@ -12,7 +12,7 @@ var/list/job_whitelist = list() job_whitelist = splittext(text, "\n") /proc/is_job_whitelisted(mob/M, var/rank) - var/datum/job/job = job_master.GetJob(rank) + var/datum/job/job = SSjobs.GetJob(rank) if(!job.whitelist_only) return 1 if(rank == USELESS_JOB) //VOREStation Edit - Visitor not Assistant diff --git a/code/game/machinery/computer/timeclock_vr.dm b/code/game/machinery/computer/timeclock_vr.dm index 32aa2da2698..9a1ba16fe75 100644 --- a/code/game/machinery/computer/timeclock_vr.dm +++ b/code/game/machinery/computer/timeclock_vr.dm @@ -81,7 +81,7 @@ if(card) data["card"] = "[card]" data["assignment"] = card.assignment - var/datum/job/job = job_master.GetJob(card.rank) + var/datum/job/job = SSjobs.GetJob(card.rank) if (job) data["job_datum"] = list( "title" = job.title, @@ -140,7 +140,7 @@ /obj/machinery/computer/timeclock/proc/getOpenOnDutyJobs(var/mob/user, var/department) var/list/available_jobs = list() - for(var/datum/job/job in job_master.occupations) + for(var/datum/job/job in SSjobs.occupations) if(job && job.is_position_available() && !job.whitelist_only && !jobban_isbanned(user,job.title) && job.player_old_enough(user.client)) if(job.department == department && !job.disallow_jobhop && job.timeoff_factor > 0) available_jobs += job.title @@ -151,14 +151,14 @@ /obj/machinery/computer/timeclock/proc/makeOnDuty(var/newjob) var/datum/job/foundjob = null - for(var/datum/job/job in job_master.occupations) + for(var/datum/job/job in SSjobs.occupations) if(newjob == job.title) foundjob = job break if(newjob in job.alt_titles) foundjob = job break - if(!newjob in getOpenOnDutyJobs(usr, job_master.GetJob(card.rank).department)) + if(!newjob in getOpenOnDutyJobs(usr, SSjobs.GetJob(card.rank).department)) return if(foundjob && card) card.access = foundjob.get_access() @@ -177,7 +177,7 @@ /obj/machinery/computer/timeclock/proc/makeOffDuty() var/datum/job/foundjob = null - for(var/datum/job/job in job_master.occupations) + for(var/datum/job/job in SSjobs.occupations) if(card.rank == job.title) foundjob = job break @@ -187,7 +187,7 @@ if(real_dept && real_dept == "Command") real_dept = "Civilian" var/datum/job/ptojob = null - for(var/datum/job/job in job_master.occupations) + for(var/datum/job/job in SSjobs.occupations) if(job.department == real_dept && job.timeoff_factor < 0) ptojob = job break diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index ea55eec3d39..6f07f441160 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -474,7 +474,7 @@ //Handle job slot/tater cleanup. var/job = to_despawn.mind.assigned_role - job_master.FreeRole(job) + SSjobs.FreeRole(job) if(to_despawn.mind.objectives.len) qdel(to_despawn.mind.objectives) diff --git a/code/game/objects/items/weapons/id cards/station_ids.dm b/code/game/objects/items/weapons/id cards/station_ids.dm index 0268187c68d..3a59a2eccf1 100755 --- a/code/game/objects/items/weapons/id cards/station_ids.dm +++ b/code/game/objects/items/weapons/id cards/station_ids.dm @@ -124,7 +124,7 @@ /obj/item/card/id/Initialize() . = ..() - var/datum/job/J = job_master.GetJob(rank) + var/datum/job/J = SSjobs.GetJob(rank) if(J) access = J.get_access() diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index eec358e20a1..71c568269ad 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -1010,7 +1010,7 @@ var/list/admin_verbs_event_manager = list( set category = "Admin" if(holder) var/list/jobs = list() - for (var/datum/job/J in job_master.occupations) + for (var/datum/job/J in SSjobs.occupations) if (J.current_positions >= J.total_positions && J.total_positions != -1) jobs += J.title if (!jobs.len) @@ -1018,7 +1018,7 @@ var/list/admin_verbs_event_manager = list( return var/job = input("Please select job slot to free", "Free job slot") as null|anything in jobs if (job) - job_master.FreeRole(job) + SSjobs.FreeRole(job) message_admins("A job slot for [job] has been opened by [key_name_admin(usr)]") return diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 359fd38d3f0..e1eb09c007b 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -376,7 +376,7 @@ if(!M.ckey) //sanity usr << "This mob has no ckey" return - if(!job_master) + if(!SSjobs) usr << "Job Master has not been setup!" return @@ -397,7 +397,7 @@ jobs += "Command Positions" for(var/jobPos in command_positions) if(!jobPos) continue - var/datum/job/job = job_master.GetJob(jobPos) + var/datum/job/job = SSjobs.GetJob(jobPos) if(!job) continue if(jobban_isbanned(M, job.title)) @@ -418,7 +418,7 @@ jobs += "Security Positions" for(var/jobPos in security_positions) if(!jobPos) continue - var/datum/job/job = job_master.GetJob(jobPos) + var/datum/job/job = SSjobs.GetJob(jobPos) if(!job) continue if(jobban_isbanned(M, job.title)) @@ -439,7 +439,7 @@ jobs += "Engineering Positions" for(var/jobPos in engineering_positions) if(!jobPos) continue - var/datum/job/job = job_master.GetJob(jobPos) + var/datum/job/job = SSjobs.GetJob(jobPos) if(!job) continue if(jobban_isbanned(M, job.title)) @@ -460,7 +460,7 @@ jobs += "Cargo Positions" for(var/jobPos in cargo_positions) if(!jobPos) continue - var/datum/job/job = job_master.GetJob(jobPos) + var/datum/job/job = SSjobs.GetJob(jobPos) if(!job) continue if(jobban_isbanned(M, job.title)) @@ -481,7 +481,7 @@ jobs += "Medical Positions" for(var/jobPos in medical_positions) if(!jobPos) continue - var/datum/job/job = job_master.GetJob(jobPos) + var/datum/job/job = SSjobs.GetJob(jobPos) if(!job) continue if(jobban_isbanned(M, job.title)) @@ -502,7 +502,7 @@ jobs += "Science Positions" for(var/jobPos in science_positions) if(!jobPos) continue - var/datum/job/job = job_master.GetJob(jobPos) + var/datum/job/job = SSjobs.GetJob(jobPos) if(!job) continue if(jobban_isbanned(M, job.title)) @@ -523,7 +523,7 @@ jobs += "Exploration Positions" for(var/jobPos in planet_positions) if(!jobPos) continue - var/datum/job/job = job_master.GetJob(jobPos) + var/datum/job/job = SSjobs.GetJob(jobPos) if(!job) continue if(jobban_isbanned(M, job.title)) @@ -544,7 +544,7 @@ jobs += "Civilian Positions" for(var/jobPos in civilian_positions) if(!jobPos) continue - var/datum/job/job = job_master.GetJob(jobPos) + var/datum/job/job = SSjobs.GetJob(jobPos) if(!job) continue if(jobban_isbanned(M, job.title)) @@ -571,7 +571,7 @@ jobs += "Non-human Positions" for(var/jobPos in nonhuman_positions) if(!jobPos) continue - var/datum/job/job = job_master.GetJob(jobPos) + var/datum/job/job = SSjobs.GetJob(jobPos) if(!job) continue if(jobban_isbanned(M, job.title)) @@ -648,7 +648,7 @@ alert("You cannot perform this action. You must be of a higher administrative rank!") return - if(!job_master) + if(!SSjobs) usr << "Job Master has not been setup!" return @@ -658,58 +658,58 @@ if("commanddept") for(var/jobPos in command_positions) if(!jobPos) continue - var/datum/job/temp = job_master.GetJob(jobPos) + var/datum/job/temp = SSjobs.GetJob(jobPos) if(!temp) continue joblist += temp.title if("securitydept") for(var/jobPos in security_positions) if(!jobPos) continue - var/datum/job/temp = job_master.GetJob(jobPos) + var/datum/job/temp = SSjobs.GetJob(jobPos) if(!temp) continue joblist += temp.title if("engineeringdept") for(var/jobPos in engineering_positions) if(!jobPos) continue - var/datum/job/temp = job_master.GetJob(jobPos) + var/datum/job/temp = SSjobs.GetJob(jobPos) if(!temp) continue joblist += temp.title if("cargodept") for(var/jobPos in cargo_positions) if(!jobPos) continue - var/datum/job/temp = job_master.GetJob(jobPos) + var/datum/job/temp = SSjobs.GetJob(jobPos) if(!temp) continue joblist += temp.title if("medicaldept") for(var/jobPos in medical_positions) if(!jobPos) continue - var/datum/job/temp = job_master.GetJob(jobPos) + var/datum/job/temp = SSjobs.GetJob(jobPos) if(!temp) continue joblist += temp.title if("sciencedept") for(var/jobPos in science_positions) if(!jobPos) continue - var/datum/job/temp = job_master.GetJob(jobPos) + var/datum/job/temp = SSjobs.GetJob(jobPos) if(!temp) continue joblist += temp.title //VOREStation Edit Start if("explorationdept") for(var/jobPos in planet_positions) if(!jobPos) continue - var/datum/job/temp = job_master.GetJob(jobPos) + var/datum/job/temp = SSjobs.GetJob(jobPos) if(!temp) continue joblist += temp.title //VOREStation Edit End if("civiliandept") for(var/jobPos in civilian_positions) if(!jobPos) continue - var/datum/job/temp = job_master.GetJob(jobPos) + var/datum/job/temp = SSjobs.GetJob(jobPos) if(!temp) continue joblist += temp.title if("nonhumandept") joblist += "pAI" for(var/jobPos in nonhuman_positions) if(!jobPos) continue - var/datum/job/temp = job_master.GetJob(jobPos) + var/datum/job/temp = SSjobs.GetJob(jobPos) if(!temp) continue joblist += temp.title else diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 81b7921fc92..a530634ab23 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -490,7 +490,7 @@ Traitors and the like can also be revived with the previous role mostly intact. //If desired, apply equipment. if(equipment) if(charjob) - job_master.EquipRank(new_character, charjob, 1) + SSjobs.EquipRank(new_character, charjob, 1) //equip_custom_items(new_character) //VOREStation Removal //If desired, add records. @@ -611,8 +611,8 @@ Traitors and the like can also be revived with the previous role mostly intact. if (!holder) to_chat(src, "Only administrators may use this command.") return - if(job_master) - for(var/datum/job/job in job_master.occupations) + if(SSjobs) + for(var/datum/job/job in SSjobs.occupations) to_chat(src, "[job.title]: [job.total_positions]") feedback_add_details("admin_verb","LFS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/tripAI.dm b/code/modules/admin/verbs/tripAI.dm index 98f60758546..b77c9650574 100644 --- a/code/modules/admin/verbs/tripAI.dm +++ b/code/modules/admin/verbs/tripAI.dm @@ -6,8 +6,8 @@ usr << "This option is currently only usable during pregame. This may change at a later date." return - if(job_master && ticker) - var/datum/job/job = job_master.GetJob("AI") + if(SSjobs && ticker) + var/datum/job/job = SSjobs.GetJob("AI") if(!job) usr << "Unable to locate the AI job" return diff --git a/code/modules/client/preference_setup/occupation/occupation.dm b/code/modules/client/preference_setup/occupation/occupation.dm index ca2bc4d787b..a3f7ebf8b4a 100644 --- a/code/modules/client/preference_setup/occupation/occupation.dm +++ b/code/modules/client/preference_setup/occupation/occupation.dm @@ -46,16 +46,16 @@ pref.job_engsec_low = sanitize_integer(pref.job_engsec_low, 0, 65535, initial(pref.job_engsec_low)) if(!(pref.player_alt_titles)) pref.player_alt_titles = new() - if(!job_master) + if(!SSjobs) return - for(var/datum/job/job in job_master.occupations) + for(var/datum/job/job in SSjobs.occupations) var/alt_title = pref.player_alt_titles[job.title] if(alt_title && !(alt_title in job.alt_titles)) pref.player_alt_titles -= job.title /datum/category_item/player_setup_item/occupation/content(mob/user, limit = 20, list/splitJobs = list("Pathfinder")) //VOREStation Edit - if(!job_master) + if(!SSjobs) return . = list() @@ -67,8 +67,8 @@ //The job before the current job. I only use this to get the previous jobs color when I'm filling in blank rows. var/datum/job/lastJob - if (!job_master) return - for(var/datum/job/job in job_master.occupations) + if (!SSjobs) return + for(var/datum/job/job in SSjobs.occupations) if(job.latejoin_only) continue //VOREStation Code if((++index >= limit) || (job.title in splitJobs)) /******* @@ -176,7 +176,7 @@ pref.player_alt_titles[job.title] = new_title /datum/category_item/player_setup_item/occupation/proc/SetJob(mob/user, role) - var/datum/job/job = job_master.GetJob(role) + var/datum/job/job = SSjobs.GetJob(role) if(!job) return 0 diff --git a/code/modules/gamemaster/game_master.dm b/code/modules/gamemaster/game_master.dm deleted file mode 100644 index 8baa0239653..00000000000 --- a/code/modules/gamemaster/game_master.dm +++ /dev/null @@ -1,156 +0,0 @@ -// This is a sort of successor to the various event systems created over the years. It is designed to be just a tad smarter than the -// previous ones, checking various things like player count, department size and composition, individual player activity, -// individual player (IC) skill, and such, in order to try to choose the best actions to take in order to add spice or variety to -// the round. - -/datum/game_master - var/suspended = TRUE // If true, it will not do anything. - var/ignore_time_restrictions = FALSE// Useful for debugging without needing to wait 20 minutes each time. - var/list/available_actions = list() // A list of 'actions' that the GM has access to, to spice up a round, such as events. - var/danger = 0 // The GM's best guess at how chaotic the round is. High danger makes it hold back. - var/staleness = -20 // Determines liklihood of the GM doing something, increases over time. - var/danger_modifier = 1 // Multiplier for how much 'danger' is accumulated. - var/staleness_modifier = 1 // Ditto. Higher numbers generally result in more events occuring in a round. - var/ticks_completed = 0 // Counts amount of ticks completed. Note that this ticks once a minute. - var/next_action = 0 // Minimum amount of time of nothingness until the GM can pick something again. - var/last_department_used = null // If an event was done for a specific department, it is written here, so it doesn't do it again. - -/datum/game_master/New() - ..() - available_actions = init_subtypes(/datum/gm_action) - for(var/datum/gm_action/action in available_actions) - action.gm = src - - var/config_setup_delay = TRUE - spawn(0) - while(config_setup_delay) - if(config) - config_setup_delay = FALSE - if(config_legacy.enable_game_master) - suspended = FALSE - else - sleep(30 SECONDS) - -/datum/game_master/process() - if(ticker && ticker.current_state == GAME_STATE_PLAYING && !suspended) - adjust_staleness(1) - adjust_danger(-1) - ticks_completed++ - - var/global_afk = metric.assess_all_living_mobs() - global_afk -= 100 - global_afk = abs(global_afk) - global_afk = round(global_afk / 100, 0.1) - adjust_staleness(global_afk) // Staleness increases faster if more people are less active. - - if(world.time < next_action && prob(staleness * 2) ) - log_debug("Game Master going to start something.") - start_action() - -// This is run before committing to an action/event. -/datum/game_master/proc/pre_action_checks() - if(!ticker || ticker.current_state != GAME_STATE_PLAYING) - log_debug("Game Master unable to start event: Ticker is nonexistant, or the game is not ongoing.") - return FALSE - if(suspended) - return FALSE - if(ignore_time_restrictions) - return TRUE - // Last minute antagging is bad for humans to do, so the GM will respect the start and end of the round. - var/mills = round_duration_in_ticks - var/mins = round((mills % 36000) / 600) - var/hours = round(mills / 36000) - - if(hours < 1 && mins <= 20) // Don't do anything for the first twenty minutes of the round. - log_debug("Game Master unable to start event: It is too early.") - return FALSE - if(hours >= 2 && mins >= 40) // Don't do anything in the last twenty minutes of the round, as well. - log_debug("Game Master unable to start event: It is too late.") - return FALSE - return TRUE - -/datum/game_master/proc/start_action() - if(!pre_action_checks()) // Make sure we're not doing last minute events, or early events. - return - log_debug("Game Master now starting action decision.") - var/list/most_active_departments = metric.assess_all_departments(3, list(last_department_used)) - var/list/best_actions = decide_best_action(most_active_departments) - - if(best_actions && best_actions.len) - var/list/weighted_actions = list() - for(var/datum/gm_action/action in best_actions) - if(action.chaotic > danger) - continue // We skip dangerous events when bad stuff is already occuring. - weighted_actions[action] = action.get_weight() - - var/datum/gm_action/choice = pickweight(weighted_actions) - if(choice) - log_debug("[choice.name] was chosen by the Game Master, and is now being ran.") - run_action(choice) - -/datum/game_master/proc/run_action(var/datum/gm_action/action) - action.set_up() - action.start() - action.announce() - if(action.chaotic) - danger += action.chaotic - if(action.length) - spawn(action.length) - action.end() - next_action = world.time + rand(15 MINUTES, 30 MINUTES) - last_department_used = action.departments[1] - - -/datum/game_master/proc/decide_best_action(var/list/most_active_departments) - if(!most_active_departments.len) // Server's empty? - log_debug("Game Master failed to find any active departments.") - return list() - - var/list/best_actions = list() // List of actions which involve the most active departments. - if(most_active_departments.len >= 2) - for(var/datum/gm_action/action in available_actions) - if(!action.enabled) - continue - // Try to incorporate an action with the top two departments first. - if(most_active_departments[1] in action.departments && most_active_departments[2] in action.departments) - best_actions.Add(action) - log_debug("[action.name] is being considered because both most active departments are involved.") - - if(best_actions.len) // We found something for those two, let's do it. - return best_actions - - // Otherwise we probably couldn't find something for the second highest group, so let's ignore them. - for(var/datum/gm_action/action in available_actions) - if(!action.enabled) - continue - if(most_active_departments[1] in action.departments) - best_actions.Add(action) - log_debug("[action.name] is being considered because the most active department is involved.") - - if(best_actions.len) // Found something for the one guy. - return best_actions - - // At this point we should expand our horizons. - for(var/datum/gm_action/action in available_actions) - if(!action.enabled) - continue - if(ROLE_EVERYONE in action.departments) - best_actions.Add(action) - log_debug("[action.name] is being considered because it involves everyone.") - - if(best_actions.len) // Finally, perhaps? - return best_actions - - // Just give a random event if for some reason it still can't make up its mind. - for(var/datum/gm_action/action in available_actions) - if(!action.enabled) - continue - best_actions.Add(action) - log_debug("[action.name] is being considered because everything else failed.") - - if(best_actions.len) // Finally, perhaps? - return best_actions - else - log_debug("Game Master failed to find a suitable event, something very wrong is going on.") - - diff --git a/code/modules/mob/dead/observer/free_vr.dm b/code/modules/mob/dead/observer/free_vr.dm index dca361725f6..71ab6f6b352 100644 --- a/code/modules/mob/dead/observer/free_vr.dm +++ b/code/modules/mob/dead/observer/free_vr.dm @@ -41,7 +41,7 @@ var/global/list/prevent_respawns = list() //Job slot cleanup var/job = src.mind.assigned_role - job_master.FreeRole(job) + SSjobs.FreeRole(job) //Their objectives cleanup if(src.mind.objectives.len) diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 6fe9aa988a8..be4217bf4aa 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -411,7 +411,7 @@ //Handle job slot/tater cleanup. var/job = mind.assigned_role - job_master.FreeRole(job) + SSjobs.FreeRole(job) if(mind.objectives.len) qdel(mind.objectives) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 577a9c9be93..94ac169025d 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -325,7 +325,7 @@ popup.open() /mob/new_player/proc/IsJobAvailable(rank) - var/datum/job/job = job_master.GetJob(rank) + var/datum/job/job = SSjobs.GetJob(rank) if(!job) return 0 if(!job.is_position_available()) return 0 if(jobban_isbanned(src,rank)) return 0 @@ -352,7 +352,7 @@ return 0 //Find our spawning point. - var/list/join_props = job_master.LateSpawn(client, rank) + var/list/join_props = SSjobs.LateSpawn(client, rank) var/turf/T = join_props["turf"] var/join_message = join_props["msg"] @@ -362,10 +362,10 @@ spawning = 1 close_spawn_windows() - job_master.AssignRole(src, rank, 1) + SSjobs.AssignRole(src, rank, 1) var/mob/living/character = create_character(T) //creates the human and transfers vars and mind - character = job_master.EquipRank(character, rank, 1) //equips the human + character = SSjobs.EquipRank(character, rank, 1) //equips the human UpdateFactionList(character) // AIs don't need a spawnpoint, they must spawn at an empty core @@ -435,7 +435,7 @@ dat += "Choose from the following open/valid positions:
" dat += "[show_hidden_jobs ? "Hide":"Show"] Hidden Jobs.
" - for(var/datum/job/job in job_master.occupations) + for(var/datum/job/job in SSjobs.occupations) if(job && IsJobAvailable(job.title)) // Checks for jobs with minimum age requirements if(job.minimum_character_age && (client.prefs.age < job.minimum_character_age)) diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm index ae706c9a27e..f8f31796d32 100644 --- a/code/modules/mob/new_player/preferences_setup.dm +++ b/code/modules/mob/new_player/preferences_setup.dm @@ -202,9 +202,9 @@ var/datum/job/previewJob // Determine what job is marked as 'High' priority, and dress them up as such. if(job_civilian_low & ASSISTANT) - previewJob = job_master.GetJob(USELESS_JOB) + previewJob = SSjobs.GetJob(USELESS_JOB) else - for(var/datum/job/job in job_master.occupations) + for(var/datum/job/job in SSjobs.occupations) var/job_flag switch(job.department_flag) if(CIVILIAN)