mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-21 20:20:33 +01:00
Fully implements the ID Card design document (#56910)
Co-authored-by: Rohesie <rohesie@gmail.com>
This commit is contained in:
@@ -0,0 +1,457 @@
|
||||
/**
|
||||
* Non-processing subsystem that holds various procs and data structures to manage ID cards, trims and access.
|
||||
*/
|
||||
SUBSYSTEM_DEF(id_access)
|
||||
name = "IDs and Access"
|
||||
init_order = INIT_ORDER_IDACCESS
|
||||
flags = SS_NO_FIRE
|
||||
|
||||
/// Dictionary of access flags. Keys are accesses. Values are their associated bitflags.
|
||||
var/list/flags_by_access = list()
|
||||
/// Dictionary of access lists. Keys are access flag names. Values are lists of all accesses as part of that access.
|
||||
var/list/accesses_by_flag = list()
|
||||
/// Dictionary of access flag string representations. Keys are bitflags. Values are their associated names.
|
||||
var/list/access_flag_string_by_flag = list()
|
||||
/// Dictionary of trim singletons. Keys are paths. Values are their associated singletons.
|
||||
var/list/trim_singletons_by_path = list()
|
||||
/// Dictionary of wildcard compatibility flags. Keys are strings for the wildcards. Values are their associated flags.
|
||||
var/list/wildcard_flags_by_wildcard = list()
|
||||
/// Dictionary of accesses based on station region. Keys are region strings. Values are lists of accesses.
|
||||
var/list/accesses_by_region = list()
|
||||
/// Specially formatted list for sending access levels to tgui interfaces.
|
||||
var/list/all_region_access_tgui = list()
|
||||
/// Dictionary of access names. Keys are access levels. Values are their associated names.
|
||||
var/list/desc_by_access = list()
|
||||
/// List of accesses for the Heads of each sub-department alongside the regions they control and their job name.
|
||||
var/list/sub_department_managers_tgui = list()
|
||||
/// Helper list containing all trim paths that can be used as job templates. Intended to be used alongside logic for ACCESS_CHANGE_IDS. Grab templates from sub_department_managers_tgui for Head of Staff restrictions.
|
||||
var/list/station_job_templates = list()
|
||||
/// Helper list containing all PDA paths that can be painted by station machines. Intended to be used alongside logic for ACCESS_CHANGE_IDS. Grab templates from sub_department_managers_tgui for Head of Staff restrictions.
|
||||
var/list/station_pda_templates = list()
|
||||
/// Helper list containing all station regions.
|
||||
var/list/station_regions = list()
|
||||
|
||||
/// The roundstart generated code for the spare ID safe. This is given to the Captain on shift start. If there's no Captain, it's given to the HoP. If there's no HoP
|
||||
var/spare_id_safe_code = ""
|
||||
|
||||
/datum/controller/subsystem/id_access/Initialize(timeofday)
|
||||
// We use this because creating the trim singletons requires the config to be loaded.
|
||||
SSmapping.HACK_LoadMapConfig()
|
||||
setup_access_flags()
|
||||
setup_region_lists()
|
||||
setup_trim_singletons()
|
||||
setup_wildcard_dict()
|
||||
setup_access_descriptions()
|
||||
setup_tgui_lists()
|
||||
|
||||
spare_id_safe_code = "[rand(0,9)][rand(0,9)][rand(0,9)][rand(0,9)][rand(0,9)]"
|
||||
|
||||
return ..()
|
||||
|
||||
/// Build access flag lists.
|
||||
/datum/controller/subsystem/id_access/proc/setup_access_flags()
|
||||
accesses_by_flag["[ACCESS_FLAG_COMMON]"] = COMMON_ACCESS
|
||||
for(var/access in accesses_by_flag["[ACCESS_FLAG_COMMON]"])
|
||||
flags_by_access |= list("[access]" = ACCESS_FLAG_COMMON)
|
||||
|
||||
accesses_by_flag["[ACCESS_FLAG_COMMAND]"] = COMMAND_ACCESS
|
||||
for(var/access in accesses_by_flag["[ACCESS_FLAG_COMMAND]"])
|
||||
flags_by_access |= list("[access]" = ACCESS_FLAG_COMMAND)
|
||||
|
||||
accesses_by_flag["[ACCESS_FLAG_PRV_COMMAND]"] = PRIVATE_COMMAND_ACCESS
|
||||
for(var/access in accesses_by_flag["[ACCESS_FLAG_PRV_COMMAND]"])
|
||||
flags_by_access |= list("[access]" = ACCESS_FLAG_PRV_COMMAND)
|
||||
|
||||
accesses_by_flag["[ACCESS_FLAG_CAPTAIN]"] = CAPTAIN_ACCESS
|
||||
for(var/access in accesses_by_flag["[ACCESS_FLAG_CAPTAIN]"])
|
||||
flags_by_access |= list("[access]" = ACCESS_FLAG_CAPTAIN)
|
||||
|
||||
accesses_by_flag["[ACCESS_FLAG_CENTCOM]"] = CENTCOM_ACCESS
|
||||
for(var/access in accesses_by_flag["[ACCESS_FLAG_CENTCOM]"])
|
||||
flags_by_access |= list("[access]" = ACCESS_FLAG_CENTCOM)
|
||||
|
||||
accesses_by_flag["[ACCESS_FLAG_SYNDICATE]"] = SYNDICATE_ACCESS
|
||||
for(var/access in accesses_by_flag["[ACCESS_FLAG_SYNDICATE]"])
|
||||
flags_by_access |= list("[access]" = ACCESS_FLAG_SYNDICATE)
|
||||
|
||||
accesses_by_flag["[ACCESS_FLAG_AWAY]"] = AWAY_ACCESS
|
||||
for(var/access in accesses_by_flag["[ACCESS_FLAG_AWAY]"])
|
||||
flags_by_access |= list("[access]" = ACCESS_FLAG_AWAY)
|
||||
|
||||
accesses_by_flag["[ACCESS_FLAG_SPECIAL]"] = CULT_ACCESS
|
||||
for(var/access in accesses_by_flag["[ACCESS_FLAG_SPECIAL]"])
|
||||
flags_by_access |= list("[access]" = ACCESS_FLAG_SPECIAL)
|
||||
|
||||
access_flag_string_by_flag["[ACCESS_FLAG_COMMON]"] = ACCESS_FLAG_COMMON_NAME
|
||||
access_flag_string_by_flag["[ACCESS_FLAG_COMMAND]"] = ACCESS_FLAG_COMMAND_NAME
|
||||
access_flag_string_by_flag["[ACCESS_FLAG_PRV_COMMAND]"] = ACCESS_FLAG_PRV_COMMAND_NAME
|
||||
access_flag_string_by_flag["[ACCESS_FLAG_CAPTAIN]"] = ACCESS_FLAG_CAPTAIN_NAME
|
||||
access_flag_string_by_flag["[ACCESS_FLAG_CENTCOM]"] = ACCESS_FLAG_CENTCOM_NAME
|
||||
access_flag_string_by_flag["[ACCESS_FLAG_SYNDICATE]"] = ACCESS_FLAG_SYNDICATE_NAME
|
||||
access_flag_string_by_flag["[ACCESS_FLAG_AWAY]"] = ACCESS_FLAG_AWAY_NAME
|
||||
access_flag_string_by_flag["[ACCESS_FLAG_SPECIAL]"] = ACCESS_FLAG_SPECIAL_NAME
|
||||
|
||||
/// Populates the region lists with data about which accesses correspond to which regions.
|
||||
/datum/controller/subsystem/id_access/proc/setup_region_lists()
|
||||
accesses_by_region[REGION_ALL_STATION] = REGION_ACCESS_ALL_STATION
|
||||
accesses_by_region[REGION_ALL_GLOBAL] = REGION_ACCESS_ALL_GLOBAL
|
||||
accesses_by_region[REGION_GENERAL] = REGION_ACCESS_GENERAL
|
||||
accesses_by_region[REGION_SECURITY] = REGION_ACCESS_SECURITY
|
||||
accesses_by_region[REGION_MEDBAY] = REGION_ACCESS_MEDBAY
|
||||
accesses_by_region[REGION_RESEARCH] = REGION_ACCESS_RESEARCH
|
||||
accesses_by_region[REGION_ENGINEERING] = REGION_ACCESS_ENGINEERING
|
||||
accesses_by_region[REGION_SUPPLY] = REGION_ACCESS_SUPPLY
|
||||
accesses_by_region[REGION_COMMAND] = REGION_ACCESS_COMMAND
|
||||
accesses_by_region[REGION_CENTCOM] = REGION_ACCESS_CENTCOM
|
||||
|
||||
station_regions = REGION_AREA_STATION
|
||||
|
||||
/// Instantiate trim singletons and add them to a list.
|
||||
/datum/controller/subsystem/id_access/proc/setup_trim_singletons()
|
||||
for(var/trim in typesof(/datum/id_trim))
|
||||
trim_singletons_by_path[trim] = new trim()
|
||||
|
||||
/// Creates various data structures that primarily get fed to tgui interfaces, although these lists are used in other places.
|
||||
/datum/controller/subsystem/id_access/proc/setup_tgui_lists()
|
||||
for(var/region in accesses_by_region)
|
||||
var/list/region_access = accesses_by_region[region]
|
||||
|
||||
var/parsed_accesses = list()
|
||||
|
||||
for(var/access in region_access)
|
||||
var/access_desc = get_access_desc(access)
|
||||
if(!access_desc)
|
||||
continue
|
||||
|
||||
parsed_accesses += list(list(
|
||||
"desc" = replacetext(access_desc, " ", " "),
|
||||
"ref" = access,
|
||||
))
|
||||
|
||||
all_region_access_tgui[region] = list(list(
|
||||
"name" = region,
|
||||
"accesses" = parsed_accesses,
|
||||
))
|
||||
|
||||
sub_department_managers_tgui = list(
|
||||
"[ACCESS_CAPTAIN]" = list(
|
||||
"regions" = list(REGION_COMMAND),
|
||||
"head" = "Captain",
|
||||
"templates" = list(),
|
||||
"pdas" = list(),
|
||||
),
|
||||
"[ACCESS_HOP]" = list(
|
||||
"regions" = list(REGION_GENERAL, REGION_SUPPLY),
|
||||
"head" = "Head of Personnel",
|
||||
"templates" = list(),
|
||||
"pdas" = list(),
|
||||
),
|
||||
"[ACCESS_HOS]" = list(
|
||||
"regions" = list(REGION_SECURITY),
|
||||
"head" = "Head of Security",
|
||||
"templates" = list(),
|
||||
"pdas" = list(),
|
||||
),
|
||||
"[ACCESS_CMO]" = list(
|
||||
"regions" = list(REGION_MEDBAY),
|
||||
"head" = "Chief Medical Officer",
|
||||
"templates" = list(),
|
||||
"pdas" = list(),
|
||||
),
|
||||
"[ACCESS_RD]" = list(
|
||||
"regions" = list(REGION_RESEARCH),
|
||||
"head" = "Research Director",
|
||||
"templates" = list(),
|
||||
"pdas" = list(),
|
||||
),
|
||||
"[ACCESS_CE]" = list(
|
||||
"regions" = list(REGION_ENGINEERING),
|
||||
"head" = "Chief Engineer",
|
||||
"templates" = list(),
|
||||
"pdas" = list(),
|
||||
),
|
||||
)
|
||||
|
||||
var/list/station_job_trims = subtypesof(/datum/id_trim/job)
|
||||
for(var/trim_path in station_job_trims)
|
||||
var/datum/id_trim/job/trim = trim_singletons_by_path[trim_path]
|
||||
if(!length(trim.template_access))
|
||||
continue
|
||||
|
||||
station_job_templates[trim_path] = trim.assignment
|
||||
for(var/access in trim.template_access)
|
||||
var/list/manager = sub_department_managers_tgui["[access]"]
|
||||
if(!manager)
|
||||
if(access != ACCESS_CHANGE_IDS)
|
||||
WARNING("Invalid template access access \[[access]\] registered with [trim_path]. Template added to global list anyway.")
|
||||
continue
|
||||
var/list/templates = manager["templates"]
|
||||
templates[trim_path] = trim.assignment
|
||||
|
||||
var/list/all_pda_paths = typesof(/obj/item/pda)
|
||||
var/list/pda_regions = PDA_PAINTING_REGIONS
|
||||
for(var/pda_path in all_pda_paths)
|
||||
if(!(pda_path in pda_regions))
|
||||
continue
|
||||
|
||||
var/list/region_whitelist = pda_regions[pda_path]
|
||||
for(var/access_txt in sub_department_managers_tgui)
|
||||
var/list/manager_info = sub_department_managers_tgui[access_txt]
|
||||
var/list/manager_regions = manager_info["regions"]
|
||||
for(var/whitelisted_region in region_whitelist)
|
||||
if(!(whitelisted_region in manager_regions))
|
||||
continue
|
||||
var/list/manager_pdas = manager_info["pdas"]
|
||||
var/obj/item/pda/fake_pda = pda_path
|
||||
manager_pdas[pda_path] = initial(fake_pda.name)
|
||||
station_pda_templates[pda_path] = initial(fake_pda.name)
|
||||
|
||||
/// Set up dictionary to convert wildcard names to flags.
|
||||
/datum/controller/subsystem/id_access/proc/setup_wildcard_dict()
|
||||
wildcard_flags_by_wildcard[WILDCARD_NAME_ALL] = WILDCARD_FLAG_ALL
|
||||
wildcard_flags_by_wildcard[WILDCARD_NAME_COMMON] = WILDCARD_FLAG_COMMON
|
||||
wildcard_flags_by_wildcard[WILDCARD_NAME_COMMAND] = WILDCARD_FLAG_COMMAND
|
||||
wildcard_flags_by_wildcard[WILDCARD_NAME_PRV_COMMAND] = WILDCARD_FLAG_PRV_COMMAND
|
||||
wildcard_flags_by_wildcard[WILDCARD_NAME_CAPTAIN] = WILDCARD_FLAG_CAPTAIN
|
||||
wildcard_flags_by_wildcard[WILDCARD_NAME_CENTCOM] = WILDCARD_FLAG_CENTCOM
|
||||
wildcard_flags_by_wildcard[WILDCARD_NAME_SYNDICATE] = WILDCARD_FLAG_SYNDICATE
|
||||
wildcard_flags_by_wildcard[WILDCARD_NAME_AWAY] = WILDCARD_FLAG_AWAY
|
||||
wildcard_flags_by_wildcard[WILDCARD_NAME_SPECIAL] = WILDCARD_FLAG_SPECIAL
|
||||
wildcard_flags_by_wildcard[WILDCARD_NAME_FORCED] = WILDCARD_FLAG_FORCED
|
||||
|
||||
/// Setup dictionary that converts access levels to text descriptions.
|
||||
/datum/controller/subsystem/id_access/proc/setup_access_descriptions()
|
||||
desc_by_access["[ACCESS_CARGO]"] = "Cargo Bay"
|
||||
desc_by_access["[ACCESS_SECURITY]"] = "Security"
|
||||
desc_by_access["[ACCESS_BRIG]"] = "Holding Cells"
|
||||
desc_by_access["[ACCESS_COURT]"] = "Courtroom"
|
||||
desc_by_access["[ACCESS_FORENSICS_LOCKERS]"] = "Forensics"
|
||||
desc_by_access["[ACCESS_MEDICAL]"] = "Medical"
|
||||
desc_by_access["[ACCESS_GENETICS]"] = "Genetics Lab"
|
||||
desc_by_access["[ACCESS_MORGUE]"] = "Morgue"
|
||||
desc_by_access["[ACCESS_RND]"] = "R&D Lab"
|
||||
desc_by_access["[ACCESS_TOXINS]"] = "Toxins Lab"
|
||||
desc_by_access["[ACCESS_TOXINS_STORAGE]"] = "Toxins Storage"
|
||||
desc_by_access["[ACCESS_CHEMISTRY]"] = "Chemistry Lab"
|
||||
desc_by_access["[ACCESS_RD]"] = "RD Office"
|
||||
desc_by_access["[ACCESS_BAR]"] = "Bar"
|
||||
desc_by_access["[ACCESS_JANITOR]"] = "Custodial Closet"
|
||||
desc_by_access["[ACCESS_ENGINE]"] = "Engineering"
|
||||
desc_by_access["[ACCESS_ENGINE_EQUIP]"] = "Power and Engineering Equipment"
|
||||
desc_by_access["[ACCESS_MAINT_TUNNELS]"] = "Maintenance"
|
||||
desc_by_access["[ACCESS_EXTERNAL_AIRLOCKS]"] = "External Airlocks"
|
||||
desc_by_access["[ACCESS_CHANGE_IDS]"] = "ID Console"
|
||||
desc_by_access["[ACCESS_AI_UPLOAD]"] = "AI Chambers"
|
||||
desc_by_access["[ACCESS_TELEPORTER]"] = "Teleporter"
|
||||
desc_by_access["[ACCESS_EVA]"] = "EVA"
|
||||
desc_by_access["[ACCESS_HEADS]"] = "Bridge"
|
||||
desc_by_access["[ACCESS_CAPTAIN]"] = "Captain"
|
||||
desc_by_access["[ACCESS_ALL_PERSONAL_LOCKERS]"] = "Personal Lockers"
|
||||
desc_by_access["[ACCESS_CHAPEL_OFFICE]"] = "Chapel Office"
|
||||
desc_by_access["[ACCESS_TECH_STORAGE]"] = "Technical Storage"
|
||||
desc_by_access["[ACCESS_ATMOSPHERICS]"] = "Atmospherics"
|
||||
desc_by_access["[ACCESS_CREMATORIUM]"] = "Crematorium"
|
||||
desc_by_access["[ACCESS_ARMORY]"] = "Armory"
|
||||
desc_by_access["[ACCESS_CONSTRUCTION]"] = "Construction"
|
||||
desc_by_access["[ACCESS_KITCHEN]"] = "Kitchen"
|
||||
desc_by_access["[ACCESS_HYDROPONICS]"] = "Hydroponics"
|
||||
desc_by_access["[ACCESS_LIBRARY]"] = "Library"
|
||||
desc_by_access["[ACCESS_LAWYER]"] = "Law Office"
|
||||
desc_by_access["[ACCESS_ROBOTICS]"] = "Robotics"
|
||||
desc_by_access["[ACCESS_VIROLOGY]"] = "Virology"
|
||||
desc_by_access["[ACCESS_PSYCHOLOGY]"] = "Psychology"
|
||||
desc_by_access["[ACCESS_CMO]"] = "CMO Office"
|
||||
desc_by_access["[ACCESS_QM]"] = "Quartermaster"
|
||||
desc_by_access["[ACCESS_SURGERY]"] = "Surgery"
|
||||
desc_by_access["[ACCESS_THEATRE]"] = "Theatre"
|
||||
desc_by_access["[ACCESS_RESEARCH]"] = "Science"
|
||||
desc_by_access["[ACCESS_MINING]"] = "Mining"
|
||||
desc_by_access["[ACCESS_MAILSORTING]"] = "Cargo Office"
|
||||
desc_by_access["[ACCESS_VAULT]"] = "Main Vault"
|
||||
desc_by_access["[ACCESS_MINING_STATION]"] = "Mining EVA"
|
||||
desc_by_access["[ACCESS_XENOBIOLOGY]"] = "Xenobiology Lab"
|
||||
desc_by_access["[ACCESS_HOP]"] = "HoP Office"
|
||||
desc_by_access["[ACCESS_HOS]"] = "HoS Office"
|
||||
desc_by_access["[ACCESS_CE]"] = "CE Office"
|
||||
desc_by_access["[ACCESS_PHARMACY]"] = "Pharmacy"
|
||||
desc_by_access["[ACCESS_RC_ANNOUNCE]"] = "RC Announcements"
|
||||
desc_by_access["[ACCESS_KEYCARD_AUTH]"] = "Keycode Auth."
|
||||
desc_by_access["[ACCESS_TCOMSAT]"] = "Telecommunications"
|
||||
desc_by_access["[ACCESS_GATEWAY]"] = "Gateway"
|
||||
desc_by_access["[ACCESS_SEC_DOORS]"] = "Brig"
|
||||
desc_by_access["[ACCESS_MINERAL_STOREROOM]"] = "Mineral Storage"
|
||||
desc_by_access["[ACCESS_MINISAT]"] = "AI Satellite"
|
||||
desc_by_access["[ACCESS_WEAPONS]"] = "Weapon Permit"
|
||||
desc_by_access["[ACCESS_NETWORK]"] = "Network Access"
|
||||
desc_by_access["[ACCESS_MECH_MINING]"] = "Mining Mech Access"
|
||||
desc_by_access["[ACCESS_MECH_MEDICAL]"] = "Medical Mech Access"
|
||||
desc_by_access["[ACCESS_MECH_SECURITY]"] = "Security Mech Access"
|
||||
desc_by_access["[ACCESS_MECH_SCIENCE]"] = "Science Mech Access"
|
||||
desc_by_access["[ACCESS_MECH_ENGINE]"] = "Engineering Mech Access"
|
||||
desc_by_access["[ACCESS_AUX_BASE]"] = "Auxiliary Base"
|
||||
desc_by_access["[ACCESS_CENT_GENERAL]"] = "Code Grey"
|
||||
desc_by_access["[ACCESS_CENT_THUNDER]"] = "Code Yellow"
|
||||
desc_by_access["[ACCESS_CENT_STORAGE]"] = "Code Orange"
|
||||
desc_by_access["[ACCESS_CENT_LIVING]"] = "Code Green"
|
||||
desc_by_access["[ACCESS_CENT_MEDICAL]"] = "Code White"
|
||||
desc_by_access["[ACCESS_CENT_TELEPORTER]"] = "Code Blue"
|
||||
desc_by_access["[ACCESS_CENT_SPECOPS]"] = "Code Black"
|
||||
desc_by_access["[ACCESS_CENT_CAPTAIN]"] = "Code Gold"
|
||||
desc_by_access["[ACCESS_CENT_BAR]"] = "Code Scotch"
|
||||
|
||||
/**
|
||||
* Returns the access bitflags associated with any given access level.
|
||||
*
|
||||
* In proc form due to accesses being stored in the list as text instead of numbers.
|
||||
* Arguments:
|
||||
* * access - Access as either pure number or as a string representation of the number.
|
||||
*/
|
||||
/datum/controller/subsystem/id_access/proc/get_access_flag(access)
|
||||
var/flag = flags_by_access["[access]"]
|
||||
return flag
|
||||
|
||||
/**
|
||||
* Returns the access description associated with any given access level.
|
||||
*
|
||||
* In proc form due to accesses being stored in the list as text instead of numbers.
|
||||
* Arguments:
|
||||
* * access - Access as either pure number or as a string representation of the number.
|
||||
*/
|
||||
/datum/controller/subsystem/id_access/proc/get_access_desc(access)
|
||||
return desc_by_access["[access]"]
|
||||
|
||||
/**
|
||||
* Builds and returns a list of accesses from a list of regions.
|
||||
*
|
||||
* Arguments:
|
||||
* * regions - A list of region defines.
|
||||
*/
|
||||
/datum/controller/subsystem/id_access/proc/get_region_access_list(list/regions)
|
||||
if(!length(regions))
|
||||
return
|
||||
|
||||
var/list/built_region_list = list()
|
||||
|
||||
for(var/region in regions)
|
||||
built_region_list |= accesses_by_region[region]
|
||||
|
||||
return built_region_list
|
||||
|
||||
/**
|
||||
* Returns the list of all accesses associated with any given access flag.
|
||||
*
|
||||
* In proc form due to accesses being stored in the list as text instead of numbers.
|
||||
* Arguments:
|
||||
* * flag - The flag to get access for as either a pure number of string representation of the flag.
|
||||
*/
|
||||
/datum/controller/subsystem/id_access/proc/get_flag_access_list(flag)
|
||||
return accesses_by_flag["[flag]"]
|
||||
|
||||
/**
|
||||
* Applies a trim singleton to a card.
|
||||
*
|
||||
* Returns FALSE if the trim could not be applied due to being incompatible with the card.
|
||||
* Incompatibility is defined as a card not being able to hold all the trim's required wildcards.
|
||||
* Returns TRUE otherwise.
|
||||
* Arguments:
|
||||
* * id_card - ID card to apply the trim_path to.
|
||||
* * trim_path - A trim path to apply to the card. Grabs the trim's associated singleton and applies it.
|
||||
* * copy_access - Boolean value. If true, the trim's access is also copied to the card.
|
||||
*/
|
||||
/datum/controller/subsystem/id_access/proc/apply_trim_to_card(obj/item/card/id/id_card, trim_path, copy_access = TRUE)
|
||||
var/datum/id_trim/trim = trim_singletons_by_path[trim_path]
|
||||
|
||||
if(!id_card.can_add_wildcards(trim.wildcard_access))
|
||||
return FALSE
|
||||
|
||||
id_card.clear_access()
|
||||
id_card.trim = trim
|
||||
|
||||
if(copy_access)
|
||||
id_card.access = trim.access.Copy()
|
||||
id_card.add_wildcards(trim.wildcard_access)
|
||||
|
||||
|
||||
if(trim.assignment)
|
||||
id_card.assignment = trim.assignment
|
||||
|
||||
id_card.update_label()
|
||||
id_card.update_icon()
|
||||
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Removes a trim from an ID card. Also removes all accesses from it too.
|
||||
*
|
||||
* Arguments:
|
||||
* * id_card - The ID card to remove the trim from.
|
||||
*/
|
||||
/datum/controller/subsystem/id_access/proc/remove_trim_from_card(obj/item/card/id/id_card)
|
||||
id_card.trim = null
|
||||
id_card.clear_access()
|
||||
id_card.update_label()
|
||||
id_card.update_icon()
|
||||
|
||||
/**
|
||||
* Applies a trim to a chameleon card. This is purely visual, utilising the card's override vars.
|
||||
*
|
||||
* Arguments:
|
||||
* * id_card - The chameleon card to apply the trim visuals to.
|
||||
* * trim_path - A trim path to apply to the card. Grabs the trim's associated singleton and applies it.
|
||||
* * check_forged - Boolean value. If TRUE, will not overwrite the card's assignment if the card has been forged.
|
||||
*/
|
||||
/datum/controller/subsystem/id_access/proc/apply_trim_to_chameleon_card(obj/item/card/id/advanced/chameleon/id_card, trim_path, check_forged = TRUE)
|
||||
var/datum/id_trim/trim = trim_singletons_by_path[trim_path]
|
||||
id_card.trim_icon_override = trim.trim_icon
|
||||
id_card.trim_state_override = trim.trim_state
|
||||
|
||||
if(!check_forged || !id_card.forged)
|
||||
id_card.assignment = trim.assignment
|
||||
|
||||
// We'll let the chameleon action update the card's label as necessary instead of doing it here.
|
||||
|
||||
/**
|
||||
* Removes a trim from a chameleon ID card.
|
||||
*
|
||||
* Arguments:
|
||||
* * id_card - The ID card to remove the trim from.
|
||||
*/
|
||||
/datum/controller/subsystem/id_access/proc/remove_trim_from_chameleon_card(obj/item/card/id/advanced/chameleon/id_card)
|
||||
id_card.trim_icon_override = null
|
||||
id_card.trim_state_override = null
|
||||
|
||||
/**
|
||||
* Adds the accesses associated with a trim to an ID card.
|
||||
*
|
||||
* Clears the card's existing access levels first.
|
||||
* Primarily intended for applying trim templates to cards. Will attempt to add as many ordinary access
|
||||
* levels as it can, without consuming any wildcards. Will then attempt to apply the trim-specific wildcards after.
|
||||
*
|
||||
* Arguments:
|
||||
* * id_card - The ID card to remove the trim from.
|
||||
*/
|
||||
/datum/controller/subsystem/id_access/proc/add_trim_access_to_card(obj/item/card/id/id_card, trim_path)
|
||||
var/datum/id_trim/trim = trim_singletons_by_path[trim_path]
|
||||
|
||||
id_card.clear_access()
|
||||
|
||||
id_card.add_access(trim.access, mode = TRY_ADD_ALL_NO_WILDCARD)
|
||||
id_card.add_wildcards(trim.wildcard_access, mode = TRY_ADD_ALL)
|
||||
|
||||
/**
|
||||
* Tallies up all accesses the card has that have flags greater than or equal to the access_flag supplied.
|
||||
*
|
||||
* Returns the number of accesses that have flags matching access_flag or a higher tier access.
|
||||
* Arguments:
|
||||
* * id_card - The ID card to tally up access for.
|
||||
* * access_flag - The minimum access flag required for an access to be tallied up.
|
||||
*/
|
||||
/datum/controller/subsystem/id_access/proc/tally_access(obj/item/card/id/id_card, access_flag = NONE)
|
||||
var/tally = 0
|
||||
|
||||
var/list/id_card_access = id_card.access
|
||||
for(var/access in id_card_access)
|
||||
if(flags_by_access["[access]"] >= access_flag)
|
||||
tally++
|
||||
|
||||
return tally
|
||||
@@ -16,8 +16,37 @@ SUBSYSTEM_DEF(job)
|
||||
|
||||
var/list/level_order = list(JP_HIGH,JP_MEDIUM,JP_LOW)
|
||||
|
||||
/// A list of all jobs associated with the station. These jobs also have various icons associated with them including sechud and card trims.
|
||||
var/list/station_jobs
|
||||
/// A list of additional jobs that have various icons associated with them including sechud and card trims.
|
||||
var/list/additional_jobs_with_icons
|
||||
/// A list of jobs associed with Centcom and should use the standard NT Centcom icons.
|
||||
var/list/centcom_jobs
|
||||
|
||||
/**
|
||||
* Keys should be assigned job roles. Values should be >= 1.
|
||||
* Represents the chain of command on the station. Lower numbers mean higher priority.
|
||||
* Used to give the Cap's Spare safe code to a an appropriate player.
|
||||
* Assumed Captain is always the highest in the chain of command.
|
||||
* See [/datum/controller/subsystem/ticker/proc/equip_characters]
|
||||
*/
|
||||
var/list/chain_of_command = list(
|
||||
"Captain" = 1,
|
||||
"Head of Personnel" = 2,
|
||||
"Research Director" = 3,
|
||||
"Chief Engineer" = 4,
|
||||
"Chief Medical Officer" = 5,
|
||||
"Head of Security" = 6,
|
||||
"Quartermaster" = 7)
|
||||
|
||||
/// If TRUE, some player has been assigned Captaincy or Acting Captaincy at some point during the shift and has been given the spare ID safe code.
|
||||
var/assigned_captain = FALSE
|
||||
/// If TRUE, the "Captain" job will always be given the code to the spare ID safe and always have a "Captain on deck!" announcement.
|
||||
var/always_promote_captain_job = TRUE
|
||||
|
||||
/datum/controller/subsystem/job/Initialize(timeofday)
|
||||
SSmapping.HACK_LoadMapConfig()
|
||||
setup_job_lists()
|
||||
if(!occupations.len)
|
||||
SetupOccupations()
|
||||
if(CONFIG_GET(flag/load_jobs_from_txt))
|
||||
@@ -403,9 +432,8 @@ SUBSYSTEM_DEF(job)
|
||||
message_admins(message)
|
||||
RejectPlayer(player)
|
||||
|
||||
|
||||
//Gives the player the stuff he should have with his rank
|
||||
/datum/controller/subsystem/job/proc/EquipRank(mob/M, rank, joined_late = FALSE)
|
||||
/datum/controller/subsystem/job/proc/EquipRank(mob/M, rank, joined_late = FALSE, is_captain = FALSE)
|
||||
var/mob/dead/new_player/newplayer
|
||||
var/mob/living/living_mob
|
||||
if(!joined_late)
|
||||
@@ -455,7 +483,7 @@ SUBSYSTEM_DEF(job)
|
||||
|
||||
to_chat(M, "<b>You are the [rank].</b>")
|
||||
if(job)
|
||||
var/new_mob = job.equip(living_mob, null, null, joined_late , null, M.client)//silicons override this proc to return a mob
|
||||
var/new_mob = job.equip(living_mob, null, null, joined_late , null, M.client, is_captain)//silicons override this proc to return a mob
|
||||
if(ismob(new_mob))
|
||||
living_mob = new_mob
|
||||
if(!joined_late)
|
||||
@@ -723,3 +751,56 @@ SUBSYSTEM_DEF(job)
|
||||
|
||||
/datum/controller/subsystem/job/proc/JobDebug(message)
|
||||
log_job_debug(message)
|
||||
|
||||
/// Builds various lists of jobs based on station, centcom and additional jobs with icons associated with them.
|
||||
/datum/controller/subsystem/job/proc/setup_job_lists()
|
||||
station_jobs = list("Assistant", "Captain", "Head of Personnel", "Bartender", "Cook", "Botanist", "Quartermaster", "Cargo Technician", \
|
||||
"Shaft Miner", "Clown", "Mime", "Janitor", "Curator", "Lawyer", "Chaplain", "Chief Engineer", "Station Engineer", \
|
||||
"Atmospheric Technician", "Chief Medical Officer", "Medical Doctor", "Paramedic", "Chemist", "Geneticist", "Virologist", "Psychologist", \
|
||||
"Research Director", "Scientist", "Roboticist", "Head of Security", "Warden", "Detective", "Security Officer", "Prisoner")
|
||||
|
||||
additional_jobs_with_icons = list("Emergency Response Team Commander", "Security Response Officer", "Engineering Response Officer", "Medical Response Officer", \
|
||||
"Entertainment Response Officer", "Religious Response Officer", "Janitorial Response Officer", "Death Commando", "Security Officer (Engineering)", \
|
||||
"Security Officer (Cargo)", "Security Officer (Medical)", "Security Officer (Science)")
|
||||
|
||||
centcom_jobs = list("Central Command","VIP Guest","Custodian","Thunderdome Overseer","CentCom Official","Medical Officer","Research Officer", \
|
||||
"Special Ops Officer","Admiral","CentCom Commander","CentCom Bartender","Private Security Force")
|
||||
|
||||
/obj/item/paper/fluff/spare_id_safe_code
|
||||
name = "Nanotrasen-Approved Spare ID Safe Code"
|
||||
desc = "Proof that you have been approved for Captaincy, with all its glory and all its horror."
|
||||
|
||||
/obj/item/paper/fluff/spare_id_safe_code/Initialize()
|
||||
. = ..()
|
||||
var/safe_code = SSid_access.spare_id_safe_code
|
||||
|
||||
info = "Captain's Spare ID safe code combination: [safe_code ? safe_code : "\[REDACTED\]"]<br><br>The spare ID can be found in its dedicated safe on the bridge.<br><br>If your job would not ordinarily have Head of Staff access, your ID card has been specially modified to possess it."
|
||||
|
||||
/datum/controller/subsystem/job/proc/promote_to_captain(mob/living/carbon/human/new_captain, acting_captain = FALSE)
|
||||
var/id_safe_code = SSid_access.spare_id_safe_code
|
||||
|
||||
if(!id_safe_code)
|
||||
CRASH("Cannot promote [new_captain.real_name] to Captain, there is no id_safe_code.")
|
||||
|
||||
var/paper = new /obj/item/paper/fluff/spare_id_safe_code()
|
||||
var/list/slots = list(
|
||||
LOCATION_LPOCKET = ITEM_SLOT_LPOCKET,
|
||||
LOCATION_RPOCKET = ITEM_SLOT_RPOCKET,
|
||||
LOCATION_BACKPACK = ITEM_SLOT_BACKPACK,
|
||||
LOCATION_HANDS = ITEM_SLOT_HANDS
|
||||
)
|
||||
var/where = new_captain.equip_in_one_of_slots(paper, slots, FALSE) || "at your feet"
|
||||
|
||||
if(acting_captain)
|
||||
to_chat(new_captain, "<span class='notice'>Due to your position in the chain of command, you have been promoted to Acting Captain. You can find in important note about this [where].</span>")
|
||||
else
|
||||
to_chat(new_captain, "<span class='notice'>You can find the code to obtain your spare ID from the secure safe on the Bridge [where].</span>")
|
||||
|
||||
// Force-give their ID card bridge access.
|
||||
var/obj/item/id_slot = new_captain.get_item_by_slot(ITEM_SLOT_ID)
|
||||
if(id_slot)
|
||||
var/obj/item/card/id/id_card = id_slot.GetID()
|
||||
if(!(ACCESS_HEADS in id_card.access))
|
||||
id_card.add_wildcards(list(ACCESS_HEADS), mode=FORCE_ADD_ALL)
|
||||
|
||||
assigned_captain = TRUE
|
||||
|
||||
@@ -381,23 +381,53 @@ SUBSYSTEM_DEF(ticker)
|
||||
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/equip_characters()
|
||||
var/captainless=1
|
||||
for(var/i in GLOB.new_player_list)
|
||||
var/mob/dead/new_player/N = i
|
||||
var/mob/living/carbon/human/player = N.new_character
|
||||
if(istype(player) && player.mind && player.mind.assigned_role)
|
||||
if(player.mind.assigned_role == "Captain")
|
||||
captainless=0
|
||||
if(player.mind.assigned_role != player.mind.special_role)
|
||||
SSjob.EquipRank(N, player.mind.assigned_role, 0)
|
||||
if(CONFIG_GET(flag/roundstart_traits) && ishuman(N.new_character))
|
||||
SSquirks.AssignQuirks(N.new_character, N.client, TRUE)
|
||||
var/captainless = TRUE
|
||||
|
||||
var/highest_rank = length(SSjob.chain_of_command) + 1
|
||||
var/list/spare_id_candidates = list()
|
||||
var/mob/dead/new_player/picked_spare_id_candidate
|
||||
|
||||
// Find a suitable player to hold captaincy.
|
||||
for(var/mob/dead/new_player/new_player_mob as anything in GLOB.new_player_list)
|
||||
if(is_banned_from(new_player_mob.ckey, list("Captain")))
|
||||
CHECK_TICK
|
||||
continue
|
||||
var/mob/living/carbon/human/new_player_human = new_player_mob.new_character
|
||||
if(istype(new_player_human) && new_player_human.mind?.assigned_role)
|
||||
// Keep a rolling tally of who'll get the cap's spare ID vault code.
|
||||
// Check assigned_role's priority and curate the candidate list appropriately.
|
||||
var/player_assigned_role = new_player_human.mind.assigned_role
|
||||
var/spare_id_priority = SSjob.chain_of_command[player_assigned_role]
|
||||
if(spare_id_priority)
|
||||
if(spare_id_priority < highest_rank)
|
||||
spare_id_candidates.Cut()
|
||||
spare_id_candidates += new_player_mob
|
||||
highest_rank = spare_id_priority
|
||||
else if(spare_id_priority == highest_rank)
|
||||
spare_id_candidates += new_player_mob
|
||||
CHECK_TICK
|
||||
|
||||
if(length(spare_id_candidates))
|
||||
picked_spare_id_candidate = pick(spare_id_candidates)
|
||||
|
||||
for(var/mob/dead/new_player/new_player_mob as anything in GLOB.new_player_list)
|
||||
var/mob/living/carbon/human/new_player_human = new_player_mob.new_character
|
||||
if(istype(new_player_human) && new_player_human.mind?.assigned_role)
|
||||
var/player_assigned_role = new_player_human.mind.assigned_role
|
||||
var/player_is_captain = (picked_spare_id_candidate == new_player_mob) || (SSjob.always_promote_captain_job && (player_assigned_role == "Captain"))
|
||||
if(player_is_captain)
|
||||
captainless = FALSE
|
||||
if(player_assigned_role != new_player_human.mind.special_role)
|
||||
SSjob.EquipRank(new_player_mob, player_assigned_role, FALSE, player_is_captain)
|
||||
if(CONFIG_GET(flag/roundstart_traits) && ishuman(new_player_human))
|
||||
SSquirks.AssignQuirks(new_player_human, new_player_mob.client, TRUE)
|
||||
CHECK_TICK
|
||||
|
||||
if(captainless)
|
||||
for(var/i in GLOB.new_player_list)
|
||||
var/mob/dead/new_player/N = i
|
||||
if(N.new_character)
|
||||
to_chat(N, "<span class='notice'>Captainship not forced on anyone.</span>")
|
||||
for(var/mob/dead/new_player/new_player_mob as anything in GLOB.new_player_list)
|
||||
var/mob/living/carbon/human/new_player_human = new_player_mob.new_character
|
||||
if(new_player_human)
|
||||
to_chat(new_player_mob, "<span class='notice'>Captainship not forced on anyone.</span>")
|
||||
CHECK_TICK
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/transfer_characters()
|
||||
|
||||
@@ -122,8 +122,8 @@ SUBSYSTEM_DEF(traumas)
|
||||
/obj/item/clothing/under/rank/security/head_of_security, /obj/item/clothing/under/rank/rnd/research_director,
|
||||
/obj/item/clothing/under/rank/medical/chief_medical_officer, /obj/item/clothing/under/rank/engineering/chief_engineer,
|
||||
/obj/item/clothing/under/rank/centcom/officer, /obj/item/clothing/under/rank/centcom/commander,
|
||||
/obj/item/melee/classic_baton/telescopic, /obj/item/card/id/silver, /obj/item/card/id/gold,
|
||||
/obj/item/card/id/captains_spare, /obj/item/card/id/centcom, /obj/machinery/door/airlock/command,
|
||||
/obj/item/melee/classic_baton/telescopic, /obj/item/card/id/advanced/silver, /obj/item/card/id/advanced/gold,
|
||||
/obj/item/card/id/advanced/centcom, /obj/machinery/door/airlock/command, /obj/item/card/id/advanced/black,
|
||||
)),
|
||||
|
||||
"the supernatural" = typecacheof(list(/obj/structure/destructible/cult, /obj/item/tome,
|
||||
|
||||
Reference in New Issue
Block a user