#define Debug(text) if (GLOB.Debug2) {job_debug += text} SUBSYSTEM_DEF(jobs) // Subsystem stuff. name = "Jobs" flags = SS_NO_FIRE init_order = INIT_ORDER_JOBS // Vars. var/list/datum/job/occupations = list() var/list/datum/job/name_occupations = list() //Dict of all jobs, keys are titles var/list/datum/job/type_occupations = list() //Dict of all jobs, keys are types var/list/mob/abstract/new_player/unassigned = list() var/list/job_debug = list() var/list/bitflag_to_job = list() var/list/factions = list() var/list/name_factions = list() var/datum/faction/default_faction = null var/safe_to_sanitize = FALSE var/list/deferred_preference_sanitizations = list() /datum/controller/subsystem/jobs/Initialize() SetupOccupations() LoadJobs("config/jobs.txt") InitializeFactions() ProcessSanitizationQueue() SSticker.setup_player_ready_list() return SS_INIT_SUCCESS /datum/controller/subsystem/jobs/Recover() occupations = SSjobs.occupations unassigned = SSjobs.unassigned job_debug = SSjobs.job_debug factions = SSjobs.factions if (islist(job_debug)) job_debug += "NOTICE: Job system Recover() triggered." /datum/controller/subsystem/jobs/proc/SetupOccupations(faction = "Station") occupations = list() var/list/all_jobs = SSatlas.current_map.allowed_jobs if(!all_jobs.len) to_world(SPAN_WARNING("Error setting up jobs, no job datums found!")) return FALSE for(var/J in all_jobs) var/datum/job/job = new J() if(!job || job.faction != faction) continue occupations += job name_occupations[job.title] = job if(LAZYLEN(job.alt_titles)) for(var/alt_title in job.alt_titles) name_occupations[alt_title] = job type_occupations[J] = job if(!length(bitflag_to_job["[job.department_flag]"])) bitflag_to_job["[job.department_flag]"] = list() bitflag_to_job["[job.department_flag]"]["[job.flag]"] = job if (GLOB.config && GLOB.config.use_age_restriction_for_jobs) job.fetch_age_restriction() return TRUE /datum/controller/subsystem/jobs/proc/GetJob(rank) if (!rank) return null return name_occupations[rank] /datum/controller/subsystem/jobs/proc/GetJobType(jobtype) if(!jobtype) return null return type_occupations[jobtype] /datum/controller/subsystem/jobs/proc/GetRandomJob() return pick(occupations) /datum/controller/subsystem/jobs/proc/ShouldCreateRecords(var/datum/mind/mind) if(player_is_antag(mind, only_offstation_roles = 1)) return 0 if(!mind.assigned_role) return 0 var/datum/job/job = GetJob(mind.assigned_role) if(!job) return 0 return job.create_record /datum/controller/subsystem/jobs/proc/GetPlayerAltTitle(mob/abstract/new_player/player, rank) . = player.client.prefs.GetPlayerAltTitle(GetJob(rank)) /datum/controller/subsystem/jobs/proc/AssignRole(mob/abstract/new_player/player, rank, latejoin = FALSE) Debug("Running AR, Player: [player], Rank: [rank], LJ: [latejoin]") if(player && player.mind && rank) var/datum/job/job = GetJob(rank) if(!job) return FALSE if(jobban_isbanned(player, rank)) return FALSE if(!player.IsJobAvailable(rank)) return FALSE if(!(player.client.prefs.GetPlayerAltTitle(job) in player.client.prefs.GetValidTitles(job))) to_chat(player, SPAN_WARNING("Your character is too young!")) return FALSE var/position_limit = job.get_total_positions() if(!latejoin) position_limit = job.get_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++ job.pre_spawn(player) return TRUE Debug("AR has failed, Player: [player], Rank: [rank]") return FALSE /datum/controller/subsystem/jobs/proc/FreeRole(rank) astype(GetJob(rank), /datum/job)?.current_positions-- /datum/controller/subsystem/jobs/proc/FreeJob(datum/job/job) job.current_positions-- /datum/controller/subsystem/jobs/proc/FindOccupationCandidates(datum/job/job, level, flag) Debug("Running FOC, Job: [job], Level: [level], Flag: [flag]") . = list() for(var/mob/abstract/new_player/player in unassigned) if(jobban_isbanned(player, job.title)) Debug("FOC isbanned failed, Player: [player]") continue if(flag && !(flag in player.client.prefs.be_special_role)) 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]") . += player /datum/controller/subsystem/jobs/proc/ResetOccupations() for(var/mob/abstract/new_player/player in GLOB.player_list) if((player) && (player.mind)) player.mind.assigned_role = null player.mind.special_role = null SetupOccupations() unassigned = list() ///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/abstract/new_player/candidate = pick(candidates) if(AssignRole(candidate, command_position)) return TRUE return FALSE ///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(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/abstract/new_player/candidate = pick(candidates) AssignRole(candidate, command_position) /** Proc DivideOccupations * fills var "assigned_role" for all ready players. * This proc must not have any side effect besides of modifying "assigned_role". **/ /datum/controller/subsystem/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(SSticker.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/abstract/new_player/player in GLOB.player_list) if(player.ready && player.mind && !player.mind.assigned_role) unassigned += player Debug("DO, Len: [unassigned.len]") if(unassigned.len == 0) return FALSE //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/abstract/new_player/player in assistant_candidates) Debug("AC1 pass, Player: [player]") AssignRole(player, "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/abstract/new_player/player in unassigned) // Loop through all jobs for(var/datum/job/job in shuffledoccupations) // SHUFFLE ME BABY if(!job || (job.title in SSticker.mode.disabled_jobs) ) //11/2/16 continue if(jobban_isbanned(player, job.title)) Debug("DO isbanned failed, 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.get_spawn_positions()) || job.get_spawn_positions() == -1) Debug("DO pass, Player: [player], Level:[level], Job:[job.title]") AssignRole(player, job.title) unassigned -= player break 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/abstract/new_player/player in unassigned) if(player.client.prefs.alternate_option == BE_ASSISTANT) Debug("AC2 Assistant located, Player: [player]") AssignRole(player, "Assistant") //For ones returning to lobby for(var/mob/abstract/new_player/player in unassigned) if(player.client.prefs.alternate_option == RETURN_TO_LOBBY) player.ready = 0 unassigned -= player return TRUE /datum/controller/subsystem/jobs/proc/EquipRank(mob/living/carbon/human/H, rank, joined_late = FALSE, spawning_at) if(!H) return null Debug("ER/([H]): Entry, joined_late=[joined_late].") if(SSatlas.current_sector.description) to_chat(H, SSatlas.current_sector.get_chat_description()) if(("Arrivals Shuttle" in SSatlas.current_map.allowed_spawns) && spawning_at == "Arrivals Shuttle") H.centcomm_despawn_timer = addtimer(CALLBACK(H, TYPE_PROC_REF(/mob/living, centcomm_timeout)), 10 MINUTES, TIMER_STOPPABLE) to_chat(H,SPAN_NOTICE("You have ten minutes to reach the [SSatlas.current_map.station_name] before you will be forced there.")) var/datum/job/job = GetJob(rank) var/list/spawn_in_storage = list() if (H.mind) H.mind.selected_faction = GetFaction(H) H.job = rank if(job) 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() //Equip job items. Debug("ER/([H]): Equipping custom loadout.") job.pre_equip(H) job.setup_account(H) job.after_spawn(H) EquipCustom(H, job, H.client.prefs, custom_equip_leftovers, spawn_in_storage, custom_equip_slots) job.equip(H) spawn_in_storage += EquipCustomDeferred(H, H.client.prefs, custom_equip_leftovers, custom_equip_slots) else to_chat(H,"Your job is [rank] and the game just can't handle it! Please report this bug to an administrator.") if(!joined_late || job.latejoin_at_spawnpoints) var/obj/S = get_roundstart_spawnpoint(rank) if(istype(S, /obj/effect/landmark/start) && istype(S.loc, /turf)) H.forceMove(S.loc) H.lastarea = get_area(H.loc) H.lastarea.set_lightswitch(TRUE) else LateSpawn(H, rank) // Moving wheelchair if they have one if(H.buckled_to && istype(H.buckled_to, /obj/structure/bed/stool/chair/office/wheelchair)) H.buckled_to.forceMove(H.loc) H.buckled_to.set_dir(H.dir) if(H.mind) // If they're a department supervisor/head give them the account info for that department var/remembered_info = "" for(var/department in job.departments) if(job.departments[department] & JOBROLE_SUPERVISOR) var/datum/money_account/department_account = SSeconomy.get_department_account(department) if(department_account) remembered_info += "[department] department's account number is: #[department_account.account_number]
" remembered_info += "[department] department's account pin is: [department_account.remote_access_pin]
" remembered_info += "[department] 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") Debug("ER/([H]): Job is Cyborg, returning early.") return H.Robotize() if("AI") Debug("ER/([H]): Job is AI, returning early.") return H //Deferred item spawning. if(LAZYLEN(spawn_in_storage)) EquipItemsStorage(H, H.client.prefs, spawn_in_storage) to_chat(H, "You are [job.get_total_positions() == 1 ? "the" : "a"] [alt_title ? alt_title : rank].") if(istype(H)) //give humans wheelchairs, if they need them. if(H.needs_wheelchair()) H.equip_wheelchair() if(job.supervisors) to_chat(H, "As [job.intro_prefix] [alt_title ? alt_title : rank] you answer directly to [job.supervisors]. Special circumstances may change this.") //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, TRUE) if(equipped != 1) var/obj/item/clothing/glasses/G = H.glasses G.prescription = 7 if(H.species && !H.species_items_equipped) H.species.equip_later_gear(H) H.species_items_equipped = TRUE // So shoes aren't silent if people never change 'em. H.update_noise_level() BITSET(H.hud_updateflag, ID_HUD) BITSET(H.hud_updateflag, IMPLOYAL_HUD) BITSET(H.hud_updateflag, SPECIALROLE_HUD) var/obj/item/clothing/under/uniform = H.w_uniform if(istype(uniform) && uniform.has_sensor) uniform.sensor_mode = SUIT_SENSOR_MODES[H.client.prefs.sensor_setting] INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(show_location_blurb), H.client, 10 SECONDS) if(spawning_at == "Arrivals Shuttle") to_chat(H, "[SSatlas.current_map.command_spawn_message]") if(joined_late) var/antag_count = 0 for(var/antag_type in SSticker.mode.antag_tags) var/datum/antagonist/A = GLOB.all_antag_types[antag_type] antag_count += A.get_active_antag_count() for(var/antag_type in SSticker.mode.antag_tags) var/datum/antagonist/A = GLOB.all_antag_types[antag_type] A.update_current_antag_max() if((A.role_type in H.client.prefs.be_special_role) && !(A.flags & ANTAG_OVERRIDE_JOB) && antag_count < A.cur_max) A.add_antagonist(H.mind) break Debug("ER/([H]): Completed.") return H /mob/living/proc/centcomm_timeout() if (!istype(get_area(src), /area/centcom/spawning)) return FALSE if (!client) SSjobs.centcomm_despawn_mob(src) return FALSE return TRUE /mob/living/carbon/human/centcomm_timeout() . = ..() if (!.) return FALSE var/datum/spawnpoint/spawnpos = SSatlas.spawn_locations["Cryogenic Storage"] if(spawnpos && istype(spawnpos)) to_chat(src, SPAN_WARNING("You come to the sudden realization that you never left the [SSatlas.current_map.station_name] at all! You were in cryo the whole time!")) src.forceMove(pick(spawnpos.turfs)) GLOB.global_announcer.autosay("[real_name], [mind.role_alt_title], [spawnpos.msg].", "Cryogenic Oversight") var/rank= src.mind.assigned_role SSjobs.EquipRank(src, rank, 1) else SSjobs.centcomm_despawn_mob(src) //somehow they can't spawn at cryo, so this is the only recourse of action. return TRUE /mob/living/silicon/robot/centcomm_timeout() . = ..() if (!.) return FALSE var/datum/spawnpoint/spawnpos = SSatlas.spawn_locations["Cyborg Storage"] if(spawnpos && istype(spawnpos)) to_chat(src, SPAN_WARNING("You come to the sudden realization that you never left the [SSatlas.current_map.station_name] at all! You were in robotic storage the whole time!")) src.forceMove(pick(spawnpos.turfs)) GLOB.global_announcer.autosay("[real_name], [mind.role_alt_title], [spawnpos.msg].", "Robotic Oversight") else SSjobs.centcomm_despawn_mob(src) return TRUE // Convenience wrapper. /datum/controller/subsystem/jobs/proc/centcomm_despawn_mob(mob/living/H) if(ishuman(H)) GLOB.global_announcer.autosay("[H.real_name], [H.mind.role_alt_title], has entered long-term storage.", "[SSatlas.current_map.dock_name] Cryogenic Oversight") H.visible_message(SPAN_NOTICE("[H.name] makes their way to the [SSatlas.current_map.dock_short]'s cryostorage, and departs."), SPAN_NOTICE("You make your way into [SSatlas.current_map.dock_short]'s cryostorage, and depart."), range = 3) DespawnMob(H) else if(!isDrone(H)) GLOB.global_announcer.autosay("[H.real_name], [H.mind.role_alt_title], has entered robotic storage.", "[SSatlas.current_map.dock_name] Robotic Oversight") H.visible_message(SPAN_NOTICE("[H.name] makes their way to the [SSatlas.current_map.dock_short]'s robotic storage, and departs."), SPAN_NOTICE("You make your way into [SSatlas.current_map.dock_short]'s robotic storage, and depart."), range = 3) DespawnMob(H) /datum/controller/subsystem/jobs/proc/LoadJobs(jobsfile) if (!GLOB.config.load_jobs_from_txt) return FALSE 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(!istype(J)) continue J.total_positions = text2num(value) J.spawn_positions = text2num(value) return TRUE /datum/controller/subsystem/jobs/proc/InitializeFactions() for (var/type in subtypesof(/datum/faction)) var/datum/faction/faction = new type() factions += faction name_factions[faction.name] = faction if (faction.is_default) if (default_faction) crash_with("Two default factions detected in SSjobs.") else default_faction = faction if (!default_faction) crash_with("No default faction assigned to SSjobs.") if (!factions.len) crash_with("No factions located in SSjobs.") /datum/controller/subsystem/jobs/proc/HandleFeedbackGathering() for(var/thing in occupations) var/datum/job/job = thing 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 for(var/mob/abstract/new_player/player in GLOB.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(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]|-" feedback_add_details("job_preferences",tmp_str) /datum/controller/subsystem/jobs/proc/LateSpawn(mob/living/carbon/human/H, rank) //spawn at one of the latespawn locations Debug("LS/([H]): Entry; rank=[rank]") var/datum/job/job = GetJob(rank) H.job = rank if(SSatlas.current_map.force_spawnpoint && LAZYLEN(GLOB.force_spawnpoints)) if(GLOB.force_spawnpoints[rank]) H.forceMove(pick(GLOB.force_spawnpoints[rank])) else H.forceMove(pick(GLOB.force_spawnpoints["Anyone"])) else if(job.latejoin_at_spawnpoints) for (var/thing in GLOB.landmarks_list) var/obj/effect/landmark/L = thing if(istype(L)) if(L.name == "LateJoin[rank]") H.forceMove(L.loc) return var/datum/spawnpoint/spawnpos if(H.client.prefs.spawnpoint in SSatlas.spawn_locations) spawnpos = SSatlas.spawn_locations[H.client.prefs.spawnpoint] if(rank == "Cyborg") spawnpos = new/datum/spawnpoint/cyborg if(!spawnpos) spawnpos = SSatlas.spawn_locations[SSatlas.current_map.default_spawn] if(spawnpos && istype(spawnpos)) if(spawnpos.check_job_spawning(rank)) if(istype(spawnpos, /datum/spawnpoint/cryo) && (rank in command_positions)) H.forceMove(pick(spawnpos.turfs)) else H.forceMove(pick(spawnpos.turfs)) . = spawnpos.msg spawnpos.after_join(H) else to_chat(H, "Your chosen spawnpoint ([spawnpos.display_name]) is unavailable for your chosen job. Spawning you at the [SSatlas.current_map.default_spawn] instead.") H.forceMove(pick(GLOB.latejoin)) . = "is inbound from the [SSatlas.current_map.default_spawn]" else H.forceMove(pick(GLOB.latejoin)) . = "is inbound from the [SSatlas.current_map.default_spawn]" H.mind.selected_faction = SSjobs.GetFaction(H) Debug("LS/([H]): Completed, spawning at area [H.loc.loc].") /datum/controller/subsystem/jobs/proc/DespawnMob(mob/living/carbon/human/H) //Update any existing objectives involving this mob. for(var/datum/objective/O in GLOB.all_objectives) // We don't want revs to get objectives that aren't for heads of staff. Letting // them win or lose based on cryo is silly so we remove the objective. if(O.target == H.mind) if(O.owner && O.owner.current) to_chat(O.owner.current, SPAN_WARNING("You get the feeling your target is no longer within your reach...")) qdel(O) //Handle job slot/tater cleanup. if (H.mind) var/datum/job/job = GetJob(H.mind.assigned_role) if (job) job.on_despawn(H) FreeJob(job) QDEL_LIST(H.mind.objectives) H.mind.special_role = null // Delete them from datacore. if(ishuman(H)) SSrecords.remove_record_by_field("fingerprint", H.get_full_print()) if(H.species) H.species.handle_despawn(H) SSrecords.remove_record_by_field("name", H.real_name) SSrecords.reset_manifest() log_and_message_admins("([H.mind.role_alt_title]) entered cryostorage.", user = H) //This should guarantee that ghosts don't spawn. H.ckey = null // Delete the mob. qdel(H) // Equips a human-type with their custom loadout crap. // Returns TRUE on success, FALSE otherwise. // H, job, and prefs MUST be supplied and not null. // leftovers, storage, custom_equip_slots can be passed if their return values are required (proc mutates passed list), or ignored if not required. /datum/controller/subsystem/jobs/proc/EquipCustom(mob/living/carbon/human/H, datum/job/job, datum/preferences/prefs, list/leftovers = null, list/storage = null, list/custom_equip_slots = list()) log_loadout("EC/([H]): Entry.") if (!istype(H) || !job) log_loadout("EC/([H]): Abort: invalid arguments.") return FALSE // if it's for their preview mob, let them wear it // so they can customize their loadout for their hologram if(!istype(H, /mob/living/carbon/human/dummy/mannequin)) switch (job.title) if ("AI", "Cyborg") log_loadout("EC/([H]): Abort: synthetic.") return FALSE for(var/thing in prefs.gear) var/datum/gear/G = GLOB.gear_datums[thing] if(G) if(G.augment) //augments are handled somewhere else continue var/metadata var/list/gear_test = prefs.gear[G.display_name] if(gear_test?.len) metadata = gear_test else metadata = list() var/cant_spawn_reason = G.cant_spawn_item_reason(null, metadata, H, job, prefs) if(cant_spawn_reason) to_chat(H, SPAN_WARNING(cant_spawn_reason)) continue // we want to handle spawning accessories after all the other clothing items have been spawned in var/list/spawn_data = G.get_spawn_item_data(H, metadata, H) if(ispath(spawn_data[1], /obj/item/clothing/accessory)) leftovers += thing 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/obj/item/CI = G.spawn_item(null,metadata, H) if (H.equip_to_slot_or_del(CI, G.slot, TRUE)) to_chat(H, SPAN_NOTICE("Equipping you with [thing]!")) if(G.slot != slot_tie) custom_equip_slots += G.slot log_loadout("EC/([H]): Equipped [CI] successfully.") else if (leftovers) leftovers += thing log_loadout("EC/([H]): Unable to equip [thing]; sending to overflow.") else if (storage) storage += thing log_loadout("EC/([H]): Unable to equip [thing]; sending to storage.") log_loadout("EC/([H]): Complete.") return TRUE // Attempts to equip custom items that failed to equip in EquipCustom. // Returns a list of items that failed to equip & should be put in storage if possible. // H and prefs must not be null. /datum/controller/subsystem/jobs/proc/EquipCustomDeferred(mob/living/carbon/human/H, datum/preferences/prefs, list/items, list/used_slots) . = list() log_loadout("ECD/([H]): Entry.") for (var/thing in items) var/datum/gear/G = GLOB.gear_datums[thing] if (G.slot in used_slots) . += thing else var/metadata var/list/gear_test = prefs.gear[G.display_name] if(gear_test?.len) metadata = gear_test else metadata = list() var/obj/item/CI = G.spawn_item(H, metadata, H) var/equip_slot = G.slot var/handled_accessory = FALSE if(isaccessory(CI)) var/datum/gear_tweak/accessory_slot/accessory_slot = locate(/datum/gear_tweak/accessory_slot) in G.gear_tweaks if(accessory_slot && metadata["[accessory_slot]"]) var/selected_slot = metadata["[accessory_slot]"] switch(selected_slot) if(GEAR_TWEAK_ACCESSORY_SLOT_UNDER) if(isclothing(H.w_uniform)) var/obj/item/clothing/worn_uniform = H.w_uniform if(worn_uniform.can_attach_accessory(CI)) to_chat(H, SPAN_NOTICE("Attaching \the [CI] to your uniform!")) worn_uniform.attach_accessory(H, CI) handled_accessory = TRUE if(GEAR_TWEAK_ACCESSORY_SLOT_SUIT) if(isclothing(H.wear_suit)) var/obj/item/clothing/worn_suit = H.wear_suit if(worn_suit.can_attach_accessory(CI)) to_chat(H, SPAN_NOTICE("Attaching \the [CI] to your suit!")) worn_suit.attach_accessory(H, CI) handled_accessory = TRUE if(GEAR_TWEAK_ACCESSORY_SLOT_SUIT_STANDALONE) equip_slot = slot_wear_suit if(!handled_accessory) if (H.equip_to_slot_or_del(CI, equip_slot, TRUE)) to_chat(H, SPAN_NOTICE("Equipping you with [thing]!")) used_slots += equip_slot log_loadout("ECD/([H]): Equipped [thing] successfully.") else . += thing log_loadout("ECD/([H]): Unable to equip [thing]; dumping into overflow.") log_loadout("ECD/([H]): Complete.") // Attempts to place everything in items into a storage object located on H, deleting them if they're unable to be inserted. // H and prefs must not be null. // Returns nothing. /datum/controller/subsystem/jobs/proc/EquipItemsStorage(mob/living/carbon/human/H, datum/preferences/prefs, list/items) log_loadout("EIS/([H]): Entry.") if (LAZYLEN(items)) log_loadout("EIS/([H]): [items.len] items.") var/obj/item/storage/B = locate() in H if (B) for(var/thing in items) to_chat(H, SPAN_NOTICE("Placing \the [thing] in your [B.name]!")) var/datum/gear/G = GLOB.gear_datums[thing] var/metadata var/list/gear_test = prefs.gear[G.display_name] if(gear_test?.len) metadata = gear_test else metadata = list() G.spawn_item(B, metadata, H) log_loadout("EIS/([H]): placed [thing] in [B].") for(var/obj/item/I in B.contents) I.in_storage = TRUE else to_chat(H, SPAN_DANGER("Failed to locate a storage object on your mob, either you spawned with no arms and no backpack or this is a bug.")) log_loadout("EIS/([H]): unable to equip; no storage.") log_loadout("EIS/([H]): Complete.") /datum/controller/subsystem/jobs/proc/get_roundstart_spawnpoint(var/rank) var/list/loc_list = list() for(var/obj/effect/landmark/start/sloc in GLOB.landmarks_list) if(sloc.name != rank) continue if(locate(/mob/living) in sloc.loc) continue loc_list += sloc if(loc_list.len) return pick(loc_list) else return locate("start*[rank]") // use old stype /datum/controller/subsystem/jobs/proc/GetFaction(mob/living/carbon/human/H) var/faction_name = H?.client?.prefs?.faction if (faction_name) return name_factions[faction_name] else return null /datum/controller/subsystem/jobs/proc/ProcessSanitizationQueue() safe_to_sanitize = TRUE for (var/p in deferred_preference_sanitizations) var/datum/callback/CB = deferred_preference_sanitizations[p] CB.Invoke() deferred_preference_sanitizations.Cut() /datum/controller/subsystem/jobs/proc/EquipAugments(mob/living/carbon/human/H, datum/preferences/prefs) log_loadout("EA/([H]): Entry.") if(!istype(H)) log_loadout("EA/([H]): Abort: invalid arguments.") return FALSE var/datum/job/rank = H.mind ? GetJob(H.mind.assigned_role) : prefs.return_chosen_high_job() switch (rank.title) if ("AI", "Cyborg") log_loadout("EA/([H]): Abort: synthetic.") return FALSE for(var/thing in prefs.gear) var/datum/gear/G = GLOB.gear_datums[thing] if(G) if(!G.augment) continue var/metadata var/list/gear_test = prefs.gear[G.display_name] if(gear_test?.len) metadata = gear_test else metadata = list() var/cant_spawn_reason = G.cant_spawn_item_reason(null, metadata, H, rank, prefs) if(cant_spawn_reason) to_chat(H, SPAN_WARNING(cant_spawn_reason)) continue var/obj/item/organ/A = G.spawn_item(H, metadata, H) if(!istype(A, /obj/item/organ/external)) var/obj/item/organ/external/affected = H.get_organ(A.parent_organ) A.replaced(H, affected) H.update_body() log_loadout("EA/([H]): Complete.") return TRUE /proc/show_location_blurb(client/C, duration) set waitfor = 0 var/style = "font-family: 'Fixedsys'; -dm-text-outline: 1 black; font-size: 11px;" var/text = "[worlddate2text()] [worldtime2text()]\n[station_name()], [SSatlas.current_sector.name]" text = uppertext(text) var/obj/effect/overlay/T = new() T.icon_state = "nothing" T.maptext_height = 64 T.maptext_width = 512 T.layer = FLOAT_LAYER T.plane = HUD_PLANE T.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA T.screen_loc = "LEFT+1,BOTTOM+2" C.screen += T animate(T, alpha = 255, time = 10) for(var/i = 1 to length(text)+1) T.maptext = "[copytext(text,1,i)] " sleep(1) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(fade_location_blurb), C, T), duration) /proc/fade_location_blurb(client/C, obj/T) animate(T, alpha = 0, time = 5) sleep(5) if(C) C.screen -= T qdel(T) #undef Debug