diff --git a/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm b/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm index db19c2473da..78b4c3766ce 100644 --- a/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm +++ b/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm @@ -17,7 +17,7 @@ /obj/item/storage/bag/ore, /obj/item/mining_scanner, /obj/item/flashlight/lantern, -/obj/item/card/id/mining, +/obj/item/card/id/advanced/mining, /turf/open/floor/plating, /area/ruin/powered/golem_ship) "d" = ( @@ -30,7 +30,7 @@ /obj/item/storage/bag/ore, /obj/item/mining_scanner, /obj/item/flashlight/lantern, -/obj/item/card/id/mining, +/obj/item/card/id/advanced/mining, /turf/open/floor/plating, /area/ruin/powered/golem_ship) "e" = ( diff --git a/_maps/RandomRuins/IceRuins/icemoon_surface_engioutpost.dmm b/_maps/RandomRuins/IceRuins/icemoon_surface_engioutpost.dmm index 27c5f1402f5..f3834cf194d 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_surface_engioutpost.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_surface_engioutpost.dmm @@ -319,14 +319,7 @@ /turf/open/floor/plating/asteroid/snow/icemoon, /area/icemoon/surface/outdoors) "be" = ( -/obj/item/card/id{ - access = list(200,204,11,12,10); - assignment = "Senior Station Engineer"; - desc = "A card used to provide ID and determine access across the station. There's blood dripping from the corner. Ew."; - name = "George 'Plastic' Miller's ID Card (Senior Station Engineer)"; - registered_age = 47; - registered_name = "George 'Plastic' Miller" - }, +/obj/item/card/id/advanced/engioutpost, /turf/open/floor/plating/asteroid/snow/icemoon, /area/icemoon/surface/outdoors) "bf" = ( diff --git a/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_village.dmm b/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_village.dmm index 73f32b5f328..2973413b171 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_village.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_village.dmm @@ -409,7 +409,7 @@ /obj/item/clothing/under/rank/cargo/miner, /obj/item/clothing/suit/hooded/wintercoat/miner, /obj/item/clothing/shoes/winterboots, -/obj/item/card/id/mining, +/obj/item/card/id/advanced/mining, /obj/effect/decal/cleanable/dirt, /turf/open/floor/wood, /area/ruin/powered) diff --git a/_maps/RandomRuins/SpaceRuins/bigderelict1.dmm b/_maps/RandomRuins/SpaceRuins/bigderelict1.dmm index bf6c64f8ad9..a8d1c5cd864 100644 --- a/_maps/RandomRuins/SpaceRuins/bigderelict1.dmm +++ b/_maps/RandomRuins/SpaceRuins/bigderelict1.dmm @@ -369,7 +369,6 @@ "bq" = ( /obj/effect/mob_spawn/human/corpse/nanotrasensoldier{ head = null; - id_job = "Tradepost Officer"; name = "Tradeport Officer"; random = 1 }, diff --git a/_maps/RandomRuins/SpaceRuins/forgottenship.dmm b/_maps/RandomRuins/SpaceRuins/forgottenship.dmm index 754774dd318..71b1cfac980 100644 --- a/_maps/RandomRuins/SpaceRuins/forgottenship.dmm +++ b/_maps/RandomRuins/SpaceRuins/forgottenship.dmm @@ -468,7 +468,7 @@ /obj/item/clothing/under/chameleon, /obj/item/pda/chameleon, /obj/item/clothing/mask/chameleon, -/obj/item/card/id/syndicate/anyone, +/obj/item/card/id/advanced/chameleon, /turf/open/floor/mineral/plastitanium, /area/ruin/space/has_grav/syndicate_forgotten_cargopod) "bv" = ( diff --git a/_maps/RandomZLevels/research.dmm b/_maps/RandomZLevels/research.dmm index b6573747b3f..abf313d686b 100644 --- a/_maps/RandomZLevels/research.dmm +++ b/_maps/RandomZLevels/research.dmm @@ -2863,13 +2863,9 @@ /turf/open/floor/iron/white, /area/awaymission/research/interior/cryo) "gw" = ( -/obj/effect/mob_spawn/human/doctor{ +/obj/effect/mob_spawn/human/geneticist{ brute_damage = 145; - id_job = "Geneticist"; - name = "Geneticist"; - oxy_damage = 55; - suit = /obj/item/clothing/suit/toggle/labcoat/genetics; - uniform = /obj/item/clothing/under/rank/rnd/geneticist + oxy_damage = 55 }, /obj/effect/decal/cleanable/blood, /obj/effect/turf_decal/tile/purple{ @@ -3842,13 +3838,9 @@ /turf/open/floor/iron/dark, /area/awaymission/research/interior/genetics) "in" = ( -/obj/effect/mob_spawn/human/doctor{ +/obj/effect/mob_spawn/human/geneticist{ brute_damage = 145; - id_job = "Geneticist"; - name = "Geneticist"; - oxy_damage = 55; - suit = /obj/item/clothing/suit/toggle/labcoat/genetics; - uniform = /obj/item/clothing/under/rank/rnd/geneticist + oxy_damage = 55 }, /obj/effect/decal/cleanable/blood, /obj/effect/turf_decal/tile/purple{ @@ -4646,13 +4638,9 @@ /turf/open/floor/iron/white, /area/awaymission/research/interior/medbay) "kj" = ( -/obj/effect/mob_spawn/human/doctor{ +/obj/effect/mob_spawn/human/geneticist{ brute_damage = 145; - id_job = "Geneticist"; - name = "Geneticist"; - oxy_damage = 55; - suit = /obj/item/clothing/suit/toggle/labcoat/genetics; - uniform = /obj/item/clothing/under/rank/rnd/geneticist + oxy_damage = 55 }, /obj/effect/decal/cleanable/blood, /obj/effect/turf_decal/tile/blue{ @@ -5522,13 +5510,9 @@ /obj/structure/chair{ dir = 8 }, -/obj/effect/mob_spawn/human/doctor{ +/obj/effect/mob_spawn/human/geneticist{ brute_damage = 145; - id_job = "Geneticist"; - name = "Geneticist"; - oxy_damage = 55; - suit = /obj/item/clothing/suit/toggle/labcoat/genetics; - uniform = /obj/item/clothing/under/rank/rnd/geneticist + oxy_damage = 55 }, /turf/open/floor/mineral/titanium/blue, /area/awaymission/research/exterior) diff --git a/_maps/RandomZLevels/spacebattle.dmm b/_maps/RandomZLevels/spacebattle.dmm index 38c798c4fa1..477a71860d8 100644 --- a/_maps/RandomZLevels/spacebattle.dmm +++ b/_maps/RandomZLevels/spacebattle.dmm @@ -905,8 +905,7 @@ /turf/open/floor/iron, /area/awaymission/spacebattle/cruiser) "dX" = ( -/obj/effect/mob_spawn/human/engineer/rig{ - id_job = "Gunner"; +/obj/effect/mob_spawn/human/engineer/rig/gunner{ mob_name = "Andrew Thorn"; name = "Andrew Thorn" }, @@ -1069,8 +1068,7 @@ /turf/open/floor/mineral/plastitanium/red, /area/awaymission/spacebattle/syndicate1) "eH" = ( -/obj/effect/mob_spawn/human/engineer/rig{ - id_job = "Gunner"; +/obj/effect/mob_spawn/human/engineer/rig/gunner{ mob_name = "Peter West"; name = "Peter West" }, @@ -1189,8 +1187,7 @@ /turf/open/floor/iron, /area/awaymission/spacebattle/cruiser) "fa" = ( -/obj/effect/mob_spawn/human/engineer/rig{ - id_job = "Gunner"; +/obj/effect/mob_spawn/human/engineer/rig/gunner{ mob_name = "Eric Abnett"; name = "Eric Abnett" }, @@ -1776,8 +1773,7 @@ /turf/open/floor/iron, /area/awaymission/spacebattle/cruiser) "gO" = ( -/obj/effect/mob_spawn/human/engineer/rig{ - id_job = "Gunner"; +/obj/effect/mob_spawn/human/engineer/rig/gunner{ name = "Jeremy Tailor" }, /obj/effect/decal/cleanable/blood, @@ -1879,8 +1875,7 @@ /turf/open/floor/wood, /area/awaymission/spacebattle/cruiser) "hh" = ( -/obj/effect/mob_spawn/human/engineer/rig{ - id_job = "Gunner"; +/obj/effect/mob_spawn/human/engineer/rig/gunner{ mob_name = "Dan Hedricks"; name = "Dan Hedricks" }, @@ -2027,8 +2022,7 @@ /turf/open/floor/wood, /area/awaymission/spacebattle/cruiser) "hL" = ( -/obj/effect/mob_spawn/human/engineer{ - id_job = "Gunner"; +/obj/effect/mob_spawn/human/engineer/rig/gunner{ mob_name = "William Gannon"; name = "William Gannon" }, diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index 9fc5bfd10af..6a1ae37a584 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -66664,6 +66664,7 @@ /obj/effect/turf_decal/tile/neutral{ dir = 8 }, +/obj/item/paper/fluff/ids_for_dummies, /turf/open/floor/iron/dark, /area/command/heads_quarters/hop) "gDQ" = ( @@ -80432,10 +80433,6 @@ /turf/open/floor/iron, /area/cargo/warehouse) "lLY" = ( -/obj/item/radio/intercom{ - pixel_x = -26; - pixel_y = 26 - }, /obj/effect/turf_decal/tile/neutral{ dir = 1 }, @@ -80446,6 +80443,10 @@ /obj/effect/turf_decal/tile/neutral{ dir = 8 }, +/obj/item/storage/secure/safe/caps_spare{ + pixel_x = -27; + pixel_y = 32 + }, /turf/open/floor/iron/dark, /area/command/bridge) "lMf" = ( diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index 2548cce5d5c..cc93d26ab35 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -29899,6 +29899,10 @@ pixel_x = 4; pixel_y = 4 }, +/obj/item/storage/box/silver_ids{ + pixel_x = 4; + pixel_y = 6 + }, /obj/item/storage/box/ids, /turf/open/floor/iron, /area/command/bridge) @@ -30924,6 +30928,10 @@ "fMy" = ( /obj/structure/table/reinforced, /obj/item/storage/firstaid/regular, +/obj/item/storage/secure/safe/caps_spare{ + pixel_x = 37; + pixel_y = 32 + }, /turf/open/floor/iron, /area/command/bridge) "fMz" = ( @@ -43494,10 +43502,6 @@ "oMm" = ( /obj/structure/table/reinforced, /obj/item/storage/fancy/donut_box, -/obj/machinery/bounty_board{ - dir = 8; - pixel_x = 32 - }, /turf/open/floor/iron, /area/command/bridge) "oMA" = ( @@ -47158,6 +47162,10 @@ /obj/structure/table/reinforced, /obj/item/assembly/flash/handheld, /obj/item/assembly/flash/handheld, +/obj/machinery/bounty_board{ + dir = 4; + pixel_x = -28 + }, /turf/open/floor/iron, /area/command/bridge) "rtx" = ( @@ -53728,6 +53736,7 @@ /obj/effect/turf_decal/tile/blue{ dir = 8 }, +/obj/item/paper/fluff/ids_for_dummies, /turf/open/floor/iron, /area/command/heads_quarters/hop) "vRV" = ( diff --git a/_maps/map_files/KiloStation/KiloStation.dmm b/_maps/map_files/KiloStation/KiloStation.dmm index 7160b58a085..6d751e12efb 100644 --- a/_maps/map_files/KiloStation/KiloStation.dmm +++ b/_maps/map_files/KiloStation/KiloStation.dmm @@ -71110,6 +71110,10 @@ }, /obj/item/healthanalyzer, /obj/item/hand_labeler, +/obj/item/storage/secure/safe/caps_spare{ + pixel_x = 5; + pixel_y = 32 + }, /turf/open/floor/iron/dark, /area/command/bridge) "mzV" = ( @@ -72950,6 +72954,7 @@ pixel_x = 24; pixel_y = 24 }, +/obj/item/paper/fluff/ids_for_dummies, /turf/open/floor/iron/dark, /area/command/heads_quarters/hop) "nFz" = ( @@ -85436,10 +85441,6 @@ /obj/effect/turf_decal/tile/yellow{ dir = 4 }, -/obj/machinery/status_display/evac{ - pixel_x = 32; - pixel_y = 32 - }, /turf/open/floor/iron/dark, /area/command/bridge) "uAx" = ( diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 3e1f1da1def..7204467efb5 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -37974,7 +37974,7 @@ contents = newlist(/obj/item/clothing/suit/armor/vest,/obj/item/gun/ballistic/automatic/pistol,/obj/item/suppressor,/obj/item/melee/classic_baton/telescopic,/obj/item/clothing/mask/balaclava,/obj/item/bodybag,/obj/item/soap/nanotrasen) }, /obj/item/storage/backpack/duffelbag/syndie/hitman, -/obj/item/card/id/silver/reaper, +/obj/item/card/id/advanced/silver/reaper, /obj/item/lazarus_injector, /obj/item/gun/energy/disabler, /obj/item/gun/ballistic/revolver/russian, @@ -39560,9 +39560,6 @@ /turf/open/floor/iron, /area/engineering/break_room) "gOe" = ( -/obj/item/radio/intercom{ - pixel_y = 29 - }, /obj/machinery/modular_computer/console/preset/engineering, /obj/effect/turf_decal/tile/blue{ dir = 1 @@ -41436,6 +41433,9 @@ /obj/effect/turf_decal/tile/green{ dir = 8 }, +/obj/item/storage/secure/safe/caps_spare{ + pixel_x = -27 + }, /turf/open/floor/iron/dark, /area/command/bridge) "hFJ" = ( @@ -52889,6 +52889,7 @@ /obj/machinery/modular_computer/console/preset/id{ dir = 4 }, +/obj/item/paper/fluff/ids_for_dummies, /turf/open/floor/wood, /area/command/heads_quarters/hop) "mpE" = ( @@ -54502,6 +54503,9 @@ /obj/effect/turf_decal/tile/blue{ dir = 4 }, +/obj/item/radio/intercom{ + pixel_y = 35 + }, /turf/open/floor/iron/dark, /area/command/bridge) "mVL" = ( diff --git a/_maps/map_files/debug/multiz.dmm b/_maps/map_files/debug/multiz.dmm index a28e6fe40d4..8642d3ac535 100644 --- a/_maps/map_files/debug/multiz.dmm +++ b/_maps/map_files/debug/multiz.dmm @@ -434,7 +434,7 @@ /area/command/bridge) "bJ" = ( /obj/structure/table, -/obj/item/card/id/captains_spare, +/obj/item/card/id/advanced/gold/captains_spare, /turf/open/floor/iron, /area/command/bridge) "bK" = ( diff --git a/_maps/map_files/debug/runtimestation.dmm b/_maps/map_files/debug/runtimestation.dmm index 01019e27ead..29984d17944 100644 --- a/_maps/map_files/debug/runtimestation.dmm +++ b/_maps/map_files/debug/runtimestation.dmm @@ -483,12 +483,12 @@ /area/command/bridge) "bJ" = ( /obj/structure/table, -/obj/item/card/id/ert{ +/obj/item/card/id/advanced/centcom/ert{ pixel_x = 6; pixel_y = -6 }, -/obj/item/card/id/syndicate/nuke_leader, -/obj/item/card/id/captains_spare{ +/obj/item/card/id/advanced/chameleon, +/obj/item/card/id/advanced/gold/captains_spare{ pixel_x = -6; pixel_y = 6 }, @@ -2048,7 +2048,7 @@ /area/construction) "gz" = ( /obj/structure/table, -/obj/item/card/id/captains_spare, +/obj/item/card/id/advanced/gold/captains_spare, /obj/machinery/keycard_auth{ pixel_y = 28 }, diff --git a/code/__DEFINES/access.dm b/code/__DEFINES/access.dm index dbb8ed24d60..390ab971f7c 100644 --- a/code/__DEFINES/access.dm +++ b/code/__DEFINES/access.dm @@ -150,3 +150,374 @@ log_game("[key_name(user)] [change_description] to an ID card [(id_card.registered_name) ? "belonging to [id_card.registered_name]." : "with no registered name."]"); \ user.investigate_log("([key_name(user)]) [change_description] to an ID card [(id_card.registered_name) ? "belonging to [id_card.registered_name]." : "with no registered name."]", INVESTIGATE_ACCESSCHANGES); \ user.log_message("[change_description] to an ID card [(id_card.registered_name) ? "belonging to [id_card.registered_name]." : "with no registered name."]", LOG_GAME); \ + +/// Displayed name for Common ID card accesses. +#define ACCESS_FLAG_COMMON_NAME "Common" +/// Bitflag for Common ID card accesses. See COMMON_ACCESS. +#define ACCESS_FLAG_COMMON (1 << 0) +/// Displayed name for Command ID card accesses. +#define ACCESS_FLAG_COMMAND_NAME "Command" +/// Bitflag for Command ID card accesses. See COMMAND_ACCESS. +#define ACCESS_FLAG_COMMAND (1 << 1) +/// Displayed name for Private Command ID card accesses. +#define ACCESS_FLAG_PRV_COMMAND_NAME "Private Command" +/// Bitflag for Private Command ID card accesses. See PRIVATE_COMMAND_ACCESS. +#define ACCESS_FLAG_PRV_COMMAND (1 << 2) +/// Displayed name for Captain ID card accesses. +#define ACCESS_FLAG_CAPTAIN_NAME "Captain" +/// Bitflag for Captain ID card accesses. See CAPTAIN_ACCESS. +#define ACCESS_FLAG_CAPTAIN (1 << 3) +/// Displayed name for Centcom ID card accesses. +#define ACCESS_FLAG_CENTCOM_NAME "Centcom" +/// Bitflag for Centcom ID card accesses. See CENTCOM_ACCESS. +#define ACCESS_FLAG_CENTCOM (1 << 4) +/// Displayed name for Syndicate ID card accesses. +#define ACCESS_FLAG_SYNDICATE_NAME "Syndicate" +/// Bitflag for Syndicate ID card accesses. See SYNDICATE_ACCESS. +#define ACCESS_FLAG_SYNDICATE (1 << 5) +/// Displayed name for Offstation/Ruin/Away Mission ID card accesses. +#define ACCESS_FLAG_AWAY_NAME "Away" +/// Bitflag for Offstation/Ruin/Away Mission ID card accesses. See AWAY_ACCESS. +#define ACCESS_FLAG_AWAY (1 << 6) +/// Displayed name for Special accesses that ordinaryily shouldn't be on ID cards. +#define ACCESS_FLAG_SPECIAL_NAME "Special" +/// Bitflag for Special accesses that ordinaryily shouldn't be on ID cards. See CULT_ACCESS. +#define ACCESS_FLAG_SPECIAL (1 << 7) + +/// This wildcraft flag accepts any access level. +#define WILDCARD_FLAG_ALL ALL +/// Name associated with the all wildcard bitflag. +#define WILDCARD_NAME_ALL "All" +/// Access flags that can be applied to common wildcard slots. +#define WILDCARD_FLAG_COMMON ACCESS_FLAG_COMMON +/// Name associated with the common wildcard bitflag. +#define WILDCARD_NAME_COMMON ACCESS_FLAG_COMMON_NAME +/// Access flags that can be applied to command wildcard slots. +#define WILDCARD_FLAG_COMMAND ACCESS_FLAG_COMMON | ACCESS_FLAG_COMMAND +/// Name associated with the command wildcard bitflag. +#define WILDCARD_NAME_COMMAND ACCESS_FLAG_COMMAND_NAME +/// Access flags that can be applied to private command wildcard slots. +#define WILDCARD_FLAG_PRV_COMMAND ACCESS_FLAG_COMMON | ACCESS_FLAG_COMMAND | ACCESS_FLAG_PRV_COMMAND +/// Name associated with the private command wildcard bitflag. +#define WILDCARD_NAME_PRV_COMMAND ACCESS_FLAG_PRV_COMMAND_NAME +/// Access flags that can be applied to captain wildcard slots. +#define WILDCARD_FLAG_CAPTAIN ACCESS_FLAG_COMMON | ACCESS_FLAG_COMMAND | ACCESS_FLAG_PRV_COMMAND | ACCESS_FLAG_CAPTAIN +/// Name associated with the captain wildcard bitflag. +#define WILDCARD_NAME_CAPTAIN ACCESS_FLAG_CAPTAIN_NAME +/// Access flags that can be applied to centcom wildcard slots. +#define WILDCARD_FLAG_CENTCOM ACCESS_FLAG_COMMON | ACCESS_FLAG_COMMAND | ACCESS_FLAG_PRV_COMMAND | ACCESS_FLAG_CAPTAIN | ACCESS_FLAG_CENTCOM +/// Name associated with the centcom wildcard bitflag. +#define WILDCARD_NAME_CENTCOM ACCESS_FLAG_CENTCOM_NAME +/// Access flags that can be applied to syndicate wildcard slots. +#define WILDCARD_FLAG_SYNDICATE ACCESS_FLAG_COMMON | ACCESS_FLAG_COMMAND | ACCESS_FLAG_PRV_COMMAND | ACCESS_FLAG_CAPTAIN | ACCESS_FLAG_SYNDICATE +/// Name associated with the syndicate wildcard bitflag. +#define WILDCARD_NAME_SYNDICATE ACCESS_FLAG_SYNDICATE_NAME +/// Access flags that can be applied to offstation wildcard slots. +#define WILDCARD_FLAG_AWAY ACCESS_FLAG_AWAY +/// Name associated with the offstation wildcard bitflag. +#define WILDCARD_NAME_AWAY ACCESS_FLAG_AWAY_NAME +/// Access flags that can be applied to super special weird wildcard slots. +#define WILDCARD_FLAG_SPECIAL ACCESS_FLAG_SPECIAL +/// Name associated with the super special weird wildcard bitflag. +#define WILDCARD_NAME_SPECIAL ACCESS_FLAG_SPECIAL_NAME +/// Access flag that indicates a wildcard was forced onto an ID card. +#define WILDCARD_FLAG_FORCED ALL +/// Name associated with the wildcard bitflag that covers wildcards that have been forced onto an ID card that could not accept them. +#define WILDCARD_NAME_FORCED "Hacked" + +/// Departmental/general/common area accesses. Do not use direct, access via SSid_access.get_flag_access_list(ACCESS_FLAG_COMMON) +#define COMMON_ACCESS list( \ + ACCESS_MECH_MINING, \ + ACCESS_MECH_MEDICAL, \ + ACCESS_MECH_SECURITY, \ + ACCESS_MECH_SCIENCE, \ + ACCESS_MECH_ENGINE, \ + ACCESS_AUX_BASE, \ + ACCESS_PSYCHOLOGY, \ + ACCESS_PHARMACY, \ + ACCESS_NETWORK, \ + ACCESS_WEAPONS, \ + ACCESS_MINERAL_STOREROOM, \ + ACCESS_SEC_DOORS, \ + ACCESS_XENOBIOLOGY, \ + ACCESS_MINING_STATION, \ + ACCESS_MAILSORTING, \ + ACCESS_MINING, \ + ACCESS_RESEARCH, \ + ACCESS_THEATRE, \ + ACCESS_SURGERY, \ + ACCESS_COURT, \ + ACCESS_QM, \ + ACCESS_VIROLOGY, \ + ACCESS_LAWYER, \ + ACCESS_LIBRARY, \ + ACCESS_HYDROPONICS, \ + ACCESS_CHEMISTRY, \ + ACCESS_CONSTRUCTION, \ + ACCESS_CARGO, \ + ACCESS_ROBOTICS, \ + ACCESS_KITCHEN, \ + ACCESS_CREMATORIUM, \ + ACCESS_JANITOR, \ + ACCESS_BAR, \ + ACCESS_CHAPEL_OFFICE, \ + ACCESS_EXTERNAL_AIRLOCKS, \ + ACCESS_MAINT_TUNNELS, \ + ACCESS_ENGINE_EQUIP, \ + ACCESS_ENGINE, \ + ACCESS_GENETICS, \ + ACCESS_RND, \ + ACCESS_MORGUE, \ + ACCESS_MEDICAL, \ + ACCESS_FORENSICS_LOCKERS, \ + ACCESS_BRIG, \ + ACCESS_SECURITY, \ +) + +/// Command staff/secure accesses, think bridge/armoury, AI upload, notably access to modify ID cards themselves. Do not use direct, access via SSid_access.get_flag_access_list(ACCESS_FLAG_COMMAND) +#define COMMAND_ACCESS list( \ + ACCESS_TOXINS_STORAGE, \ + ACCESS_MINISAT, \ + ACCESS_TCOMSAT, \ + ACCESS_KEYCARD_AUTH, \ + ACCESS_RC_ANNOUNCE, \ + ACCESS_VAULT, \ + ACCESS_ATMOSPHERICS, \ + ACCESS_TECH_STORAGE, \ + ACCESS_HEADS, \ + ACCESS_TELEPORTER, \ + ACCESS_ARMORY, \ + ACCESS_AI_UPLOAD, \ + ACCESS_CHANGE_IDS, \ + ACCESS_TOXINS, \ + ACCESS_EVA, \ + ACCESS_GATEWAY, \ + ACCESS_ALL_PERSONAL_LOCKERS, \ +) + +/// Private head of staff offices, usually only granted to most cards by trimming. Do not use direct, access via SSid_access.get_flag_access_list(ACCESS_FLAG_PRV_COMMAND) +#define PRIVATE_COMMAND_ACCESS list( \ + ACCESS_HOS, \ + ACCESS_HOP, \ + ACCESS_CE, \ + ACCESS_CMO, \ + ACCESS_RD, \ +) + +/// Captains private rooms. Do not use direct, access via SSid_access.get_flag_access_list(ACCESS_FLAG_CAPTAIN) +#define CAPTAIN_ACCESS list( \ + ACCESS_CAPTAIN, \ +) +/// Centcom area stuff. Do not use direct, access via SSid_access.get_flag_access_list(ACCESS_FLAG_CENTCOM) +#define CENTCOM_ACCESS list( \ + ACCESS_CENT_BAR, \ + ACCESS_CENT_CAPTAIN, \ + ACCESS_CENT_TELEPORTER, \ + ACCESS_CENT_STORAGE, \ + ACCESS_CENT_LIVING, \ + ACCESS_CENT_MEDICAL, \ + ACCESS_CENT_SPECOPS, \ + ACCESS_CENT_THUNDER, \ + ACCESS_CENT_GENERAL, \ +) + +/// Syndicate areas off station. Do not use direct, access via SSid_access.get_flag_access_list(ACCESS_FLAG_SYNDICATE) +#define SYNDICATE_ACCESS list( \ + ACCESS_SYNDICATE_LEADER, \ + ACCESS_SYNDICATE, \ +) + +/// Away missions/gateway/space ruins. Do not use direct, access via SSid_access.get_flag_access_list(ACCESS_FLAG_AWAY) +#define AWAY_ACCESS list( \ + ACCESS_AWAY_GENERAL, \ + ACCESS_AWAY_MAINT, \ + ACCESS_AWAY_MED, \ + ACCESS_AWAY_SEC, \ + ACCESS_AWAY_ENGINE, \ + ACCESS_AWAY_GENERIC1, \ + ACCESS_AWAY_GENERIC2, \ + ACCESS_AWAY_GENERIC3, \ + ACCESS_AWAY_GENERIC4, \ +) + +/// Weird internal Cult access that prevents non-cult from using their doors. Do not use direct, access via SSid_access.get_flag_access_list(ACCESS_FLAG_SPECIAL) +#define CULT_ACCESS list( \ + ACCESS_BLOODCULT, \ +) + +/// Name for the Global region. +#define REGION_ALL_GLOBAL "All" +/// Used to seed the accesses_by_region list in SSid_access. A list of every single access in the game. +#define REGION_ACCESS_ALL_GLOBAL REGION_ACCESS_ALL_STATION + CENTCOM_ACCESS + SYNDICATE_ACCESS + AWAY_ACCESS + CULT_ACCESS +/// Name for the Station All Access region. +#define REGION_ALL_STATION "Station" +/// Used to seed the accesses_by_region list in SSid_access. A list of all station accesses. +#define REGION_ACCESS_ALL_STATION COMMON_ACCESS + COMMAND_ACCESS + PRIVATE_COMMAND_ACCESS + CAPTAIN_ACCESS +/// Name for the General region. +#define REGION_GENERAL "General" +/// Used to seed the accesses_by_region list in SSid_access. A list of general service accesses that are overseen by the HoP. +#define REGION_ACCESS_GENERAL list( \ + ACCESS_KITCHEN, \ + ACCESS_BAR, \ + ACCESS_HYDROPONICS, \ + ACCESS_JANITOR, \ + ACCESS_CHAPEL_OFFICE, \ + ACCESS_CREMATORIUM, \ + ACCESS_LIBRARY, \ + ACCESS_THEATRE, \ + ACCESS_LAWYER, \ +) +/// Name for the Security region. +#define REGION_SECURITY "Security" +/// Used to seed the accesses_by_region list in SSid_access. A list of all security regional accesses that are overseen by the HoS. +#define REGION_ACCESS_SECURITY list( \ + ACCESS_SEC_DOORS, \ + ACCESS_WEAPONS, \ + ACCESS_SECURITY, \ + ACCESS_BRIG, \ + ACCESS_ARMORY, \ + ACCESS_FORENSICS_LOCKERS, \ + ACCESS_COURT, \ + ACCESS_MECH_SECURITY, \ + ACCESS_HOS, \ +) +/// Name for the Medbay region. +#define REGION_MEDBAY "Medbay" +/// Used to seed the accesses_by_region list in SSid_access. A list of all medbay regional accesses that are overseen by the CMO. +#define REGION_ACCESS_MEDBAY list( \ + ACCESS_MEDICAL, \ + ACCESS_MORGUE, \ + ACCESS_CHEMISTRY, \ + ACCESS_VIROLOGY, \ + ACCESS_SURGERY, \ + ACCESS_MECH_MEDICAL, \ + ACCESS_CMO, \ + ACCESS_PHARMACY, \ + ACCESS_PSYCHOLOGY, \ +) +/// Name for the Research region. +#define REGION_RESEARCH "Research" +/// Used to seed the accesses_by_region list in SSid_access. A list of all research regional accesses that are overseen by the RD. +#define REGION_ACCESS_RESEARCH list( \ + ACCESS_RESEARCH, \ + ACCESS_RND, \ + ACCESS_TOXINS, \ + ACCESS_TOXINS_STORAGE, \ + ACCESS_GENETICS, \ + ACCESS_ROBOTICS, \ + ACCESS_XENOBIOLOGY, \ + ACCESS_MECH_SCIENCE, \ + ACCESS_MINISAT, \ + ACCESS_RD, \ + ACCESS_NETWORK, \ +) +/// Name for the Engineering region. +#define REGION_ENGINEERING "Engineering" +/// Used to seed the accesses_by_region list in SSid_access. A list of all engineering regional accesses that are overseen by the CE. +#define REGION_ACCESS_ENGINEERING list( \ + ACCESS_CONSTRUCTION, \ + ACCESS_AUX_BASE, \ + ACCESS_MAINT_TUNNELS, \ + ACCESS_ENGINE, \ + ACCESS_ENGINE_EQUIP, \ + ACCESS_EXTERNAL_AIRLOCKS, \ + ACCESS_TECH_STORAGE, \ + ACCESS_ATMOSPHERICS, \ + ACCESS_MECH_ENGINE, \ + ACCESS_TCOMSAT, \ + ACCESS_MINISAT, \ + ACCESS_CE, \ +) +/// Name for the Supply region. +#define REGION_SUPPLY "Supply" +/// Used to seed the accesses_by_region list in SSid_access. A list of all cargo regional accesses that are overseen by the HoP. +#define REGION_ACCESS_SUPPLY list( \ + ACCESS_MAILSORTING, \ + ACCESS_MINING, \ + ACCESS_MINING_STATION, \ + ACCESS_MECH_MINING, \ + ACCESS_MINERAL_STOREROOM, \ + ACCESS_CARGO, \ + ACCESS_QM, \ + ACCESS_VAULT, \ +) +/// Name for the Command region. +#define REGION_COMMAND "Command" +/// Used to seed the accesses_by_region list in SSid_access. A list of all command regional accesses that are overseen by the Captain. +#define REGION_ACCESS_COMMAND list( \ + ACCESS_HEADS, \ + ACCESS_RC_ANNOUNCE, \ + ACCESS_KEYCARD_AUTH, \ + ACCESS_CHANGE_IDS, \ + ACCESS_AI_UPLOAD, \ + ACCESS_TELEPORTER, \ + ACCESS_EVA, \ + ACCESS_GATEWAY, \ + ACCESS_ALL_PERSONAL_LOCKERS, \ + ACCESS_HOP, \ + ACCESS_CAPTAIN, \ + ACCESS_VAULT, \ +) +/// Name for the Centcom region. +#define REGION_CENTCOM "Central Command" +/// Used to seed the accesses_by_region list in SSid_access. A list of all CENTCOM_ACCESS regional accesses. +#define REGION_ACCESS_CENTCOM CENTCOM_ACCESS + +/** + * A list of PDA paths that can be painted as well as the regional heads which should be able to paint them. + * If a PDA is not in this list, it cannot be painted using the PDA & ID Painter. + * If a PDA is in this list, it can always be painted with ACCESS_CHANGE_IDS. + * Used to see pda_region in [/datum/controller/subsystem/id_access/proc/setup_tgui_lists] + */ +#define PDA_PAINTING_REGIONS list( \ + /obj/item/pda = list(REGION_GENERAL), \ + /obj/item/pda/clown = list(REGION_GENERAL), \ + /obj/item/pda/mime = list(REGION_GENERAL), \ + /obj/item/pda/medical = list(REGION_MEDBAY), \ + /obj/item/pda/viro = list(REGION_MEDBAY), \ + /obj/item/pda/engineering = list(REGION_ENGINEERING), \ + /obj/item/pda/security = list(REGION_SECURITY), \ + /obj/item/pda/detective = list(REGION_SECURITY), \ + /obj/item/pda/warden = list(REGION_SECURITY), \ + /obj/item/pda/janitor = list(REGION_GENERAL), \ + /obj/item/pda/toxins = list(REGION_RESEARCH), \ + /obj/item/pda/heads/hop = list(REGION_COMMAND), \ + /obj/item/pda/heads/hos = list(REGION_COMMAND), \ + /obj/item/pda/heads/cmo = list(REGION_COMMAND), \ + /obj/item/pda/heads/ce = list(REGION_COMMAND), \ + /obj/item/pda/heads/rd = list(REGION_COMMAND), \ + /obj/item/pda/captain = list(REGION_COMMAND), \ + /obj/item/pda/cargo = list(REGION_SUPPLY), \ + /obj/item/pda/quartermaster = list(REGION_SUPPLY), \ + /obj/item/pda/shaftminer = list(REGION_SUPPLY), \ + /obj/item/pda/chaplain = list(REGION_GENERAL), \ + /obj/item/pda/lawyer = list(REGION_GENERAL, REGION_SECURITY), \ + /obj/item/pda/botanist = list(REGION_GENERAL), \ + /obj/item/pda/roboticist = list(REGION_RESEARCH), \ + /obj/item/pda/curator = list(REGION_GENERAL), \ + /obj/item/pda/cook = list(REGION_GENERAL), \ + /obj/item/pda/bar = list(REGION_GENERAL), \ + /obj/item/pda/atmos = list(REGION_ENGINEERING), \ + /obj/item/pda/chemist = list(REGION_MEDBAY), \ + /obj/item/pda/geneticist = list(REGION_RESEARCH), \ +) + +/// All regions that make up the station area. Helper define to quickly designate a region as part of the station or not. Access via SSid_access.station_regions. +#define REGION_AREA_STATION list( \ + REGION_GENERAL, \ + REGION_SECURITY, \ + REGION_MEDBAY, \ + REGION_RESEARCH, \ + REGION_ENGINEERING, \ + REGION_SUPPLY, \ + REGION_COMMAND, \ +) + +/// Used in ID card access adding procs. Will try to add all accesses and utilises free wildcards, skipping over any accesses it can't add. +#define TRY_ADD_ALL 0 +/// Used in ID card access adding procs. Will try to add all accesses and does not utilise wildcards, skipping anything requiring a wildcard. +#define TRY_ADD_ALL_NO_WILDCARD 1 +/// Used in ID card access adding procs. Will forcefully add all accesses. +#define FORCE_ADD_ALL 2 +/// Used in ID card access adding procs. Will stack trace on fail. +#define ERROR_ON_FAIL 3 diff --git a/code/__DEFINES/id_cards.dm b/code/__DEFINES/id_cards.dm new file mode 100644 index 00000000000..716a488ed33 --- /dev/null +++ b/code/__DEFINES/id_cards.dm @@ -0,0 +1,38 @@ +/** + * The order in which these lists are defined matters. When attempting to add a wildcard access to a card + * and not specifying an explicit wildcard slot in which to include it, the code will iterate over the list + * and stop at the first wildcard flag that can hold the access. + * + * The lower level the access, the earlier in the list it should be so that low level wildcards get + * added to the lowest wildcard flag that can take them. + + * A limit of -1 means infinite slots. The system is designed to reject a wildcard when the slot limit + * explicitly equal 0 for all compatible wildcard slots. + */ + +/// Wildcard slot define for basic grey cards. Only hold 2 common wildcards. +#define WILDCARD_LIMIT_GREY list(WILDCARD_NAME_COMMON = list(limit = 2, usage = list())) +/// Wildcard slot define for Head of Staff silver cards. Can hold 3 common, 1 command and 1 private command. +#define WILDCARD_LIMIT_SILVER list( \ + WILDCARD_NAME_COMMON = list(limit = 3, usage = list()), \ + WILDCARD_NAME_COMMAND = list(limit = 1, usage = list()), \ + WILDCARD_NAME_PRV_COMMAND = list(limit = 1, usage = list()) \ +) +/// Wildcard slot define for Captain gold cards. Can hold infinite of any Captain level wildcard. +#define WILDCARD_LIMIT_GOLD list(WILDCARD_NAME_CAPTAIN = list(limit = -1, usage = list())) +/// Wildcard slot define for select Syndicate-affiliated cards. Can hold infinite of any Syndicate level wildcard. Syndicate includes all station accesses. +#define WILDCARD_LIMIT_SYNDICATE list(WILDCARD_NAME_SYNDICATE = list(limit = -1, usage = list())) +/// Wildcard slot define for Deathsquad black cards. Can hold infinite of any Centcom level wildcard. Centcom includes all station accesses. +#define WILDCARD_LIMIT_DEATHSQUAD list(WILDCARD_NAME_CENTCOM = list(limit = -1, usage = list())) +/// Wildcard slot define for Centcom blue cards. Can hold infinite of any Centcom level wildcard. Centcom includes all station accesses. +#define WILDCARD_LIMIT_CENTCOM list(WILDCARD_NAME_CENTCOM = list(limit = -1, usage = list())) +/// Wildcard slot define for Prisoner orange cards. No wildcard slots. +#define WILDCARD_LIMIT_PRISONER list() +/// Wildcard slot define for Chameleon/Agent ID grey cards. Can hold 3 common, 1 command and 1 captain access. +#define WILDCARD_LIMIT_CHAMELEON list( \ + WILDCARD_NAME_COMMON = list(limit = 3, usage = list()), \ + WILDCARD_NAME_COMMAND = list(limit = 1, usage = list()), \ + WILDCARD_NAME_CAPTAIN = list(limit = 1, usage = list()) \ +) +/// Wildcard slot define for admin/debug/weird, special abstract cards. Can hold infinite of any access. +#define WILDCARD_LIMIT_ADMIN list(WILDCARD_NAME_ALL = list(limit = -1, usage = list())) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 5b4a596af46..593dfa7bb25 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -13,15 +13,16 @@ #define REVERSE_DIR(dir) ( ((dir & 85) << 1) | ((dir & 170) >> 1) ) //Human Overlays Indexes///////// -#define MUTATIONS_LAYER 28 //mutations. Tk headglows, cold resistance glow, etc -#define BODY_BEHIND_LAYER 27 //certain mutantrace features (tail when looking south) that must appear behind the body parts -#define BODYPARTS_LAYER 26 //Initially "AUGMENTS", this was repurposed to be a catch-all bodyparts flag -#define BODY_ADJ_LAYER 25 //certain mutantrace features (snout, body markings) that must appear above the body parts -#define BODY_LAYER 24 //underwear, undershirts, socks, eyes, lips(makeup) -#define FRONT_MUTATIONS_LAYER 23 //mutations that should appear above body, body_adj and bodyparts layer (e.g. laser eyes) -#define DAMAGE_LAYER 22 //damage indicators (cuts and burns) -#define UNIFORM_LAYER 21 -#define ID_LAYER 20 //lmao at the idiot who put both ids and hands on the same layer +#define MUTATIONS_LAYER 29 //mutations. Tk headglows, cold resistance glow, etc +#define BODY_BEHIND_LAYER 28 //certain mutantrace features (tail when looking south) that must appear behind the body parts +#define BODYPARTS_LAYER 27 //Initially "AUGMENTS", this was repurposed to be a catch-all bodyparts flag +#define BODY_ADJ_LAYER 26 //certain mutantrace features (snout, body markings) that must appear above the body parts +#define BODY_LAYER 25 //underwear, undershirts, socks, eyes, lips(makeup) +#define FRONT_MUTATIONS_LAYER 24 //mutations that should appear above body, body_adj and bodyparts layer (e.g. laser eyes) +#define DAMAGE_LAYER 23 //damage indicators (cuts and burns) +#define UNIFORM_LAYER 22 +#define ID_LAYER 21 +#define ID_CARD_LAYER 20 #define HANDS_PART_LAYER 19 #define GLOVES_LAYER 18 #define SHOES_LAYER 17 @@ -41,7 +42,7 @@ #define BODY_FRONT_LAYER 3 #define HALO_LAYER 2 //blood cult ascended halo, because there's currently no better solution for adding/removing #define FIRE_LAYER 1 //If you're on fire -#define TOTAL_LAYERS 28 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_; +#define TOTAL_LAYERS 29 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_; //Human Overlay Index Shortcuts for alternate_worn_layer, layers //Because I *KNOW* somebody will think layer+1 means "above" diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index cb002c68c1b..305065fea14 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -115,6 +115,7 @@ #define INIT_ORDER_RESEARCH 75 #define INIT_ORDER_STATION 74 //This is high priority because it manipulates a lot of the subsystems that will initialize after it. #define INIT_ORDER_EVENTS 70 +#define INIT_ORDER_IDACCESS 66 #define INIT_ORDER_JOBS 65 #define INIT_ORDER_QUIRKS 60 #define INIT_ORDER_AI_MOVEMENT 56 //We need the movement setup diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index e9b2316c5c6..67e25e58c57 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -514,7 +514,9 @@ return var/area/A = get_area(character) deadchat_broadcast(" has arrived at the station at [A.name].", "[character.real_name] ([rank])", follow_target = character, message_type=DEADCHAT_ARRIVALRATTLE) - if((!GLOB.announcement_systems.len) || (!character.mind)) + if(!character.mind) + return + if(!GLOB.announcement_systems.len) return if((character.mind.assigned_role == "Cyborg") || (character.mind.assigned_role == character.mind.special_role)) return diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 977b553f704..1b4f63ff305 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -1123,7 +1123,7 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0 dummySave = null fdel("tmp/dummySave.sav") //if you get the idea to try and make this more optimized, make sure to still call unlock on the savefile after every write to unlock it. -/proc/icon2html(thing, target, icon_state, dir = SOUTH, frame = 1, moving = FALSE, sourceonly = FALSE) +/proc/icon2html(thing, target, icon_state, dir = SOUTH, frame = 1, moving = FALSE, sourceonly = FALSE, extra_classes = null) if (!thing) return @@ -1151,7 +1151,7 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0 SSassets.transport.send_assets(thing2, name) if(sourceonly) return SSassets.transport.get_asset_url(name) - return "" + return "" var/atom/A = thing I = A.icon @@ -1185,7 +1185,7 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0 SSassets.transport.send_assets(thing2, key) if(sourceonly) return SSassets.transport.get_asset_url(key) - return "" + return "" /proc/icon2base64html(thing) if (!thing) diff --git a/code/__HELPERS/names.dm b/code/__HELPERS/names.dm index 75fc0c1a224..68742c98ad7 100644 --- a/code/__HELPERS/names.dm +++ b/code/__HELPERS/names.dm @@ -198,7 +198,7 @@ GLOBAL_DATUM(syndicate_code_response_regex, /regex) new_name += pick(GLOB.last_names) . += new_name if(2) - . += pick(get_all_jobs())//Returns a job. + . += pick(SSjob.station_jobs)//Returns a job. safety -= 1 if(2) switch(rand(1,3))//Food, drinks, or places. Only selectable once. diff --git a/code/controllers/subsystem/id_access.dm b/code/controllers/subsystem/id_access.dm new file mode 100644 index 00000000000..fa083600ae6 --- /dev/null +++ b/code/controllers/subsystem/id_access.dm @@ -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 diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index 27e1265bcce..da803350087 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -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, "You are the [rank].") 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\]"]

The spare ID can be found in its dedicated safe on the bridge.

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, "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].") + else + to_chat(new_captain, "You can find the code to obtain your spare ID from the secure safe on the Bridge [where].") + + // 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 diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index eaa05fe0ffd..0acf83cd69d 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -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, "Captainship not forced on anyone.") + 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, "Captainship not forced on anyone.") CHECK_TICK /datum/controller/subsystem/ticker/proc/transfer_characters() diff --git a/code/controllers/subsystem/traumas.dm b/code/controllers/subsystem/traumas.dm index 6b258b58405..cc56eaf9ec8 100644 --- a/code/controllers/subsystem/traumas.dm +++ b/code/controllers/subsystem/traumas.dm @@ -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, diff --git a/code/datums/id_trim/_id_trim.dm b/code/datums/id_trim/_id_trim.dm new file mode 100644 index 00000000000..9fec9deacea --- /dev/null +++ b/code/datums/id_trim/_id_trim.dm @@ -0,0 +1,13 @@ +/// Simple datum that holds the basic information associated with an ID card trim. +/datum/id_trim + /// Icon file for this trim. + var/trim_icon = 'icons/obj/card.dmi' + /// Icon state for this trim. Overlayed on advanced ID cards. + var/trim_state + /// Job/assignment associated with this trim. Can be transferred to ID cards holding this trim. Decides the card's HUD icon. + var/assignment + + /// Accesses that this trim unlocks on a card it is imprinted on. These accesses never take wildcard slots and can be added and removed at will. + var/list/access = list() + /// Accesses that this trim unlocks on a card that require wildcard slots to apply. If a card cannot accept all a trim's wildcard accesses, the card is incompatible with the trim. + var/list/wildcard_access = list() diff --git a/code/datums/id_trim/centcom.dm b/code/datums/id_trim/centcom.dm new file mode 100644 index 00000000000..cc504b32bba --- /dev/null +++ b/code/datums/id_trim/centcom.dm @@ -0,0 +1,170 @@ +/// Trim for basic Centcom cards. +/datum/id_trim/centcom + access = list(ACCESS_CENT_GENERAL) + assignment = "Central Command" + trim_state = "trim_centcom" + +/// Trim for Centcom VIPs +/datum/id_trim/centcom/vip + access = list(ACCESS_CENT_GENERAL) + assignment = "VIP Guest" + +/// Trim for Centcom Custodians. +/datum/id_trim/centcom/custodian + access = list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_STORAGE) + assignment = "Custodian" + +/// Trim for Centcom Thunderdome Overseers. +/datum/id_trim/centcom/thunderdome_overseer + access = list(ACCESS_CENT_GENERAL, ACCESS_CENT_THUNDER) + assignment = "Thunderdome Overseer" + +/// Trim for Centcom Officials. +/datum/id_trim/centcom/official + access = list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_WEAPONS) + assignment = "CentCom Official" + +/// Trim for Centcom Interns. +/datum/id_trim/centcom/intern + access = list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_WEAPONS) + assignment = "CentCom Intern" + +/// Trim for Centcom Head Interns. Different assignment, common station access added on. +/datum/id_trim/centcom/intern/head + assignment = "CentCom Head Intern" + +/datum/id_trim/centcom/intern/head/New() + . = ..() + + access |= SSid_access.get_flag_access_list(ACCESS_FLAG_COMMON) + +/// Trim for Centcom Bartenders. +/datum/id_trim/centcom/bartender + access = list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_BAR) + assignment = "CentCom Bartender" + +/// Trim for Centcom Medical Officers. +/datum/id_trim/centcom/medical_officer + access = list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL) + assignment = "Medical Officer" + +/// Trim for Centcom Research Officers. +/datum/id_trim/centcom/research_officer + access = list(ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_MEDICAL, ACCESS_CENT_TELEPORTER, ACCESS_CENT_STORAGE) + assignment = "Research Officer" + +/// Trim for Centcom Specops Officers. All Centcom and Station Access. +/datum/id_trim/centcom/specops_officer + assignment = "Special Ops Officer" + +/datum/id_trim/centcom/specops_officer/New() + . = ..() + + access = SSid_access.get_region_access_list(list(REGION_CENTCOM, REGION_ALL_STATION)) + +/// Trim for Centcom (Soviet) Admirals. All Centcom and Station Access. +/datum/id_trim/centcom/admiral + assignment = "Admiral" + +/datum/id_trim/centcom/admiral/New() + . = ..() + + access = SSid_access.get_region_access_list(list(REGION_CENTCOM, REGION_ALL_STATION)) + +/// Trim for Centcom Commanders. All Centcom and Station Access. +/datum/id_trim/centcom/commander + assignment = "CentCom Commander" + +/datum/id_trim/centcom/commander/New() + . = ..() + + access = SSid_access.get_region_access_list(list(REGION_CENTCOM, REGION_ALL_STATION)) + +/// Trim for Deathsquad officers. All Centcom and Station Access. +/datum/id_trim/centcom/deathsquad + assignment = "Death Commando" + trim_state = "trim_ert_commander" + +/datum/id_trim/centcom/deathsquad/New() + . = ..() + + access = SSid_access.get_region_access_list(list(REGION_CENTCOM, REGION_ALL_STATION)) + +/// Trim for generic ERT interns. No universal ID card changing access. +/datum/id_trim/centcom/ert + assignment = "Emergency Response Team Intern" + +/datum/id_trim/centcom/ert/New() + . = ..() + + access = list(ACCESS_CENT_GENERAL) | (SSid_access.get_region_access_list(list(REGION_ALL_STATION)) - ACCESS_CHANGE_IDS) + +/// Trim for ERT Commanders. All station and centcom access. +/datum/id_trim/centcom/ert/commander + assignment = "Emergency Response Team Commander" + trim_state = "trim_ert_commander" + +/datum/id_trim/centcom/ert/commander/New() + . = ..() + + access = SSid_access.get_region_access_list(list(REGION_CENTCOM, REGION_ALL_STATION)) + +/// Trim for generic ERT seccies. No universal ID card changing access. +/datum/id_trim/centcom/ert/security + assignment = "Security Response Officer" + trim_state = "trim_ert_security" + +/datum/id_trim/centcom/ert/security/New() + . = ..() + + access = list(ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_LIVING) | (SSid_access.get_region_access_list(list(REGION_ALL_STATION)) - ACCESS_CHANGE_IDS) + +/// Trim for generic ERT engineers. No universal ID card changing access. +/datum/id_trim/centcom/ert/engineer + assignment = "Engineering Response Officer" + trim_state = "trim_ert_engineering" + +/datum/id_trim/centcom/ert/engineer/New() + . = ..() + + access = list(ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_LIVING, ACCESS_CENT_STORAGE) | (SSid_access.get_region_access_list(list(REGION_ALL_STATION)) - ACCESS_CHANGE_IDS) + +/// Trim for generic ERT medics. No universal ID card changing access. +/datum/id_trim/centcom/ert/medical + assignment = "Medical Response Officer" + trim_state = "trim_ert_medical" + +/datum/id_trim/centcom/ert/medical/New() + . = ..() + + access = list(ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_MEDICAL, ACCESS_CENT_LIVING) | (SSid_access.get_region_access_list(list(REGION_ALL_STATION)) - ACCESS_CHANGE_IDS) + +/// Trim for generic ERT chaplains. No universal ID card changing access. +/datum/id_trim/centcom/ert/chaplain + assignment = "Religious Response Officer" + trim_state = "trim_ert_religious" + +/datum/id_trim/centcom/ert/chaplain/New() + . = ..() + + access = list(ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_LIVING) | (SSid_access.get_region_access_list(list(REGION_ALL_STATION)) - ACCESS_CHANGE_IDS) + +/// Trim for generic ERT janitors. No universal ID card changing access. +/datum/id_trim/centcom/ert/janitor + assignment = "Janitorial Response Officer" + trim_state = "trim_ert_janitor" + +/datum/id_trim/centcom/ert/janitor/New() + . = ..() + + access = list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING) | (SSid_access.get_region_access_list(list(REGION_ALL_STATION)) - ACCESS_CHANGE_IDS) + +/// Trim for generic ERT clowns. No universal ID card changing access. +/datum/id_trim/centcom/ert/clown + assignment = "Entertainment Response Officer" + trim_state = "trim_ert_entertainment" + +/datum/id_trim/centcom/ert/clown/New() + . = ..() + + access = list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING) | (SSid_access.get_region_access_list(list(REGION_ALL_STATION)) - ACCESS_CHANGE_IDS) diff --git a/code/datums/id_trim/families.dm b/code/datums/id_trim/families.dm new file mode 100644 index 00000000000..2a5ab685cbf --- /dev/null +++ b/code/datums/id_trim/families.dm @@ -0,0 +1,8 @@ +/// Trim for the families space police. Has all access. +/datum/id_trim/space_police + trim_state = "trim_ert_security" + assignment = "Space Police" + +/datum/id_trim/space_police/New() + . = ..() + access = SSid_access.get_region_access_list(list(REGION_ALL_STATION)) diff --git a/code/datums/id_trim/jobs.dm b/code/datums/id_trim/jobs.dm new file mode 100644 index 00000000000..2f4fb5ec2fc --- /dev/null +++ b/code/datums/id_trim/jobs.dm @@ -0,0 +1,472 @@ +/** + * This file contains all the trims associated with station jobs. + * It also contains special prisoner trims and the miner's spare ID trim. + */ + +/// ID Trims for station jobs. +/datum/id_trim/job + trim_state = "trim_assistant" + /// Minimal access is the non-wildcard access from the base id_trim but is applied when minimal access flags are set. + var/list/minimal_access = list() + /// Minimal access is the wildcard access from the base id_trim but is applied when minimal access flags are set. + var/list/minimal_wildcard_access = list() + /// Static list. Cache of any mapping config job changes. + var/static/list/job_changes + /// What config entry relates to this job. Should be a lowercase job name with underscores for spaces, eg "prisoner" "research_director" "head_of_security" + var/config_job + /// An ID card with an access in this list can apply this trim to IDs or use it as a job template when adding access to a card. If the list is null, cannot be used as a template. Should be Head of Staff or ID Console accesses or it may do nothing. + var/list/template_access + +/datum/id_trim/job/New() + if(isnull(job_changes)) + SSmapping.HACK_LoadMapConfig() + job_changes = SSmapping.config.job_changes + + if(!length(job_changes) || !config_job) + return + + var/list/access_changes = job_changes[config_job] + + if(!length(access_changes)) + return + + if(islist(access_changes["additional_access"])) + access |= access_changes["additional_access"] + if(islist(access_changes["additional_minimal_access"])) + minimal_access |= access_changes["additional_minimal_access"] + if(islist(access_changes["additional_wildcard_access"])) + wildcard_access |= access_changes["additional_wildcard_access"] + if(islist(access_changes["additional_minimal_wildcard_access"])) + minimal_wildcard_access |= access_changes["additional_minimal_wildcard_access"] + + // If there's no config or minimal access is set, let's overwrite the accesses accordingly. + if(!config) + access = minimal_access.Copy() + wildcard_access = minimal_wildcard_access.Copy() + else + if(CONFIG_GET(flag/jobs_have_minimal_access)) + access = minimal_access.Copy() + wildcard_access = minimal_wildcard_access.Copy() + + // If the config has global maint access set, we always want to add maint access. + if(CONFIG_GET(flag/everyone_has_maint_access)) + access |= list(ACCESS_MAINT_TUNNELS) + + return ..() + +/datum/id_trim/job/assistant + assignment = "Assistant" + trim_state = "trim_assistant" + access = list(ACCESS_MAINT_TUNNELS) + minimal_access = list() + config_job = "assistant" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOP, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/assistant/New() + . = ..() + + // Config has assistant maint access set. + if(CONFIG_GET(flag/assistants_have_maint_access)) + access |= list(ACCESS_MAINT_TUNNELS) + +/datum/id_trim/job/atmospheric_technician + assignment = "Atmospheric Technician" + trim_state = "trim_atmospherictechnician" + access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_MECH_ENGINE, ACCESS_AUX_BASE, + ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_ATMOSPHERICS, ACCESS_MAINT_TUNNELS, ACCESS_AUX_BASE, ACCESS_CONSTRUCTION, ACCESS_MECH_ENGINE, + ACCESS_MINERAL_STOREROOM) + config_job = "atmospheric_technician" + template_access = list(ACCESS_CAPTAIN, ACCESS_CE, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/bartender + assignment = "Bartender" + trim_state = "trim_bartender" + access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM, ACCESS_THEATRE) + minimal_access = list(ACCESS_BAR, ACCESS_MINERAL_STOREROOM, ACCESS_THEATRE) + config_job = "bartender" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOP, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/botanist + assignment = "Botanist" + trim_state = "trim_botanist" + access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_HYDROPONICS, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM) + config_job = "botanist" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOP, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/captain + assignment = "Captain" + trim_state = "trim_captain" + config_job = "captain" + template_access = list(ACCESS_CAPTAIN, ACCESS_CHANGE_IDS) + +/// Captain gets all station accesses hardcoded in because it's the captain. +/datum/id_trim/job/captain/New() + . = ..() + + access |= (SSid_access.get_flag_access_list(ACCESS_FLAG_COMMON) + SSid_access.get_flag_access_list(ACCESS_FLAG_COMMAND)) + wildcard_access |= (SSid_access.get_flag_access_list(ACCESS_FLAG_PRV_COMMAND) + SSid_access.get_flag_access_list(ACCESS_FLAG_CAPTAIN)) + minimal_access |= (SSid_access.get_flag_access_list(ACCESS_FLAG_COMMON) + SSid_access.get_flag_access_list(ACCESS_FLAG_COMMAND)) + minimal_wildcard_access |= (SSid_access.get_flag_access_list(ACCESS_FLAG_PRV_COMMAND) + SSid_access.get_flag_access_list(ACCESS_FLAG_CAPTAIN)) + +/datum/id_trim/job/cargo_technician + assignment = "Cargo Technician" + trim_state = "trim_cargotechnician" + access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_QM, ACCESS_MINING, ACCESS_MECH_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_CARGO, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM, ACCESS_MECH_MINING) + config_job = "cargo_technician" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOP, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/chaplain + assignment = "Chaplain" + trim_state = "trim_chaplain" + access = list(ACCESS_MORGUE, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_THEATRE) + minimal_access = list(ACCESS_MORGUE, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_THEATRE) + config_job = "chaplain" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOP, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/chemist + assignment = "Chemist" + trim_state = "trim_chemist" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_PHARMACY, ACCESS_VIROLOGY, ACCESS_MECH_MEDICAL, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_CHEMISTRY, ACCESS_MECH_MEDICAL, ACCESS_MINERAL_STOREROOM, ACCESS_PHARMACY) + config_job = "chemist" + template_access = list(ACCESS_CAPTAIN, ACCESS_CMO, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/chief_engineer + assignment = "Chief Engineer" + trim_state = "trim_chiefengineer" + access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, + ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ATMOSPHERICS, ACCESS_EVA, ACCESS_AUX_BASE, + ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS, ACCESS_MINISAT, ACCESS_MECH_ENGINE, + ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINERAL_STOREROOM, ACCESS_TELEPORTER) + wildcard_access = list(ACCESS_CE) + minimal_access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, + ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ATMOSPHERICS, ACCESS_EVA, ACCESS_AUX_BASE, + ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS, ACCESS_MINISAT, ACCESS_MECH_ENGINE, + ACCESS_CE, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINERAL_STOREROOM) + minimal_wildcard_access = list(ACCESS_CE) + config_job = "chief_engineer" + template_access = list(ACCESS_CAPTAIN, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/chief_medical_officer + assignment = "Chief Medical Officer" + trim_state = "trim_chiefmedicalofficer" + access = list(ACCESS_MEDICAL, ACCESS_PSYCHOLOGY, ACCESS_MORGUE, ACCESS_PHARMACY, ACCESS_HEADS, ACCESS_MINERAL_STOREROOM, + ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE, ACCESS_MECH_MEDICAL, + ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_MAINT_TUNNELS, ACCESS_EVA, ACCESS_TELEPORTER) + wildcard_access = list(ACCESS_CMO) + minimal_access = list(ACCESS_MEDICAL, ACCESS_PSYCHOLOGY, ACCESS_MORGUE, ACCESS_PHARMACY, ACCESS_HEADS, ACCESS_MINERAL_STOREROOM, + ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE, ACCESS_MECH_MEDICAL, + ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_MAINT_TUNNELS, ACCESS_EVA) + minimal_wildcard_access = list(ACCESS_CMO) + config_job = "chief_medical_officer" + template_access = list(ACCESS_CAPTAIN, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/clown + assignment = "Clown" + trim_state = "trim_clown" + access = list(ACCESS_THEATRE) + minimal_access = list(ACCESS_THEATRE) + config_job = "clown" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOP, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/cook + assignment = "Cook" + trim_state = "trim_cook" + access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM) + config_job = "cook" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOP, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/curator + assignment = "Curator" + trim_state = "trim_curator" + access = list(ACCESS_LIBRARY, ACCESS_AUX_BASE, ACCESS_MINING_STATION) + minimal_access = list(ACCESS_LIBRARY, ACCESS_AUX_BASE, ACCESS_MINING_STATION) + config_job = "curator" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOP, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/detective + assignment = "Detective" + trim_state = "trim_detective" + access = list(ACCESS_SEC_DOORS, ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_MECH_SECURITY, ACCESS_COURT, ACCESS_BRIG, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_SEC_DOORS, ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_MECH_SECURITY, ACCESS_COURT, ACCESS_BRIG, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) + config_job = "detective" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOS, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/detective/New() + . = ..() + + // Config check for if sec has maint access. + if(CONFIG_GET(flag/security_has_maint_access)) + access |= list(ACCESS_MAINT_TUNNELS) + +/datum/id_trim/job/geneticist + assignment = "Geneticist" + trim_state = "trim_geneticist" + access = list(ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_MECH_SCIENCE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_ROBOTICS, ACCESS_MINERAL_STOREROOM, ACCESS_TECH_STORAGE, ACCESS_RND) + minimal_access = list(ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_MECH_SCIENCE, ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM, ACCESS_RND) + config_job = "geneticist" + template_access = list(ACCESS_CAPTAIN, ACCESS_RD, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/head_of_personnel + assignment = "Head of Personnel" + trim_state = "trim_headofpersonnel" + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_COURT, ACCESS_WEAPONS, + ACCESS_MEDICAL, ACCESS_PSYCHOLOGY, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_EVA, ACCESS_HEADS, + ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, + ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_VAULT, ACCESS_MINING_STATION, + ACCESS_MECH_MINING, ACCESS_MECH_ENGINE, ACCESS_MECH_SCIENCE, ACCESS_MECH_SECURITY, ACCESS_MECH_MEDICAL, + ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM, ACCESS_AUX_BASE, ACCESS_TELEPORTER) + wildcard_access = list(ACCESS_HOP) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_COURT, ACCESS_WEAPONS, + ACCESS_MEDICAL, ACCESS_PSYCHOLOGY, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_EVA, ACCESS_HEADS, + ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, + ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, + ACCESS_MECH_MINING, ACCESS_MECH_ENGINE, ACCESS_MECH_SCIENCE, ACCESS_MECH_SECURITY, ACCESS_MECH_MEDICAL, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_VAULT, ACCESS_MINING_STATION, + ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM, ACCESS_AUX_BASE, ACCESS_TELEPORTER) + minimal_wildcard_access = list(ACCESS_HOP) + config_job = "head_of_personnel" + template_access = list(ACCESS_CAPTAIN, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/head_of_security + assignment = "Head of Security" + trim_state = "trim_headofsecurity" + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_MECH_SECURITY, + ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_AUX_BASE, + ACCESS_RESEARCH, ACCESS_ENGINE, ACCESS_MINING, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_MAILSORTING, ACCESS_EVA, ACCESS_TELEPORTER, + ACCESS_HEADS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) + wildcard_access = list(ACCESS_HOS) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_MECH_SECURITY, + ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_AUX_BASE, + ACCESS_RESEARCH, ACCESS_ENGINE, ACCESS_MINING, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_MAILSORTING, ACCESS_EVA, + ACCESS_HEADS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) + minimal_wildcard_access = list(ACCESS_HOS) + config_job = "head_of_security" + template_access = list(ACCESS_CAPTAIN, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/head_of_security/New() + . = ..() + + // Config check for if sec has maint access. + if(CONFIG_GET(flag/security_has_maint_access)) + access |= list(ACCESS_MAINT_TUNNELS) + +/datum/id_trim/job/janitor + assignment = "Janitor" + trim_state = "trim_janitor" + access = list(ACCESS_JANITOR, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_JANITOR, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) + config_job = "janitor" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOP, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/lawyer + assignment = "Lawyer" + trim_state = "trim_lawyer" + access = list(ACCESS_LAWYER, ACCESS_COURT, ACCESS_SEC_DOORS) + minimal_access = list(ACCESS_LAWYER, ACCESS_COURT, ACCESS_SEC_DOORS) + config_job = "lawyer" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOP, ACCESS_HOS, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/medical_doctor + assignment = "Medical Doctor" + trim_state = "trim_medicaldoctor" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_PHARMACY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_MECH_MEDICAL, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_MECH_MEDICAL, ACCESS_MINERAL_STOREROOM, ACCESS_PHARMACY) + config_job = "medical_doctor" + template_access = list(ACCESS_CAPTAIN, ACCESS_CMO, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/mime + assignment = "Mime" + trim_state = "trim_mime" + access = list(ACCESS_THEATRE) + minimal_access = list(ACCESS_THEATRE) + config_job = "mime" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOP, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/paramedic + assignment = "Paramedic" + trim_state = "trim_paramedic" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_MECH_MEDICAL, ACCESS_MINERAL_STOREROOM, ACCESS_MAINT_TUNNELS, + ACCESS_EVA, ACCESS_ENGINE, ACCESS_CONSTRUCTION, ACCESS_CARGO, ACCESS_HYDROPONICS, ACCESS_RESEARCH, ACCESS_AUX_BASE) + minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_MECH_MEDICAL, ACCESS_MINERAL_STOREROOM, ACCESS_MAINT_TUNNELS, ACCESS_EVA, + ACCESS_ENGINE, ACCESS_CONSTRUCTION, ACCESS_CARGO, ACCESS_HYDROPONICS, ACCESS_RESEARCH, ACCESS_AUX_BASE) + config_job = "paramedic" + template_access = list(ACCESS_CAPTAIN, ACCESS_CMO, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/prisoner + assignment = "Prisoner" + trim_state = "trim_prisoner" + config_job = "prisoner" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOP, ACCESS_HOS, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/prisoner/one + trim_state = "trim_prisoner_1" + template_access = null + +/datum/id_trim/job/prisoner/two + trim_state = "trim_prisoner_2" + template_access = null + +/datum/id_trim/job/prisoner/three + trim_state = "trim_prisoner_3" + template_access = null + +/datum/id_trim/job/prisoner/four + trim_state = "trim_prisoner_4" + template_access = null + +/datum/id_trim/job/prisoner/five + trim_state = "trim_prisoner_5" + template_access = null + +/datum/id_trim/job/prisoner/six + trim_state = "trim_prisoner_6" + template_access = null + +/datum/id_trim/job/prisoner/seven + trim_state = "trim_prisoner_7" + template_access = null + +/datum/id_trim/job/psychologist + assignment = "Psychologist" + trim_state = "trim_psychologist" + access = list(ACCESS_MEDICAL, ACCESS_PSYCHOLOGY) + minimal_access = list(ACCESS_MEDICAL, ACCESS_PSYCHOLOGY) + config_job = "psychologist" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOP, ACCESS_CMO, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/quartermaster + assignment = "Quartermaster" + trim_state = "trim_quartermaster" + access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_QM, ACCESS_MINING, ACCESS_MECH_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_VAULT, ACCESS_AUX_BASE) + minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_QM, ACCESS_MINING, ACCESS_MECH_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_VAULT, ACCESS_AUX_BASE) + config_job = "quartermaster" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOP, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/research_director + assignment = "Research Director" + trim_state = "trim_researchdirector" + access = list(ACCESS_HEADS, ACCESS_RND, ACCESS_GENETICS, ACCESS_MORGUE, + ACCESS_TOXINS, ACCESS_TELEPORTER, ACCESS_SEC_DOORS, ACCESS_MECH_SCIENCE, + ACCESS_RESEARCH, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_AI_UPLOAD, + ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM, + ACCESS_TECH_STORAGE, ACCESS_MINISAT, ACCESS_MAINT_TUNNELS, ACCESS_NETWORK, + ACCESS_TOXINS_STORAGE, ACCESS_AUX_BASE, ACCESS_EVA) + wildcard_access = list(ACCESS_RD) + minimal_access = list(ACCESS_HEADS, ACCESS_RND, ACCESS_GENETICS, ACCESS_MORGUE, + ACCESS_TOXINS, ACCESS_TELEPORTER, ACCESS_SEC_DOORS, ACCESS_MECH_SCIENCE, + ACCESS_RESEARCH, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_AI_UPLOAD, + ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM, + ACCESS_TECH_STORAGE, ACCESS_MINISAT, ACCESS_MAINT_TUNNELS, ACCESS_NETWORK, + ACCESS_TOXINS_STORAGE, ACCESS_AUX_BASE, ACCESS_EVA) + minimal_wildcard_access = list(ACCESS_RD) + config_job = "research_director" + template_access = list(ACCESS_CAPTAIN, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/roboticist + assignment = "Roboticist" + trim_state = "trim_roboticist" + access = list(ACCESS_ROBOTICS, ACCESS_RND, ACCESS_TOXINS, ACCESS_TOXINS_STORAGE, ACCESS_TECH_STORAGE, ACCESS_MORGUE, ACCESS_MECH_SCIENCE, + ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM, ACCESS_XENOBIOLOGY, ACCESS_GENETICS, ACCESS_AUX_BASE) + minimal_access = list(ACCESS_ROBOTICS, ACCESS_TECH_STORAGE, ACCESS_MORGUE, ACCESS_RESEARCH, ACCESS_MECH_SCIENCE, ACCESS_MINERAL_STOREROOM, + ACCESS_RND, ACCESS_AUX_BASE) + config_job = "roboticist" + template_access = list(ACCESS_CAPTAIN, ACCESS_RD, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/scientist + assignment = "Scientist" + trim_state = "trim_scientist" + access = list(ACCESS_ROBOTICS, ACCESS_RND, ACCESS_TOXINS, ACCESS_TOXINS_STORAGE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, + ACCESS_MECH_SCIENCE, ACCESS_MINERAL_STOREROOM, ACCESS_TECH_STORAGE, ACCESS_GENETICS, ACCESS_AUX_BASE) + minimal_access = list(ACCESS_RND, ACCESS_TOXINS, ACCESS_TOXINS_STORAGE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_MECH_SCIENCE, + ACCESS_MINERAL_STOREROOM, ACCESS_AUX_BASE) + config_job = "scientist" + template_access = list(ACCESS_CAPTAIN, ACCESS_RD, ACCESS_CHANGE_IDS) + +/// Sec officers have departmental variants. They each have their own trims with bonus departmental accesses. +/datum/id_trim/job/security_officer + assignment = "Security Officer" + trim_state = "trim_securityofficer" + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_MECH_SECURITY, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_FORENSICS_LOCKERS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_MECH_SECURITY, ACCESS_MINERAL_STOREROOM) + /// List of bonus departmental accesses that departmental sec officers get. + var/department_access = list() + config_job = "security_officer" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOS, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/security_officer/New() + . = ..() + + // Config check for if sec has maint access. + if(CONFIG_GET(flag/security_has_maint_access)) + access |= list(ACCESS_MAINT_TUNNELS) + + access |= department_access + +/datum/id_trim/job/security_officer/supply + assignment = "Security Officer (Cargo)" + department_access = list(ACCESS_MAILSORTING, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_CARGO, ACCESS_AUX_BASE) + +/datum/id_trim/job/security_officer/engineering + assignment = "Security Officer (Engineering)" + department_access = list(ACCESS_CONSTRUCTION, ACCESS_ENGINE, ACCESS_ATMOSPHERICS, ACCESS_AUX_BASE) + +/datum/id_trim/job/security_officer/medical + assignment = "Security Officer (Medical)" + department_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY) + +/datum/id_trim/job/security_officer/science + assignment = "Security Officer (Science)" + department_access = list(ACCESS_RESEARCH, ACCESS_RND, ACCESS_AUX_BASE) + +/datum/id_trim/job/shaft_miner + assignment = "Shaft Miner" + trim_state = "trim_shaftminer" + access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_QM, ACCESS_MINING, ACCESS_MECH_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_AUX_BASE) + minimal_access = list(ACCESS_MINING, ACCESS_MECH_MINING, ACCESS_MINING_STATION, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM, ACCESS_AUX_BASE) + config_job = "shaft_miner" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOP, ACCESS_CHANGE_IDS) + +/// ID card obtained from the mining Disney dollar points vending machine. +/datum/id_trim/job/shaft_miner/spare + access = list(ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MECH_MINING, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MECH_MINING, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM) + template_access = null + +/datum/id_trim/job/station_engineer + assignment = "Station Engineer" + trim_state = "trim_stationengineer" + access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_MECH_ENGINE, ACCESS_AUX_BASE, + ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_TCOMSAT, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_MECH_ENGINE, ACCESS_AUX_BASE, + ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_TCOMSAT, ACCESS_MINERAL_STOREROOM) + config_job = "station_engineer" + template_access = list(ACCESS_CAPTAIN, ACCESS_CE, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/virologist + assignment = "Virologist" + trim_state = "trim_virologist" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_MECH_MEDICAL, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MEDICAL, ACCESS_VIROLOGY, ACCESS_MECH_MEDICAL, ACCESS_MINERAL_STOREROOM) + config_job = "virologist" + template_access = list(ACCESS_CAPTAIN, ACCESS_CMO, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/warden + assignment = "Warden" + trim_state = "trim_warden" + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_MECH_SECURITY, ACCESS_MAINT_TUNNELS, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_FORENSICS_LOCKERS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_MECH_SECURITY, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) // See /datum/job/warden/get_access() + config_job = "warden" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOS, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/warden/New() + . = ..() + + // Config check for if sec has maint access. + if(CONFIG_GET(flag/security_has_maint_access)) + access |= list(ACCESS_MAINT_TUNNELS) diff --git a/code/datums/id_trim/misc.dm b/code/datums/id_trim/misc.dm new file mode 100644 index 00000000000..fdf21a1637a --- /dev/null +++ b/code/datums/id_trim/misc.dm @@ -0,0 +1,57 @@ +/// Trim for admins and debug cards. Has every single access in the game. +/datum/id_trim/admin + assignment = "Jannie" + trim_state = "trim_ert_janitor" + +/datum/id_trim/admin/New() + . = ..() + // Every single access in the game, all on one handy trim. + access = SSid_access.get_region_access_list(list(REGION_ALL_GLOBAL)) + +/// Trim for highlander cards, used during the highlander adminbus event. +/datum/id_trim/highlander + assignment = "Highlander" + trim_state = "trim_ert_deathcommando" + +/datum/id_trim/highlander/New() + . = ..() + access = SSid_access.get_region_access_list(list(REGION_CENTCOM, REGION_ALL_STATION)) + +/// Trim for the assassin outfit. +/datum/id_trim/reaper_assassin + assignment = "Reaper" + trim_state = "trim_ert_deathcommando" + +/datum/id_trim/highlander/New() + . = ..() + access = SSid_access.get_region_access_list(list(REGION_ALL_STATION)) + +/// Trim for the mobster outfit. +/datum/id_trim/mobster + assignment = "Mobster" + trim_state = "trim_assistant" + +/// Trim for VR outfits. +/datum/id_trim/vr + assignment = "VR Participant" + +/datum/id_trim/vr/New() + . = ..() + access |= SSid_access.get_region_access_list(list(REGION_ALL_STATION)) + +/// Trim for VR outfits. +/datum/id_trim/vr/operative + assignment = "Syndicate VR Operative" + +/datum/id_trim/vr/operative/New() + . = ..() + access |= list(ACCESS_SYNDICATE, ACCESS_MAINT_TUNNELS) + +/// Trim for the Tunnel Clown! outfit. Has all access. +/datum/id_trim/tunnel_clown + assignment = "Tunnel Clown!" + trim_state = "trim_clown" + +/datum/id_trim/tunnel_clown/New() + . = ..() + access |= SSid_access.get_region_access_list(list(REGION_ALL_STATION)) diff --git a/code/datums/id_trim/ruins.dm b/code/datums/id_trim/ruins.dm new file mode 100644 index 00000000000..824ae7696a2 --- /dev/null +++ b/code/datums/id_trim/ruins.dm @@ -0,0 +1,97 @@ +/// Generic away/pffstation trim. +/datum/id_trim/away + access = list(ACCESS_AWAY_GENERAL) + +/// Trim for the hotel ruin. Not Hilbert's Hotel. +/datum/id_trim/away/hotel + access = list(ACCESS_AWAY_GENERAL, ACCESS_AWAY_MAINT) + +/// Trim for the hotel ruin. Not Hilbert's Hotel. +/datum/id_trim/away/hotel/security + access = list(ACCESS_AWAY_GENERAL, ACCESS_AWAY_MAINT, ACCESS_AWAY_SEC) + +/// Trim for the oldstation ruin/Charlie station +/datum/id_trim/away/old/sec + access = list(ACCESS_AWAY_GENERAL, ACCESS_AWAY_SEC) + assignment = "Charlie Station Security Officer" + +/// Trim for the oldstation ruin/Charlie station +/datum/id_trim/away/old/sci + access = list(ACCESS_AWAY_GENERAL) + assignment = "Charlie Station Scientist" + +/// Trim for the oldstation ruin/Charlie station +/datum/id_trim/away/old/eng + access = list(ACCESS_AWAY_GENERAL, ACCESS_AWAY_ENGINE) + assignment = "Charlie Station Engineer" + +/// Trim for the oldstation ruin/Charlie station +/datum/id_trim/away/old/apc + access = list(ACCESS_ENGINE_EQUIP) + +/// Trim for the cat surgeon ruin. +/datum/id_trim/away/cat_surgeon + assignment = "Cat Surgeon" + trim_state = "trim_medicaldoctor" + access = list(ACCESS_AWAY_GENERAL, ACCESS_AWAY_MAINT) + +/// Trim for Hilber in Hilbert's Hotel. +/datum/id_trim/away/hilbert + assignment = "Head Researcher" + trim_state = "trim_researchdirector" + access = list(ACCESS_AWAY_GENERIC3, ACCESS_RESEARCH) + +/// Trim for beach bum lifeguards. +/datum/id_trim/lifeguard + assignment = "Lifeguard" + +/// Trim for beach bum bartenders. +/datum/id_trim/space_bartender + assignment = "Space Bartender" + trim_state = "trim_bartender" + access = list(ACCESS_BAR) + +/// Trim for various Centcom corpses. +/datum/id_trim/centcom/corpse/bridge_officer + assignment = "Bridge Officer" + access = list(ACCESS_CENT_CAPTAIN) + +/// Trim for various Centcom corpses. +/datum/id_trim/centcom/corpse/commander + assignment = "Commander" + access = list(ACCESS_CENT_CAPTAIN, ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_MEDICAL, ACCESS_CENT_STORAGE) + +/// Trim for various Centcom corpses. +/datum/id_trim/centcom/corpse/private_security + assignment = "Private Security Force" + access = list(ACCESS_CENT_CAPTAIN, ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_MEDICAL, ACCESS_CENT_STORAGE, ACCESS_SECURITY, ACCESS_MECH_SECURITY) + +/// Trim for various Centcom corpses. +/datum/id_trim/centcom/corpse/private_security/tradepost_officer + assignment = "Tradepost Officer" + +/// Trim for various Centcom corpses. +/datum/id_trim/centcom/corpse/assault + assignment = "Nanotrasen Assault Force" + access = list(ACCESS_CENT_CAPTAIN, ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_MEDICAL, ACCESS_CENT_STORAGE, ACCESS_SECURITY, ACCESS_MECH_SECURITY) + +/// Trim for various various ruins. +/datum/id_trim/engioutpost + assignment = "Senior Station Engineer" + trim_state = "trim_stationengineer" + access = list(ACCESS_AWAY_GENERAL, ACCESS_AWAY_ENGINE, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_MAINT_TUNNELS) + +/// Trim for various various ruins. +/datum/id_trim/job/station_engineer/gunner + assignment = "Gunner" + template_access = null + +/// Trim for silverscale pirates. +/datum/id_trim/pirate/silverscale + assignment = "Silver Scale Member" + trim_state = "trim_unknown" + +/// Trim for silverscale pirates. +/datum/id_trim/pirate/silverscale/captain + assignment = "Silver Scale VIP" + trim_state = "trim_captain" diff --git a/code/datums/id_trim/syndicate.dm b/code/datums/id_trim/syndicate.dm new file mode 100644 index 00000000000..aa03514b6b1 --- /dev/null +++ b/code/datums/id_trim/syndicate.dm @@ -0,0 +1,50 @@ +/// Trim for Syndicate mobs, outfits and corpses. +/datum/id_trim/syndicom + assignment = "Syndicate Overlord" + trim_state = "trim_syndicate" + access = list(ACCESS_SYNDICATE) + +/// Trim for Syndicate mobs, outfits and corpses. +/datum/id_trim/syndicom/crew + assignment = "Syndicate Operative" + access = list(ACCESS_SYNDICATE, ACCESS_ROBOTICS) + +/// Trim for Syndicate mobs, outfits and corpses. +/datum/id_trim/syndicom/captain + assignment = "Syndicate Ship Captain" + access = list(ACCESS_SYNDICATE, ACCESS_ROBOTICS) + +/// Trim for Syndicate mobs, outfits and corpses. +/datum/id_trim/battlecruiser + assignment = "Syndicate Battlecruiser Crew" + trim_state = "trim_syndicate" + access = list(ACCESS_SYNDICATE) + +/// Trim for Syndicate mobs, outfits and corpses. +/datum/id_trim/battlecruiser/captain + assignment = "Syndicate Battlecruiser Captain" + access = SYNDICATE_ACCESS + +/// Trim for Chameleon ID cards. Many outfits, nuke ops and some corpses hold Chameleon ID cards. +/datum/id_trim/chameleon + assignment = "Unknown" + access = list(ACCESS_SYNDICATE, ACCESS_MAINT_TUNNELS) + +/// Trim for Chameleon ID cards. Many outfits, nuke ops and some corpses hold Chameleon ID cards. +/datum/id_trim/chameleon/operative + assignment = "Syndicate Operative" + trim_state = "trim_syndicate" + +/// Trim for Chameleon ID cards. Many outfits, nuke ops and some corpses hold Chameleon ID cards. +/datum/id_trim/chameleon/operative/clown + assignment = "Syndicate Entertainment Operative" + +/// Trim for Chameleon ID cards. Many outfits, nuke ops and some corpses hold Chameleon ID cards. +/datum/id_trim/chameleon/operative/clown_leader + assignment = "Syndicate Entertainment Operative Leader" + access = list(ACCESS_MAINT_TUNNELS, ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER) + +/// Trim for Chameleon ID cards. Many outfits, nuke ops and some corpses hold Chameleon ID cards. +/datum/id_trim/chameleon/operative/nuke_leader + assignment = "Syndicate Operative Leader" + access = list(ACCESS_MAINT_TUNNELS, ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER) diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 47aefe0cc7d..cda5668fa40 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -489,7 +489,7 @@ A.admin_remove(usr) if (href_list["role_edit"]) - var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in sortList(get_all_jobs()) + var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in sortList(SSjob.station_jobs) if (!new_role) return assigned_role = new_role diff --git a/code/datums/outfit.dm b/code/datums/outfit.dm index 8286fa6c4a3..58fa7f10fc1 100755 --- a/code/datums/outfit.dm +++ b/code/datums/outfit.dm @@ -53,6 +53,9 @@ /// Type path of item to go in the idcard slot var/id = null + /// Type path of ID card trim associated with this outfit. + var/id_trim = null + /// Type path of item for left pocket slot var/l_pocket = null @@ -183,6 +186,10 @@ H.equip_to_slot_or_del(new glasses(H),ITEM_SLOT_EYES, TRUE) if(id) H.equip_to_slot_or_del(new id(H),ITEM_SLOT_ID, TRUE) + if(id_trim && H.wear_id) + var/obj/item/card/id/id_card = H.wear_id + if(istype(id_card) && !SSid_access.apply_trim_to_card(id_card, id_trim)) + WARNING("Unable to apply trim [id_trim] to [id_card] in outfit [name].") if(suit_store) H.equip_to_slot_or_del(new suit_store(H),ITEM_SLOT_SUITSTORE, TRUE) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index d2945316d2b..3064bb8057b 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -580,6 +580,16 @@ /atom/proc/get_examine_string(mob/user, thats = FALSE) return "[icon2html(src, user)] [thats? "That's ":""][get_examine_name(user)]" +/** + * Returns an extended list of examine strings for any contained ID cards. + * + * Arguments: + * * user - The user who is doing the examining. + */ +/atom/proc/get_id_examine_strings(mob/user) + . = list() + return + ///Used to insert text after the name but before the description in examine() /atom/proc/get_name_chaser(mob/user, list/name_chaser = list()) return name_chaser diff --git a/code/game/gamemodes/clown_ops/clown_ops.dm b/code/game/gamemodes/clown_ops/clown_ops.dm index 5017b1d38c0..087c3b400cf 100644 --- a/code/game/gamemodes/clown_ops/clown_ops.dm +++ b/code/game/gamemodes/clown_ops/clown_ops.dm @@ -36,7 +36,7 @@ ears = /obj/item/radio/headset/syndicate/alt l_pocket = /obj/item/pinpointer/nuke/syndicate r_pocket = /obj/item/bikehorn - id = /obj/item/card/id/syndicate + id = /obj/item/card/id/advanced/chameleon backpack_contents = list(/obj/item/storage/box/survival/syndie=1,\ /obj/item/kitchen/knife/combat/survival, /obj/item/dnainjector/clumsymut, //in case you want to be clumsy for the memes @@ -46,11 +46,14 @@ uplink_type = /obj/item/uplink/clownop + id_trim = /datum/id_trim/chameleon/operative/clown + /datum/outfit/syndicate/clownop/no_crystals tc = 0 /datum/outfit/syndicate/clownop/leader name = "Clown Operative Leader - Basic" - id = /obj/item/card/id/syndicate/nuke_leader gloves = /obj/item/clothing/gloves/krav_maga/combatglovesplus command_radio = TRUE + + id_trim = /datum/id_trim/chameleon/operative/clown_leader diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index 178519c61d7..0adb40be6ba 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -114,7 +114,7 @@ back = /obj/item/storage/backpack/fireproof ears = /obj/item/radio/headset/syndicate/alt l_pocket = /obj/item/modular_computer/tablet/nukeops - id = /obj/item/card/id/syndicate + id = /obj/item/card/id/advanced/chameleon belt = /obj/item/gun/ballistic/automatic/pistol backpack_contents = list(/obj/item/storage/box/survival/syndie=1,\ /obj/item/kitchen/knife/combat/survival) @@ -125,13 +125,15 @@ var/command_radio = FALSE var/uplink_type = /obj/item/uplink/nuclear + id_trim = /datum/id_trim/chameleon/operative /datum/outfit/syndicate/leader name = "Syndicate Leader - Basic" - id = /obj/item/card/id/syndicate/nuke_leader gloves = /obj/item/clothing/gloves/krav_maga/combatglovesplus command_radio = TRUE + id_trim = /datum/id_trim/chameleon/operative/nuke_leader + /datum/outfit/syndicate/no_crystals name = "Syndicate Operative - Reinforcement" tc = 0 diff --git a/code/game/gamemodes/sandbox/airlock_maker.dm b/code/game/gamemodes/sandbox/airlock_maker.dm index fca6cc484cd..92eafdf665a 100644 --- a/code/game/gamemodes/sandbox/airlock_maker.dm +++ b/code/game/gamemodes/sandbox/airlock_maker.dm @@ -45,8 +45,9 @@ var/list/leftcolumn = list() var/list/rightcolumn = list() leftcolumn += "Required Access" - for(var/access in get_all_accesses()) - leftcolumn += linkpretty("access=[access]",get_access_desc(access),access in access_used) + var/list/all_station_access = SSid_access.get_region_access_list(list(REGION_ALL_STATION)) + for(var/access in all_station_access) + leftcolumn += linkpretty("access=[access]",SSid_access.get_access_desc(access),access in access_used) leftcolumn += "Require all listed accesses: [linkpretty("reqall",null,require_all)]" rightcolumn += "Paintjob" diff --git a/code/game/gamemodes/sandbox/h_sandbox.dm b/code/game/gamemodes/sandbox/h_sandbox.dm index 7a984f34676..bf091b98706 100644 --- a/code/game/gamemodes/sandbox/h_sandbox.dm +++ b/code/game/gamemodes/sandbox/h_sandbox.dm @@ -205,11 +205,10 @@ GLOBAL_VAR_INIT(hsboxspawn, TRUE) // All access ID // if("hsbaaid") - var/obj/item/card/id/gold/ID = new(usr.loc) + var/obj/item/card/id/advanced/debug/ID = new(usr.loc) ID.registered_name = usr.real_name - ID.assignment = "Sandbox" - ID.access = get_all_accesses() ID.update_label() + ID.update_icon() // // RCD - starts with full clip @@ -301,3 +300,12 @@ GLOBAL_VAR_INIT(hsboxspawn, TRUE) if(CONFIG_GET(flag/sandbox_autoclose)) usr << browse(null,"window=sandbox") + +/// Simple helper trim for sandbox mode's ID cards. Comes with station AA. +/datum/id_trim/sandbox + assignment = "Sandbox" + trim_state = "trim_captain" + +/datum/id_trim/sandbox/New() + . = ..() + access = SSid_access.get_region_access_list(list(REGION_ALL_STATION)) diff --git a/code/game/machinery/PDApainter.dm b/code/game/machinery/PDApainter.dm index cb393a5e35b..656230defc3 100644 --- a/code/game/machinery/PDApainter.dm +++ b/code/game/machinery/PDApainter.dm @@ -1,13 +1,31 @@ +/// Basic machine used to paint PDAs and re-trim ID cards. /obj/machinery/pdapainter - name = "\improper PDA painter" - desc = "A PDA painting machine. To use, simply insert your PDA and choose the desired preset paint scheme." + name = "\improper PDA & ID Painter" + desc = "A painting machine that can be used to paint PDAs and trim IDs. To use, simply insert the item and choose the desired preset." icon = 'icons/obj/pda.dmi' icon_state = "pdapainter" base_icon_state = "pdapainter" density = TRUE max_integrity = 200 - var/obj/item/pda/storedpda = null - var/list/colorlist = list() + /// Current ID card inserted into the machine. + var/obj/item/card/id/stored_id_card = null + /// Current PDA inserted into the machine. + var/obj/item/pda/stored_pda = null + /// A blacklist of PDA types that we should not be able to paint. + var/static/list/pda_type_blacklist = list( + /obj/item/pda/ai/pai, + /obj/item/pda/ai, + /obj/item/pda/heads, + /obj/item/pda/clear, + /obj/item/pda/syndicate, + /obj/item/pda/chameleon, + /obj/item/pda/chameleon/broken) + /// A list of the PDA types that this machine can currently paint. + var/list/pda_types = list() + /// A list of the card trims that this machine can currently imprint onto a card. + var/list/card_trims = list() + /// Set to a region define (REGION_SECURITY for example) to create a departmental variant, limited to departmental options. If null, this is unrestricted. + var/target_dept /obj/machinery/pdapainter/update_icon_state() if(machine_stat & BROKEN) @@ -22,45 +40,56 @@ if(machine_stat & BROKEN) return - if(storedpda) + if(stored_pda || stored_id_card) . += "[initial(icon_state)]-closed" /obj/machinery/pdapainter/Initialize() . = ..() - var/list/blocked = list( - /obj/item/pda/ai/pai, - /obj/item/pda/ai, - /obj/item/pda/heads, - /obj/item/pda/clear, - /obj/item/pda/syndicate, - /obj/item/pda/chameleon, - /obj/item/pda/chameleon/broken) - for(var/P in typesof(/obj/item/pda) - blocked) - var/obj/item/pda/D = new P + if(!target_dept) + pda_types = SSid_access.station_pda_templates.Copy() + card_trims = SSid_access.station_job_templates.Copy() + return - //D.name = "PDA Style [colorlist.len+1]" //Gotta set the name, otherwise it all comes up as "PDA" - D.name = D.icon_state //PDAs don't have unique names, but using the sprite names works. - - src.colorlist += D + // Cache the manager list, then check through each manager. + // If we get a region match, add their trim templates and PDA paths to our lists. + var/list/manager_cache = SSid_access.sub_department_managers_tgui + for(var/access_txt in manager_cache) + var/list/manager_info = manager_cache[access_txt] + var/list/manager_regions = manager_info["regions"] + if(target_dept in manager_regions) + var/list/pda_list = manager_info["pdas"] + var/list/trim_list = manager_info["templates"] + pda_types |= pda_list + card_trims |= trim_list /obj/machinery/pdapainter/Destroy() - QDEL_NULL(storedpda) + QDEL_NULL(stored_pda) + QDEL_NULL(stored_id_card) return ..() /obj/machinery/pdapainter/on_deconstruction() - if(storedpda) - storedpda.forceMove(loc) - storedpda = null + // Don't use ejection procs as we're gonna be destroyed anyway, so no need to update icons or anything. + if(stored_pda) + stored_pda.forceMove(loc) + stored_pda = null + if(stored_id_card) + stored_id_card.forceMove(loc) + stored_id_card = null /obj/machinery/pdapainter/contents_explosion(severity, target) - if(storedpda) - storedpda.ex_act(severity, target) + if(stored_pda) + stored_pda.ex_act(severity, target) + if(stored_id_card) + stored_id_card.ex_act(severity, target) /obj/machinery/pdapainter/handle_atom_del(atom/A) - if(A == storedpda) - storedpda = null - update_appearance() + if(A == stored_pda) + stored_pda = null + update_appearance(UPDATE_ICON) + if(A == stored_id_card) + stored_id_card = null + update_appearance(UPDATE_ICON) /obj/machinery/pdapainter/attackby(obj/item/O, mob/living/user, params) if(machine_stat & BROKEN) @@ -76,68 +105,243 @@ to_chat(user, "You repair [src].") set_machine_stat(machine_stat & ~BROKEN) obj_integrity = max_integrity - update_appearance() + update_appearance(UPDATE_ICON) + return + return ..() - else - return ..() - - else if(default_unfasten_wrench(user, O)) + if(default_unfasten_wrench(user, O)) power_change() return - else if(istype(O, /obj/item/pda)) - if(storedpda) - to_chat(user, "There is already a PDA inside!") - return - else if(!user.transferItemToLoc(O, src)) - return - storedpda = O - O.add_fingerprint(user) - update_appearance() + // Chameleon checks first so they can exit the logic early if they're detected. + if(istype(O, /obj/item/card/id/advanced/chameleon)) + to_chat(user, "The machine rejects your [src]. It's clearly not a compatible ID card.") + return - else - return ..() + if(istype(O, /obj/item/pda/chameleon)) + to_chat(user, "The machine rejects your [src]. It's clearly not a compatible PDA.") + return + + if(istype(O, /obj/item/pda)) + insert_pda(O, user) + return + + if(istype(O, /obj/item/card/id)) + if(stored_id_card) + to_chat(user, "There is already an ID card inside!") + return + + if(!user.transferItemToLoc(O, src)) + return + + stored_id_card = O + O.add_fingerprint(user) + update_appearance(UPDATE_ICON) + return + + return ..() /obj/machinery/pdapainter/deconstruct(disassembled = TRUE) obj_break() -/obj/machinery/pdapainter/attack_hand(mob/user, list/modifiers) +/** + * Insert a PDA into the machine. + * + * Will swap PDAs if one is already inside. Attempts to put the PDA into the user's hands if possible. + * Returns TRUE on success, FALSE otherwise. + * Arguments: + * * new_pda - The PDA to insert. + * * user - The user to try and eject the PDA into the hands of. + */ +/obj/machinery/pdapainter/proc/insert_pda(obj/item/pda/new_pda, mob/living/user) + if(!istype(new_pda)) + return FALSE + + if(user && !user.transferItemToLoc(new_pda, src)) + return FALSE + else + new_pda.forceMove(src) + + if(stored_pda) + eject_pda(user) + + stored_pda = new_pda + new_pda.add_fingerprint(user) + update_icon() + return TRUE + +/** + * Eject the stored PDA into the user's hands if possible, otherwise on the floor. + * + * Arguments: + * * user - The user to try and eject the PDA into the hands of. + */ +/obj/machinery/pdapainter/proc/eject_pda(mob/living/user) + if(stored_pda) + if(user && !issilicon(user) && in_range(src, user)) + user.put_in_hands(stored_pda) + else + stored_pda.forceMove(drop_location()) + + stored_pda = null + update_icon() + +/** + * Insert an ID card into the machine. + * + * Will swap ID cards if one is already inside. Attempts to put the card into the user's hands if possible. + * Returns TRUE on success, FALSE otherwise. + * Arguments: + * * new_id_card - The ID card to insert. + * * user - The user to try and eject the PDA into the hands of. + */ +/obj/machinery/pdapainter/proc/insert_id_card(obj/item/card/id/new_id_card, mob/living/user) + if(!istype(new_id_card)) + return FALSE + + if(user && !user.transferItemToLoc(new_id_card, src)) + return FALSE + else + new_id_card.forceMove(src) + + if(stored_id_card) + eject_id_card(user) + + stored_id_card = new_id_card + new_id_card.add_fingerprint(user) + update_icon() + return TRUE + +/** + * Eject the stored ID card into the user's hands if possible, otherwise on the floor. + * + * Arguments: + * * user - The user to try and eject the ID card into the hands of. + */ +/obj/machinery/pdapainter/proc/eject_id_card(mob/living/user) + if(stored_id_card) + if(user && !issilicon(user) && in_range(src, user)) + user.put_in_hands(stored_id_card) + else + stored_id_card.forceMove(drop_location()) + + stored_id_card = null + update_appearance(UPDATE_ICON) + +/obj/machinery/pdapainter/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PaintingMachine", name) + ui.open() + +/obj/machinery/pdapainter/ui_data(mob/user) + var/data = list() + + if(stored_pda) + data["hasPDA"] = TRUE + data["pdaName"] = stored_pda.name + else + data["hasPDA"] = FALSE + data["pdaName"] = null + + if(stored_id_card) + data["hasID"] = TRUE + data["idName"] = stored_id_card.name + else + data["hasID"] = FALSE + data["idName"] = null + + return data + +/obj/machinery/pdapainter/ui_static_data(mob/user) + var/data = list() + + data["pdaTypes"] = pda_types + data["cardTrims"] = card_trims + + return data + +/obj/machinery/pdapainter/ui_act(action, params) . = ..() if(.) return - if(storedpda) - if(machine_stat & BROKEN) //otherwise the PDA is stuck until repaired - ejectpda() - to_chat(user, "You manage to eject the loaded PDA.") - else - var/obj/item/pda/P - P = input(user, "Select your color!", "PDA Painting") as null|anything in sortNames(colorlist) - if(!P) - return - if(!in_range(src, user)) - return - if(!storedpda)//is the pda still there? - return - storedpda.icon_state = P.icon_state - storedpda.desc = P.desc - ejectpda() + switch(action) + if("eject_pda") + if((machine_stat & BROKEN)) + return TRUE - else - to_chat(user, "[src] is empty!") + var/obj/item/held_item = usr.get_active_held_item() + if(istype(held_item, /obj/item/pda)) + // If we successfully inserted, we've ejected the old item. Return early. + if(insert_pda(held_item, usr)) + return TRUE + // If we did not successfully insert, try to eject. + if(stored_pda) + eject_pda(usr) + return TRUE + return TRUE + if("eject_card") + if((machine_stat & BROKEN)) + return TRUE -/obj/machinery/pdapainter/verb/ejectpda() - set name = "Eject PDA" - set category = "Object" - set src in oview(1) + var/obj/item/held_item = usr.get_active_held_item() + if(istype(held_item, /obj/item/card/id)) + // If we successfully inserted, we've ejected the old item. Return early. + if(insert_id_card(held_item, usr)) + return TRUE + // If we did not successfully insert, try to eject. + if(stored_id_card) + eject_id_card(usr) + return TRUE - if(usr.stat != CONSCIOUS || HAS_TRAIT(usr, TRAIT_HANDS_BLOCKED)) - return + return TRUE + if("trim_pda") + if((machine_stat & BROKEN) || !stored_pda) + return TRUE - if(storedpda) - storedpda.forceMove(drop_location()) - storedpda = null - update_appearance() - else - to_chat(usr, "[src] is empty!") + var/selection = params["selection"] + for(var/path in pda_types) + if(!(pda_types[path] == selection)) + continue + + var/obj/item/pda/pda_path = path + stored_pda.icon_state = initial(pda_path.icon_state) + stored_pda.desc = initial(pda_path.desc) + return TRUE + if("trim_card") + if((machine_stat & BROKEN) || !stored_id_card) + return TRUE + + var/selection = params["selection"] + for(var/path in card_trims) + if(!(card_trims[path] == selection)) + continue + + if(SSid_access.apply_trim_to_card(stored_id_card, path, copy_access = FALSE)) + return TRUE + + to_chat(usr, "The trim you selected could not be added to \the [src]. You will need a rarer ID card to imprint that trim data.") + + return TRUE + +/// Security departmental variant. Limited to PDAs defined in the SSid_access.sub_department_managers_tgui data structure. +/obj/machinery/pdapainter/security + name = "\improper Security PDA & ID Painter" + target_dept = REGION_SECURITY + +/// Medical departmental variant. Limited to PDAs defined in the SSid_access.sub_department_managers_tgui data structure. +/obj/machinery/pdapainter/medbay + name = "\improper Medbay PDA & ID Painter" + target_dept = REGION_MEDBAY + +/// Science departmental variant. Limited to PDAs defined in the SSid_access.sub_department_managers_tgui data structure. +/obj/machinery/pdapainter/research + name = "\improper Research PDA & ID Painter" + target_dept = REGION_RESEARCH + +/// Engineering departmental variant. Limited to PDAs defined in the SSid_access.sub_department_managers_tgui data structure. +/obj/machinery/pdapainter/engineering + name = "\improper Engineering PDA & ID Painter" + target_dept = REGION_ENGINEERING diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index f81b6d6774c..87ebb60fa32 100755 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -75,7 +75,7 @@ return obj_flags |= EMAGGED if (authenticated) - authorize_access = get_all_accesses() + authorize_access = SSid_access.get_region_access_list(list(REGION_ALL_STATION)) to_chat(user, "You scramble the communication routing circuits!") playsound(src, 'sound/machines/terminal_alert.ogg', 50, FALSE) @@ -302,7 +302,7 @@ if (obj_flags & EMAGGED) authenticated = TRUE - authorize_access = get_all_accesses() + authorize_access = SSid_access.get_region_access_list(list(REGION_ALL_STATION)) authorize_name = "Unknown" to_chat(usr, "[src] lets out a quiet alarm as its login is overridden.") playsound(src, 'sound/machines/terminal_alert.ogg', 25, FALSE) @@ -311,7 +311,7 @@ var/obj/item/card/id/id_card = L.get_idcard(hand_first = TRUE) if (check_access(id_card)) authenticated = TRUE - authorize_access = id_card.access + authorize_access = id_card.access.Copy() authorize_name = "[id_card.registered_name] - [id_card.assignment]" state = STATE_MAIN diff --git a/code/game/machinery/computer/prisoner/_prisoner.dm b/code/game/machinery/computer/prisoner/_prisoner.dm index d07c351a22f..fb453fb8ccb 100644 --- a/code/game/machinery/computer/prisoner/_prisoner.dm +++ b/code/game/machinery/computer/prisoner/_prisoner.dm @@ -1,11 +1,11 @@ /obj/machinery/computer/prisoner - var/obj/item/card/id/prisoner/contained_id + var/obj/item/card/id/advanced/prisoner/contained_id /obj/machinery/computer/prisoner/Destroy() if(contained_id) contained_id.forceMove(get_turf(src)) return ..() - + /obj/machinery/computer/prisoner/examine(mob/user) . = ..() @@ -18,7 +18,7 @@ id_eject(user) return ..() -/obj/machinery/computer/prisoner/proc/id_insert(mob/user, obj/item/card/id/prisoner/P) +/obj/machinery/computer/prisoner/proc/id_insert(mob/user, obj/item/card/id/advanced/prisoner/P) if(istype(P)) if(contained_id) to_chat(user, "There's already an ID card in the console!") @@ -46,7 +46,7 @@ updateUsrDialog() /obj/machinery/computer/prisoner/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/card/id/prisoner)) + if(istype(I, /obj/item/card/id/advanced/prisoner)) id_insert(user, I) else return ..() diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index 8b995ae3285..e8ce346695e 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -739,7 +739,7 @@ What a mess.*/ if((istype(active1, /datum/data/record) && L.Find(rank))) temp = "
Rank:
" temp += "" else @@ -751,7 +751,7 @@ What a mess.*/ if("Change Rank") if(active1) active1.fields["rank"] = strip_html(href_list["rank"]) - if(href_list["rank"] in get_all_jobs()) + if(href_list["rank"] in SSjob.station_jobs) active1.fields["real_rank"] = href_list["real_rank"] if("Change Criminal Status") diff --git a/code/game/machinery/doors/airlock_electronics.dm b/code/game/machinery/doors/airlock_electronics.dm index 30c669ee7b0..cce851049ed 100644 --- a/code/game/machinery/doors/airlock_electronics.dm +++ b/code/game/machinery/doors/airlock_electronics.dm @@ -25,21 +25,11 @@ /obj/item/electronics/airlock/ui_static_data(mob/user) var/list/data = list() - var/list/regions = list() - for(var/i in 1 to 7) - var/list/accesses = list() - for(var/access in get_region_accesses(i)) - if (get_access_desc(access)) - accesses += list(list( - "desc" = replacetext(get_access_desc(access), " ", " "), - "ref" = access, - )) - regions += list(list( - "name" = get_region_accesses_name(i), - "regid" = i, - "accesses" = accesses - )) + var/list/regions = list() + var/list/tgui_region_data = SSid_access.all_region_access_tgui + for(var/region in SSid_access.station_regions) + regions += tgui_region_data[region] data["regions"] = regions return data @@ -62,7 +52,7 @@ one_access = 0 . = TRUE if("grant_all") - accesses = get_all_accesses() + accesses = SSid_access.get_region_access_list(list(REGION_ALL_STATION)) . = TRUE if("one_access") one_access = !one_access @@ -79,16 +69,16 @@ unres_sides ^= unres_direction //XOR, toggles only the bit that was clicked . = TRUE if("grant_region") - var/region = text2num(params["region"]) + var/region = params["region"] if(isnull(region)) return - accesses |= get_region_accesses(region) + accesses |= SSid_access.get_region_access_list(list(region)) . = TRUE if("deny_region") - var/region = text2num(params["region"]) + var/region = params["region"] if(isnull(region)) return - accesses -= get_region_accesses(region) + accesses -= SSid_access.get_region_access_list(list(region)) . = TRUE /obj/item/electronics/airlock/ui_host() diff --git a/code/game/machinery/gulag_item_reclaimer.dm b/code/game/machinery/gulag_item_reclaimer.dm index 74bfb358a07..4274e82cb10 100644 --- a/code/game/machinery/gulag_item_reclaimer.dm +++ b/code/game/machinery/gulag_item_reclaimer.dm @@ -43,8 +43,8 @@ if(isliving(user)) var/mob/living/L = user I = L.get_idcard(TRUE) - if(istype(I, /obj/item/card/id/prisoner)) - var/obj/item/card/id/prisoner/P = I + if(istype(I, /obj/item/card/id/advanced/prisoner)) + var/obj/item/card/id/advanced/prisoner/P = I if(P.points >= P.goal) can_reclaim = TRUE diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm index 72b1d94ce29..b23a147d90a 100644 --- a/code/game/machinery/porta_turret/portable_turret.dm +++ b/code/game/machinery/porta_turret/portable_turret.dm @@ -546,7 +546,7 @@ DEFINE_BITFIELD(turret_flags, list( return 10 if(turret_flags & TURRET_FLAG_AUTH_WEAPONS) //check for weapon authorization - if(isnull(perp.wear_id) || istype(perp.wear_id.GetID(), /obj/item/card/id/syndicate)) + if(isnull(perp.wear_id) || istype(perp.wear_id.GetID(), /obj/item/card/id/advanced/chameleon)) if(allowed(perp)) //if the perp has security access, return 0 return 0 diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm index 2d3aca0c067..e6a14193d5c 100644 --- a/code/game/machinery/recycler.dm +++ b/code/game/machinery/recycler.dm @@ -33,8 +33,8 @@ ) AddComponent(/datum/component/material_container, allowed_materials, INFINITY, MATCONTAINER_NO_INSERT|BREAKDOWN_FLAGS_RECYCLER) . = ..() - update_appearance() - req_one_access = get_all_accesses() + get_all_centcom_access() + update_appearance(UPDATE_ICON) + req_one_access = SSid_access.get_region_access_list(list(REGION_ALL_STATION, REGION_CENTCOM)) /obj/machinery/recycler/RefreshParts() var/amt_made = 0 diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm index 044c3faab17..cd5e804089a 100644 --- a/code/game/objects/items/cards_ids.dm +++ b/code/game/objects/items/cards_ids.dm @@ -61,38 +61,52 @@ * ID CARDS */ +/// "Retro" ID card that renders itself as the icon state with no overlays. /obj/item/card/id - name = "identification card" + name = "retro identification card" desc = "A card used to provide ID and determine access across the station." - icon_state = "id" + icon_state = "card_grey" + worn_icon_state = "card_retro" inhand_icon_state = "card-id" lefthand_file = 'icons/mob/inhands/equipment/idcards_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi' slot_flags = ITEM_SLOT_ID armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 100, ACID = 100) resistance_flags = FIRE_PROOF | ACID_PROOF - var/id_type_name = "identification card" - var/mining_points = 0 //For redeeming at mining equipment vendors - ///The stuff that makes you open doors and shit - var/list/access = list() - ///Access that cannot be removed by the ID console. Do not add access levels that are actually visible in the console here if a HoP knowing what kind of ID he's modifying is a concern. - var/list/sticky_access + + /// How many magical mining Disney Dollars this card has for spending at the mining equipment vendors. + var/mining_points = 0 /// The name registered on the card (for example: Dr Bryan See) var/registered_name = null - ///The job name registered on the card (for example: Assistant) - var/assignment = null - /// mapping aid - var/access_txt + /// Linked bank account. var/datum/bank_account/registered_account + /// Linked paystand. var/obj/machinery/paystand/my_store - var/uses_overlays = TRUE - var/icon/cached_flat_icon - var/registered_age = 13 // default age for ss13 players + /// Registered owner's age. + var/registered_age = 13 + + /// The job name registered on the card (for example: Assistant). + var/assignment + + /// Trim datum associated with the card. Controls which job icon is displayed on the card and which accesses do not require wildcards. + var/datum/id_trim/trim + + /// Access levels held by this card. + var/list/access = list() + + /// List of wildcard slot names as keys with lists of wildcard data as values. + var/list/wildcard_slots = list() /obj/item/card/id/Initialize(mapload) . = ..() - if(mapload && access_txt) - access = text2access(access_txt) + + // Applying the trim updates the label and icon, so don't do this twice. + if(ispath(trim)) + SSid_access.apply_trim_to_card(src, trim) + else + update_label() + update_icon() + RegisterSignal(src, COMSIG_ATOM_UPDATED_ICON, .proc/update_in_wallet) /obj/item/card/id/Destroy() @@ -102,6 +116,264 @@ my_store.my_card = null return ..() +/obj/item/card/id/get_id_examine_strings(mob/user) + . = ..() + . += list("[icon2html(get_icon_source(), user, extra_classes = "bigicon")]") + +/// Simple helper proc. Returns the source of the icon for this card. Advanced cards can override this to return their icon that has been cached due to using overlays. +/obj/item/card/id/proc/get_icon_source() + return src + +/** + * Helper proc, checks whether the ID card can hold any given set of wildcards. + * + * Returns TRUE if the card can hold the wildcards, FALSE otherwise. + * Arguments: + * * wildcard_list - List of accesses to check. + * * try_wildcard - If not null, will attempt to add wildcards for this wildcard specifically and will return FALSE if the card cannot hold all wildcards in this slot. + */ +/obj/item/card/id/proc/can_add_wildcards(list/wildcard_list, try_wildcard = null) + if(!length(wildcard_list)) + return TRUE + + var/list/new_wildcard_limits = list() + + for(var/flag_name in wildcard_slots) + if(try_wildcard && !(flag_name == try_wildcard)) + continue + var/list/wildcard_info = wildcard_slots[flag_name] + new_wildcard_limits[flag_name] = wildcard_info["limit"] - length(wildcard_info["usage"]) + + if(!length(new_wildcard_limits)) + return FALSE + + var/wildcard_allocated + for(var/wildcard in wildcard_list) + var/wildcard_flag = SSid_access.get_access_flag(wildcard) + wildcard_allocated = FALSE + for(var/flag_name in new_wildcard_limits) + var/limit_flags = SSid_access.wildcard_flags_by_wildcard[flag_name] + if(!(wildcard_flag & limit_flags)) + continue + // Negative limits mean infinite slots. Positive limits mean limited slots still available. 0 slots means no slots. + if(new_wildcard_limits[flag_name] == 0) + continue + new_wildcard_limits[flag_name]-- + wildcard_allocated = TRUE + break + if(!wildcard_allocated) + return FALSE + + return TRUE + +/** + * Attempts to add the given wildcards to the ID card. + * + * Arguments: + * * wildcard_list - List of accesses to add. + * * try_wildcard - If not null, will attempt to add all wildcards to this wildcard slot only. + * * mode - The method to use when adding wildcards. See define for ERROR_ON_FAIL + */ +/obj/item/card/id/proc/add_wildcards(list/wildcard_list, try_wildcard = null, mode = ERROR_ON_FAIL) + var/wildcard_allocated + // Iterate through each wildcard in our list. Get its access flag. Then iterate over wildcard slots and try to fit it in. + for(var/wildcard in wildcard_list) + var/wildcard_flag = SSid_access.get_access_flag(wildcard) + wildcard_allocated = FALSE + for(var/flag_name in wildcard_slots) + if(flag_name == WILDCARD_NAME_FORCED) + continue + + if(try_wildcard && !(flag_name == try_wildcard)) + continue + + var/limit_flags = SSid_access.wildcard_flags_by_wildcard[flag_name] + + if(!(wildcard_flag & limit_flags)) + continue + + var/list/wildcard_info = wildcard_slots[flag_name] + var/wildcard_limit = wildcard_info["limit"] + var/list/wildcard_usage = wildcard_info["usage"] + + var/wildcard_count = wildcard_limit - length(wildcard_usage) + + // Negative limits mean infinite slots. Positive limits mean limited slots still available. 0 slots means no slots. + if(wildcard_count == 0) + continue + + wildcard_usage |= wildcard + access |= wildcard + wildcard_allocated = TRUE + break + // Fallback for if we couldn't allocate the wildcard for some reason. + if(!wildcard_allocated) + if(mode == ERROR_ON_FAIL) + CRASH("Wildcard ([wildcard]) could not be added to [src].") + + if(mode == TRY_ADD_ALL) + continue + + // If the card has no info for historic forced wildcards, create the list. + if(!wildcard_slots[WILDCARD_NAME_FORCED]) + wildcard_slots[WILDCARD_NAME_FORCED] = list(limit = 0, usage = list()) + + var/list/wildcard_info = wildcard_slots[WILDCARD_NAME_FORCED] + var/list/wildcard_usage = wildcard_info["usage"] + wildcard_usage |= wildcard + access |= wildcard + wildcard_info["limit"] = length(wildcard_usage) + +/** + * Removes wildcards from the ID card. + * + * Arguments: + * * wildcard_list - List of accesses to remove. + */ +/obj/item/card/id/proc/remove_wildcards(list/wildcard_list) + var/wildcard_removed + // Iterate through each wildcard in our list. Get its access flag. Then iterate over wildcard slots and try to remove it. + for(var/wildcard in wildcard_list) + wildcard_removed = FALSE + for(var/flag_name in wildcard_slots) + if(flag_name == WILDCARD_NAME_FORCED) + continue + + var/list/wildcard_info = wildcard_slots[flag_name] + var/wildcard_usage = wildcard_info["usage"] + + if(!(wildcard in wildcard_usage)) + continue + + wildcard_usage -= wildcard + access -= wildcard + wildcard_removed = TRUE + break + // Fallback to see if this was a force-added wildcard. + if(!wildcard_removed) + // If the card has no info for historic forced wildcards, that's an error state. + if(!wildcard_slots[WILDCARD_NAME_FORCED]) + stack_trace("Wildcard ([wildcard]) could not be removed from [src]. This card has no forced wildcard data and the wildcard is not in this card's wildcard lists.") + + var/list/wildcard_info = wildcard_slots[WILDCARD_NAME_FORCED] + var/wildcard_usage = wildcard_info["usage"] + + if(!(wildcard in wildcard_usage)) + stack_trace("Wildcard ([wildcard]) could not be removed from [src]. This access is not a wildcard on this card.") + + wildcard_usage -= wildcard + access -= wildcard + wildcard_info["limit"] = length(wildcard_usage) + + if(!wildcard_info["limit"]) + wildcard_slots -= WILDCARD_NAME_FORCED + +/** + * Attempts to add the given accesses to the ID card as non-wildcards. + * + * Depending on the mode, may add accesses as wildcards or error if it can't add them as non-wildcards. + * Arguments: + * * add_accesses - List of accesses to check. + * * try_wildcard - If not null, will attempt to add all accesses that require wildcard slots to this wildcard slot only. + * * mode - The method to use when adding accesses. See define for ERROR_ON_FAIL + */ +/obj/item/card/id/proc/add_access(list/add_accesses, try_wildcard = null, mode = ERROR_ON_FAIL) + var/list/wildcard_access = list() + var/list/normal_access = list() + + build_access_lists(add_accesses, normal_access, wildcard_access) + + // Check if we can add the wildcards. + if(mode == ERROR_ON_FAIL) + if(!can_add_wildcards(wildcard_access, try_wildcard)) + CRASH("Cannot add wildcards from \[[add_accesses.Join(",")]\] to [src]") + + // All clear to add the accesses. + access |= normal_access + if(mode != TRY_ADD_ALL_NO_WILDCARD) + add_wildcards(wildcard_access, try_wildcard, mode = mode) + + return TRUE + +/** + * Removes the given accesses from the ID Card. + * + * Will remove the wildcards if the accesses given are on the card as wildcard accesses. + * Arguments: + * * rem_accesses - List of accesses to remove. + */ +/obj/item/card/id/proc/remove_access(list/rem_accesses) + var/list/wildcard_access = list() + var/list/normal_access = list() + + build_access_lists(rem_accesses, normal_access, wildcard_access) + + access -= normal_access + remove_wildcards(wildcard_access) + +/** + * Attempts to set the card's accesses to the given accesses, clearing all accesses not in the given list. + * + * Depending on the mode, may add accesses as wildcards or error if it can't add them as non-wildcards. + * Arguments: + * * new_access_list - List of all accesses that this card should hold exclusively. + * * mode - The method to use when setting accesses. See define for ERROR_ON_FAIL + */ +/obj/item/card/id/proc/set_access(list/new_access_list, mode = ERROR_ON_FAIL) + var/list/wildcard_access = list() + var/list/normal_access = list() + + build_access_lists(new_access_list, normal_access, wildcard_access) + + // Check if we can add the wildcards. + if(mode == ERROR_ON_FAIL) + if(!can_add_wildcards(wildcard_access)) + CRASH("Cannot add wildcards from \[[new_access_list.Join(",")]\] to [src]") + + clear_access() + + access = normal_access.Copy() + + if(mode != TRY_ADD_ALL_NO_WILDCARD) + add_wildcards(wildcard_access, mode = mode) + + return TRUE + +/// Clears all accesses from the ID card - both wildcard and normal. +/obj/item/card/id/proc/clear_access() + // Go through the wildcards and reset them. + for(var/flag_name in wildcard_slots) + var/list/wildcard_info = wildcard_slots[flag_name] + var/list/wildcard_usage = wildcard_info["usage"] + wildcard_usage.Cut() + + // Hard reset access + access.Cut() + +/** + * Helper proc. Creates access lists for the access procs. + * + * Takes the accesses list and compares it with the trim. Any basic accesses that match the trim are + * added to basic_access_list and the rest are added to wildcard_access_list. + + * This proc directly modifies the lists passed in as args. It expects these lists to be instantiated. + * There is no return value. + * Arguments: + */ +/obj/item/card/id/proc/build_access_lists(list/accesses, list/basic_access_list, list/wildcard_access_list) + if(!length(accesses) || isnull(basic_access_list) || isnull(wildcard_access_list)) + CRASH("Invalid parameters passed to build_access_lists") + + var/list/trim_accesses = trim?.access + + // Populate the lists. + for(var/new_access in accesses) + if(new_access in trim_accesses) + basic_access_list |= new_access + continue + + wildcard_access_list |= new_access + /obj/item/card/id/attack_self(mob/user) if(Adjacent(user)) var/minor @@ -114,8 +386,12 @@ . = ..() if(.) switch(var_name) - if(NAMEOF(src, assignment),NAMEOF(src, registered_name),NAMEOF(src, registered_age)) + if(NAMEOF(src, assignment), NAMEOF(src, registered_name), NAMEOF(src, registered_age)) update_label() + update_icon() + if(NAMEOF(src, trim)) + if(ispath(trim)) + SSid_access.apply_trim_to_card(src, trim) /obj/item/card/id/attackby(obj/item/W, mob/user, params) if(istype(W, /obj/item/holochip)) @@ -139,25 +415,40 @@ else return ..() -/obj/item/card/id/proc/insert_money(obj/item/I, mob/user, physical_currency) +/** + * Insert credits or coins into the ID card and add their value to the associated bank account. + * + * Arguments: + * money - The item to attempt to convert to credits and insert into the card. + * user - The user inserting the item. + * physical_currency - Boolean, whether this is a physical currency such as a coin and not a holochip. + */ +/obj/item/card/id/proc/insert_money(obj/item/money, mob/user, physical_currency) if(!registered_account) - to_chat(user, "[src] doesn't have a linked account to deposit [I] into!") + to_chat(user, "[src] doesn't have a linked account to deposit [money] into!") return - var/cash_money = I.get_item_credit_value() + var/cash_money = money.get_item_credit_value() if(!cash_money) - to_chat(user, "[I] doesn't seem to be worth anything!") + to_chat(user, "[money] doesn't seem to be worth anything!") return registered_account.adjust_money(cash_money) SSblackbox.record_feedback("amount", "credits_inserted", cash_money) log_econ("[cash_money] credits were inserted into [src] owned by [src.registered_name]") if(physical_currency) - to_chat(user, "You stuff [I] into [src]. It disappears in a small puff of bluespace smoke, adding [cash_money] credits to the linked account.") + to_chat(user, "You stuff [money] into [src]. It disappears in a small puff of bluespace smoke, adding [cash_money] credits to the linked account.") else - to_chat(user, "You insert [I] into [src], adding [cash_money] credits to the linked account.") + to_chat(user, "You insert [money] into [src], adding [cash_money] credits to the linked account.") to_chat(user, "The linked account now reports a balance of [registered_account.account_balance] cr.") - qdel(I) + qdel(money) +/** + * Insert multiple money or money-equivalent items at once. + * + * Arguments: + * money - List of items to attempt to convert to credits and insert into the card. + * user - The user inserting the items. + */ /obj/item/card/id/proc/mass_insert_money(list/money, mob/user) if(!registered_account) to_chat(user, "[src] doesn't have a linked account to deposit into!") @@ -179,6 +470,7 @@ return total +/// Helper proc. Can the user alt-click the ID? /obj/item/card/id/proc/alt_click_can_use_id(mob/living/user) if(!isliving(user)) return @@ -187,7 +479,7 @@ return TRUE -// Returns true if new account was set. +/// Attempts to set a new bank account on the ID card. /obj/item/card/id/proc/set_new_account(mob/living/user) . = FALSE var/datum/bank_account/old_account = registered_account @@ -291,17 +583,7 @@ /obj/item/card/id/RemoveID() return src -/obj/item/card/id/update_overlays() - . = ..() - if(!uses_overlays) - return - cached_flat_icon = null - var/job = assignment ? ckey(GetJobName()) : null - if(registered_name && registered_name != "Captain") - . += mutable_appearance(icon, "assigned") - if(job) - . += mutable_appearance(icon, "id[job]") - +/// Called on COMSIG_ATOM_UPDATED_ICON. Updates the visuals of the wallet this card is in. /obj/item/card/id/proc/update_in_wallet() SIGNAL_HANDLER @@ -311,384 +593,26 @@ powergaming.update_label() powergaming.update_appearance() -/obj/item/card/id/proc/get_cached_flat_icon() - if(!cached_flat_icon) - cached_flat_icon = getFlatIcon(src) - return cached_flat_icon - - -/obj/item/card/id/get_examine_string(mob/user, thats = FALSE) - if(uses_overlays) - return "[icon2html(get_cached_flat_icon(), user)] [thats? "That's ":""][get_examine_name(user)]" //displays all overlays in chat - return ..() - -/* -Usage: -update_label() - Sets the id name to whatever registered_name and assignment is -*/ - +/// Updates the name based on the card's vars and state. /obj/item/card/id/proc/update_label() var/blank = !registered_name - name = "[blank ? id_type_name : "[registered_name]'s ID Card"][(!assignment) ? "" : " ([assignment])"]" - update_appearance() - -/obj/item/card/id/silver - name = "silver identification card" - id_type_name = "silver identification card" - desc = "A silver card which shows honour and dedication." - icon_state = "silver" - inhand_icon_state = "silver_id" - lefthand_file = 'icons/mob/inhands/equipment/idcards_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi' - -/obj/item/card/id/silver/reaper - name = "Thirteen's ID Card (Reaper)" - access = list(ACCESS_MAINT_TUNNELS) - assignment = "Reaper" - registered_name = "Thirteen" - -/obj/item/card/id/gold - name = "gold identification card" - id_type_name = "gold identification card" - desc = "A golden card which shows power and might." - icon_state = "gold" - inhand_icon_state = "gold_id" - lefthand_file = 'icons/mob/inhands/equipment/idcards_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi' - -/obj/item/card/id/syndicate - name = "agent card" - access = list(ACCESS_MAINT_TUNNELS, ACCESS_SYNDICATE) - sticky_access = list(ACCESS_SYNDICATE) - ///Can anyone forge the ID or just syndicate? - var/anyone = FALSE - ///have we set a custom name and job assignment, or will we use what we're given when we chameleon change? - var/forged = FALSE - -/obj/item/card/id/syndicate/Initialize() - . = ..() - var/datum/action/item_action/chameleon/change/id/chameleon_action = new(src) - chameleon_action.chameleon_type = /obj/item/card/id - chameleon_action.chameleon_name = "ID Card" - chameleon_action.initialize_disguises() - -/obj/item/card/id/syndicate/afterattack(obj/item/O, mob/user, proximity) - if(!proximity) - return - if(istype(O, /obj/item/card/id)) - var/obj/item/card/id/I = O - src.access |= I.access - if(isliving(user) && user.mind) - if(user.mind.special_role || anyone) - to_chat(usr, "The card's microscanners activate as you pass it over the ID, copying its access.") - -/obj/item/card/id/syndicate/attack_self(mob/user) - if(isliving(user) && user.mind) - var/first_use = registered_name ? FALSE : TRUE - if(!(user.mind.special_role || anyone)) //Unless anyone is allowed, only syndies can use the card, to stop metagaming. - if(first_use) //If a non-syndie is the first to forge an unassigned agent ID, then anyone can forge it. - anyone = TRUE - else - return ..() - - var/popup_input = alert(user, "Choose Action", "Agent ID", "Show", "Forge/Reset", "Change Account ID") - if(user.incapacitated()) - return - if(popup_input == "Forge/Reset" && !forged) - var/input_name = stripped_input(user, "What name would you like to put on this card? Leave blank to randomise.", "Agent card name", registered_name ? registered_name : (ishuman(user) ? user.real_name : user.name), MAX_NAME_LEN) - input_name = sanitize_name(input_name) - if(!input_name) - // Invalid/blank names give a randomly generated one. - if(user.gender == MALE) - input_name = "[pick(GLOB.first_names_male)] [pick(GLOB.last_names)]" - else if(user.gender == FEMALE) - input_name = "[pick(GLOB.first_names_female)] [pick(GLOB.last_names)]" - else - input_name = "[pick(GLOB.first_names)] [pick(GLOB.last_names)]" - - var/target_occupation = stripped_input(user, "What occupation would you like to put on this card?\nNote: This will not grant any access levels other than Maintenance.", "Agent card job assignment", assignment ? assignment : "Assistant", MAX_MESSAGE_LEN) - if(!target_occupation) - return - - var/newAge = input(user, "Choose the ID's age:\n([AGE_MIN]-[AGE_MAX])", "Agent card age") as num|null - if(newAge) - registered_age = max(round(text2num(newAge)), 0) - - registered_name = input_name - assignment = target_occupation - update_label() - forged = TRUE - to_chat(user, "You successfully forge the ID card.") - log_game("[key_name(user)] has forged \the [initial(name)] with name \"[registered_name]\" and occupation \"[assignment]\".") - - // First time use automatically sets the account id to the user. - if (first_use && !registered_account) - if(ishuman(user)) - var/mob/living/carbon/human/accountowner = user - - var/datum/bank_account/account = SSeconomy.bank_accounts_by_id["[accountowner.account_id]"] - if(account) - account.bank_cards += src - registered_account = account - to_chat(user, "Your account number has been automatically assigned.") - return - else if (popup_input == "Forge/Reset" && forged) - registered_name = initial(registered_name) - assignment = initial(assignment) - log_game("[key_name(user)] has reset \the [initial(name)] named \"[src]\" to default.") - update_label() - forged = FALSE - to_chat(user, "You successfully reset the ID card.") - return - else if (popup_input == "Change Account ID") - set_new_account(user) - return - return ..() - -/obj/item/card/id/syndicate/anyone - anyone = TRUE - -/obj/item/card/id/syndicate/nuke_leader - name = "lead agent card" - access = list(ACCESS_MAINT_TUNNELS, ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER) - sticky_access = list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER) - -/obj/item/card/id/syndicate_command - name = "syndicate ID card" - id_type_name = "syndicate ID card" - desc = "An ID straight from the Syndicate." - registered_name = "Syndicate" - assignment = "Syndicate Overlord" - icon_state = "syndie" - access = list(ACCESS_SYNDICATE) - sticky_access = list(ACCESS_SYNDICATE) - uses_overlays = FALSE - registered_age = null - -/obj/item/card/id/syndicate_command/crew_id - name = "syndicate ID card" - id_type_name = "syndicate ID card" - desc = "An ID straight from the Syndicate." - registered_name = "Syndicate" - assignment = "Syndicate Operative" - icon_state = "syndie" - access = list(ACCESS_SYNDICATE, ACCESS_ROBOTICS) - sticky_access = list(ACCESS_SYNDICATE) - uses_overlays = FALSE - -/obj/item/card/id/syndicate_command/captain_id - name = "syndicate captain ID card" - id_type_name = "syndicate captain ID card" - desc = "An ID straight from the Syndicate." - registered_name = "Syndicate" - assignment = "Syndicate Ship Captain" - icon_state = "syndie" - access = list(ACCESS_SYNDICATE, ACCESS_ROBOTICS) - sticky_access = list(ACCESS_SYNDICATE) - uses_overlays = FALSE - -/obj/item/card/id/captains_spare - name = "captain's spare ID" - id_type_name = "captain's spare ID" - desc = "The spare ID of the High Lord himself." - icon_state = "gold" - inhand_icon_state = "gold_id" - lefthand_file = 'icons/mob/inhands/equipment/idcards_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi' - registered_name = "Captain" - assignment = "Captain" - registered_age = null - -/obj/item/card/id/captains_spare/Initialize() - var/datum/job/captain/J = new/datum/job/captain - access = J.get_access() - . = ..() - update_label() - -/obj/item/card/id/captains_spare/update_label() //so it doesn't change to Captain's ID card (Captain) on a sneeze - if(registered_name == "Captain") - name = "[id_type_name][(!assignment || assignment == "Captain") ? "" : " ([assignment])"]" - update_appearance() - else - ..() - -/obj/item/card/id/centcom - name = "\improper CentCom ID" - id_type_name = "\improper CentCom ID" - desc = "An ID straight from Central Command." - icon_state = "centcom" - registered_name = "Central Command" - assignment = "Central Command" - uses_overlays = FALSE - registered_age = null - -/obj/item/card/id/centcom/Initialize() - access = get_all_centcom_access() - . = ..() - -/obj/item/card/id/ert - name = "\improper CentCom ID" - id_type_name = "\improper CentCom ID" - desc = "An ERT ID card." - icon_state = "ert_commander" - registered_name = "Emergency Response Team Commander" - assignment = "Emergency Response Team Commander" - uses_overlays = FALSE - registered_age = null - -/obj/item/card/id/ert/Initialize() - access = get_all_accesses()+get_ert_access("commander")-ACCESS_CHANGE_IDS - . = ..() - -/obj/item/card/id/ert/security - registered_name = "Security Response Officer" - assignment = "Security Response Officer" - icon_state = "ert_security" - -/obj/item/card/id/ert/security/Initialize() - access = get_all_accesses()+get_ert_access("sec")-ACCESS_CHANGE_IDS - . = ..() - -/obj/item/card/id/ert/engineer - registered_name = "Engineering Response Officer" - assignment = "Engineering Response Officer" - icon_state = "ert_engineer" - -/obj/item/card/id/ert/engineer/Initialize() - access = get_all_accesses()+get_ert_access("eng")-ACCESS_CHANGE_IDS - . = ..() - -/obj/item/card/id/ert/medical - registered_name = "Medical Response Officer" - assignment = "Medical Response Officer" - icon_state = "ert_medic" - -/obj/item/card/id/ert/medical/Initialize() - access = get_all_accesses()+get_ert_access("med")-ACCESS_CHANGE_IDS - . = ..() - -/obj/item/card/id/ert/chaplain - registered_name = "Religious Response Officer" - assignment = "Religious Response Officer" - icon_state = "ert_chaplain" - -/obj/item/card/id/ert/chaplain/Initialize() - access = get_all_accesses()+get_ert_access("sec")-ACCESS_CHANGE_IDS - . = ..() - -/obj/item/card/id/ert/janitor - registered_name = "Janitorial Response Officer" - assignment = "Janitorial Response Officer" - icon_state = "ert_janitor" - -/obj/item/card/id/ert/janitor/Initialize() - access = get_all_accesses() - . = ..() - -/obj/item/card/id/ert/clown - registered_name = "Entertainment Response Officer" - assignment = "Entertainment Response Officer" - icon_state = "ert_clown" - -/obj/item/card/id/ert/clown/Initialize() - access = get_all_accesses() - . = ..() - -/obj/item/card/id/ert/deathsquad - name = "\improper Death Squad ID" - id_type_name = "\improper Death Squad ID" - desc = "A Death Squad ID card." - icon_state = "deathsquad" //NO NO SIR DEATH SQUADS ARENT A PART OF NANOTRASEN AT ALL - registered_name = "Death Commando" - assignment = "Death Commando" - uses_overlays = FALSE - -/obj/item/card/id/debug - name = "\improper Debug ID" - desc = "A debug ID card. Has ALL the all access, you really shouldn't have this." - icon_state = "ert_janitor" - assignment = "Jannie" - uses_overlays = FALSE - -/obj/item/card/id/debug/Initialize() - access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access() - registered_account = SSeconomy.get_dep_account(ACCOUNT_CAR) - . = ..() - -/obj/item/card/id/prisoner - name = "prisoner ID card" - id_type_name = "prisoner ID card" - desc = "You are a number, you are not a free man." - icon_state = "orange" - inhand_icon_state = "orange-id" - lefthand_file = 'icons/mob/inhands/equipment/idcards_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi' - assignment = "Prisoner" - registered_name = "Scum" - uses_overlays = FALSE - var/goal = 0 //How far from freedom? - var/points = 0 - registered_age = null - -/obj/item/card/id/prisoner/attack_self(mob/user) - to_chat(usr, "You have accumulated [points] out of the [goal] points you need for freedom.") - -/obj/item/card/id/prisoner/one - name = "Prisoner #13-001" - registered_name = "Prisoner #13-001" - icon_state = "prisoner_001" - -/obj/item/card/id/prisoner/two - name = "Prisoner #13-002" - registered_name = "Prisoner #13-002" - icon_state = "prisoner_002" - -/obj/item/card/id/prisoner/three - name = "Prisoner #13-003" - registered_name = "Prisoner #13-003" - icon_state = "prisoner_003" - -/obj/item/card/id/prisoner/four - name = "Prisoner #13-004" - registered_name = "Prisoner #13-004" - icon_state = "prisoner_004" - -/obj/item/card/id/prisoner/five - name = "Prisoner #13-005" - registered_name = "Prisoner #13-005" - icon_state = "prisoner_005" - -/obj/item/card/id/prisoner/six - name = "Prisoner #13-006" - registered_name = "Prisoner #13-006" - icon_state = "prisoner_006" - -/obj/item/card/id/prisoner/seven - name = "Prisoner #13-007" - registered_name = "Prisoner #13-007" - icon_state = "prisoner_007" - -/obj/item/card/id/mining - name = "mining ID" - access = list(ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MECH_MINING, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM) + name = "[blank ? initial(name) : "[registered_name]'s ID Card"][(!assignment) ? "" : " ([assignment])"]" /obj/item/card/id/away name = "\proper a perfectly generic identification card" desc = "A perfectly generic identification card. Looks like it could use some flavor." - access = list(ACCESS_AWAY_GENERAL) + trim = /datum/id_trim/away icon_state = "retro" - uses_overlays = FALSE registered_age = null /obj/item/card/id/away/hotel name = "Staff ID" desc = "A staff ID used to access the hotel's doors." - access = list(ACCESS_AWAY_GENERAL, ACCESS_AWAY_MAINT) + trim = /datum/id_trim/away/hotel /obj/item/card/id/away/hotel/securty name = "Officer ID" - access = list(ACCESS_AWAY_GENERAL, ACCESS_AWAY_MAINT, ACCESS_AWAY_SEC) + trim = /datum/id_trim/away/hotel/security /obj/item/card/id/away/old name = "\proper a perfectly generic identification card" @@ -697,34 +621,30 @@ update_label() /obj/item/card/id/away/old/sec name = "Charlie Station Security Officer's ID card" desc = "A faded Charlie Station ID card. You can make out the rank \"Security Officer\"." - assignment = "Charlie Station Security Officer" - access = list(ACCESS_AWAY_GENERAL, ACCESS_AWAY_SEC) + trim = /datum/id_trim/away/old/sec /obj/item/card/id/away/old/sci name = "Charlie Station Scientist's ID card" desc = "A faded Charlie Station ID card. You can make out the rank \"Scientist\"." - assignment = "Charlie Station Scientist" - access = list(ACCESS_AWAY_GENERAL) + trim = /datum/id_trim/away/old/sci /obj/item/card/id/away/old/eng name = "Charlie Station Engineer's ID card" desc = "A faded Charlie Station ID card. You can make out the rank \"Station Engineer\"." - assignment = "Charlie Station Engineer" - access = list(ACCESS_AWAY_GENERAL, ACCESS_AWAY_ENGINE) + trim = /datum/id_trim/away/old/eng /obj/item/card/id/away/old/apc name = "APC Access ID" desc = "A special ID card that allows access to APC terminals." - access = list(ACCESS_ENGINE_EQUIP) + trim = /datum/id_trim/away/old/apc /obj/item/card/id/away/deep_storage //deepstorage.dmm space ruin name = "bunker access ID" /obj/item/card/id/departmental_budget - name = "departmental card (FUCK)" + name = "departmental card (ERROR)" desc = "Provides access to the departmental budget." icon_state = "budgetcard" - uses_overlays = FALSE var/department_ID = ACCOUNT_CIV var/department_name = ACCOUNT_CIV_NAME registered_age = null @@ -754,3 +674,469 @@ update_label() /obj/item/card/id/departmental_budget/AltClick(mob/living/user) registered_account.bank_card_talk("Withdrawing is not compatible with this card design.", TRUE) //prevents the vault bank machine being useless and putting money from the budget to your card to go over personal crates + +/obj/item/card/id/advanced + name = "identification card" + desc = "A card used to provide ID and determine access across the station. Has an integrated digital display and advanced microchips." + icon_state = "card_grey" + worn_icon_state = "card_grey" + + wildcard_slots = WILDCARD_LIMIT_GREY + + /// An overlay icon state for when the card is assigned to a name. Usually manifests itself as a little scribble to the right of the job icon. + var/assigned_icon_state = "assigned" + /// Cached icon that has been built for this card. + var/icon/cached_flat_icon + + /// If this is set, will manually override the icon file for the trim. Intended for admins to VV edit and chameleon ID cards. + var/trim_icon_override + /// If this is set, will manually override the icon state for the trim. Intended for admins to VV edit and chameleon ID cards. + var/trim_state_override + +/obj/item/card/id/advanced/get_icon_source() + return get_cached_flat_icon() + +/// If no cached_flat_icon exists, this proc creates it. This proc then returns the cached_flat_icon. +/obj/item/card/id/advanced/proc/get_cached_flat_icon() + if(!cached_flat_icon) + cached_flat_icon = getFlatIcon(src) + return cached_flat_icon + +/obj/item/card/id/advanced/get_examine_string(mob/user, thats = FALSE) + return "[icon2html(get_cached_flat_icon(), user)] [thats? "That's ":""][get_examine_name(user)]" //displays all overlays in chat + +/obj/item/card/id/advanced/update_overlays() + . = ..() + + cached_flat_icon = null + + if(registered_name && registered_name != "Captain") + . += mutable_appearance(icon, assigned_icon_state) + + var/trim_icon_file = trim_icon_override ? trim_icon_override : trim?.trim_icon + var/trim_icon_state = trim_state_override ? trim_state_override : trim?.trim_state + + if(!trim_icon_file || !trim_icon_state) + return + + . += mutable_appearance(trim_icon_file, trim_icon_state) + +/obj/item/card/id/advanced/silver + name = "silver identification card" + desc = "A silver card which shows honour and dedication." + icon_state = "card_silver" + worn_icon_state = "card_silver" + inhand_icon_state = "silver_id" + wildcard_slots = WILDCARD_LIMIT_SILVER + +/datum/id_trim/maint_reaper + access = list(ACCESS_MAINT_TUNNELS) + trim_state = "trim_janitor" + assignment = "Reaper" + +/obj/item/card/id/advanced/silver/reaper + name = "Thirteen's ID Card (Reaper)" + trim = /datum/id_trim/maint_reaper + registered_name = "Thirteen" + +/obj/item/card/id/advanced/gold + name = "gold identification card" + desc = "A golden card which shows power and might." + icon_state = "card_gold" + worn_icon_state = "card_gold" + inhand_icon_state = "gold_id" + wildcard_slots = WILDCARD_LIMIT_GOLD + +/obj/item/card/id/advanced/gold/captains_spare + name = "captain's spare ID" + desc = "The spare ID of the High Lord himself." + registered_name = "Captain" + trim = /datum/id_trim/job/captain + registered_age = null + +/obj/item/card/id/advanced/gold/captains_spare/update_label() //so it doesn't change to Captain's ID card (Captain) on a sneeze + if(registered_name == "Captain") + name = "[initial(name)][(!assignment || assignment == "Captain") ? "" : " ([assignment])"]" + update_appearance(UPDATE_ICON) + else + ..() + +/obj/item/card/id/advanced/centcom + name = "\improper CentCom ID" + desc = "An ID straight from Central Command." + icon_state = "card_centcom" + worn_icon_state = "card_centcom" + assigned_icon_state = "assigned_centcom" + registered_name = "Central Command" + registered_age = null + trim = /datum/id_trim/centcom + wildcard_slots = WILDCARD_LIMIT_CENTCOM + +/obj/item/card/id/advanced/centcom/ert + name = "\improper CentCom ID" + desc = "An ERT ID card." + registered_age = null + registered_name = "Emergency Response Intern" + trim = /datum/id_trim/centcom/ert + +/obj/item/card/id/advanced/centcom/ert + registered_name = "Emergency Response Team Commander" + trim = /datum/id_trim/centcom/ert/commander + +/obj/item/card/id/advanced/centcom/ert/security + registered_name = "Security Response Officer" + trim = /datum/id_trim/centcom/ert/security + +/obj/item/card/id/advanced/centcom/ert/engineer + registered_name = "Engineering Response Officer" + trim = /datum/id_trim/centcom/ert/engineer + +/obj/item/card/id/advanced/centcom/ert/medical + registered_name = "Medical Response Officer" + trim = /datum/id_trim/centcom/ert/medical + +/obj/item/card/id/advanced/centcom/ert/chaplain + registered_name = "Religious Response Officer" + trim = /datum/id_trim/centcom/ert/chaplain + +/obj/item/card/id/advanced/centcom/ert/janitor + registered_name = "Janitorial Response Officer" + trim = /datum/id_trim/centcom/ert/janitor + +/obj/item/card/id/advanced/centcom/ert/clown + registered_name = "Entertainment Response Officer" + trim = /datum/id_trim/centcom/ert/clown + +/obj/item/card/id/advanced/black + name = "black identification card" + desc = "This card is telling you one thing and one thing alone. The person holding this card is an utter badass." + icon_state = "card_black" + worn_icon_state = "card_black" + assigned_icon_state = "assigned_syndicate" + wildcard_slots = WILDCARD_LIMIT_GOLD + +/obj/item/card/id/advanced/black/deathsquad + name = "\improper Death Squad ID" + desc = "A Death Squad ID card." + registered_name = "Death Commando" + trim = /datum/id_trim/centcom/deathsquad + wildcard_slots = WILDCARD_LIMIT_DEATHSQUAD + +/obj/item/card/id/advanced/black/syndicate_command + name = "syndicate ID card" + desc = "An ID straight from the Syndicate." + registered_name = "Syndicate" + registered_age = null + trim = /datum/id_trim/syndicom + wildcard_slots = WILDCARD_LIMIT_SYNDICATE + +/obj/item/card/id/advanced/black/syndicate_command/crew_id + name = "syndicate ID card" + desc = "An ID straight from the Syndicate." + registered_name = "Syndicate" + trim = /datum/id_trim/syndicom/crew + +/obj/item/card/id/advanced/black/syndicate_command/captain_id + name = "syndicate captain ID card" + desc = "An ID straight from the Syndicate." + registered_name = "Syndicate" + trim = /datum/id_trim/syndicom/captain + +/obj/item/card/id/advanced/debug + name = "\improper Debug ID" + desc = "A debug ID card. Has ALL the all access, you really shouldn't have this." + icon_state = "card_centcom" + worn_icon_state = "card_centcom" + assigned_icon_state = "assigned_centcom" + trim = /datum/id_trim/admin + wildcard_slots = WILDCARD_LIMIT_ADMIN + +/obj/item/card/id/advanced/debug/Initialize() + . = ..() + registered_account = SSeconomy.get_dep_account(ACCOUNT_CAR) + +/obj/item/card/id/advanced/prisoner + name = "prisoner ID card" + desc = "You are a number, you are not a free man." + icon_state = "card_prisoner" + worn_icon_state = "card_prisoner" + inhand_icon_state = "orange-id" + lefthand_file = 'icons/mob/inhands/equipment/idcards_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi' + registered_name = "Scum" + registered_age = null + trim = /datum/id_trim/job/prisoner + + wildcard_slots = WILDCARD_LIMIT_PRISONER + + /// Number of gulag points required to earn freedom. + var/goal = 0 + /// Number of gulag points earned. + var/points = 0 + +/obj/item/card/id/advanced/prisoner/attack_self(mob/user) + to_chat(usr, "You have accumulated [points] out of the [goal] points you need for freedom.") + +/obj/item/card/id/advanced/prisoner/one + name = "Prisoner #13-001" + registered_name = "Prisoner #13-001" + trim = /datum/id_trim/job/prisoner/one + +/obj/item/card/id/advanced/prisoner/two + name = "Prisoner #13-002" + registered_name = "Prisoner #13-002" + trim = /datum/id_trim/job/prisoner/two + +/obj/item/card/id/advanced/prisoner/three + name = "Prisoner #13-003" + registered_name = "Prisoner #13-003" + trim = /datum/id_trim/job/prisoner/three + +/obj/item/card/id/advanced/prisoner/four + name = "Prisoner #13-004" + registered_name = "Prisoner #13-004" + trim = /datum/id_trim/job/prisoner/four + +/obj/item/card/id/advanced/prisoner/five + name = "Prisoner #13-005" + registered_name = "Prisoner #13-005" + trim = /datum/id_trim/job/prisoner/five + +/obj/item/card/id/advanced/prisoner/six + name = "Prisoner #13-006" + registered_name = "Prisoner #13-006" + trim = /datum/id_trim/job/prisoner/six + +/obj/item/card/id/advanced/prisoner/seven + name = "Prisoner #13-007" + registered_name = "Prisoner #13-007" + trim = /datum/id_trim/job/prisoner/seven + +/obj/item/card/id/advanced/mining + name = "mining ID" + trim = /datum/id_trim/job/shaft_miner/spare + +/obj/item/card/id/advanced/highlander + name = "highlander ID" + registered_name = "Highlander" + desc = "There can be only one!" + icon_state = "card_black" + worn_icon_state = "card_black" + assigned_icon_state = "assigned_syndicate" + trim = /datum/id_trim/highlander + wildcard_slots = WILDCARD_LIMIT_ADMIN + +/obj/item/card/id/advanced/chameleon + name = "agent card" + desc = "A highly advanced chameleon ID card. Touch this card on another ID card to choose which accesses to copy." + wildcard_slots = WILDCARD_LIMIT_CHAMELEON + + /// Have we set a custom name and job assignment, or will we use what we're given when we chameleon change? + var/forged = FALSE + /// Anti-metagaming protections. If TRUE, anyone can change the ID card's details. If FALSE, only syndicate agents can. + var/anyone = FALSE + /// Weak ref to the ID card we're currently attempting to steal access from. + var/datum/weakref/theft_target + +/obj/item/card/id/advanced/chameleon/Initialize() + . = ..() + var/datum/action/item_action/chameleon/change/id/chameleon_card_action = new(src) + chameleon_card_action.chameleon_type = /obj/item/card/id/advanced + chameleon_card_action.chameleon_name = "ID Card" + chameleon_card_action.initialize_disguises() + +/obj/item/card/id/advanced/chameleon/Destroy() + theft_target = null + . = ..() + +/obj/item/card/id/advanced/chameleon/afterattack(obj/item/O, mob/user, proximity) + if(!proximity) + return + if(istype(O, /obj/item/card/id)) + theft_target = WEAKREF(O) + ui_interact(user) + return + + return ..() + +/obj/item/card/id/advanced/chameleon/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ChameleonCard", name) + ui.open() + +/obj/item/card/id/advanced/chameleon/ui_static_data(mob/user) + var/list/data = list() + data["wildcardFlags"] = SSid_access.wildcard_flags_by_wildcard + data["accessFlagNames"] = SSid_access.access_flag_string_by_flag + data["accessFlags"] = SSid_access.flags_by_access + return data + +/obj/item/card/id/advanced/chameleon/ui_data(mob/user) + var/list/data = list() + + data["showBasic"] = FALSE + + var/list/regions = list() + + var/obj/item/card/id/target_card = theft_target.resolve() + if(target_card) + var/list/tgui_region_data = SSid_access.all_region_access_tgui + for(var/region in SSid_access.station_regions) + regions += tgui_region_data[region] + + data["accesses"] = regions + data["ourAccess"] = access + data["ourTrimAccess"] = trim ? trim.access : list() + data["theftAccess"] = target_card.access.Copy() + data["wildcardSlots"] = wildcard_slots + data["selectedList"] = access + data["trimAccess"] = list() + + return data + +/obj/item/card/id/advanced/chameleon/ui_act(action, list/params) + . = ..() + if(.) + return + + var/obj/item/card/id/target_card = theft_target?.resolve() + if(QDELETED(target_card)) + to_chat(usr, "The ID card you were attempting to scan is no longer in range.") + target_card = null + return TRUE + + // Wireless ID theft! + var/turf/our_turf = get_turf(src) + var/turf/target_turf = get_turf(target_card) + if(!our_turf.Adjacent(target_turf)) + to_chat(usr, "The ID card you were attempting to scan is no longer in range.") + target_card = null + return TRUE + + switch(action) + if("mod_access") + var/access_type = params["access_target"] + var/try_wildcard = params["access_wildcard"] + if(access_type in access) + remove_access(list(access_type)) + LOG_ID_ACCESS_CHANGE(usr, src, "removed [SSid_access.get_access_desc(access_type)]") + return TRUE + + if(!(access_type in target_card.access)) + to_chat(usr, "ID error: ID card rejected your attempted access modification.") + LOG_ID_ACCESS_CHANGE(usr, src, "failed to add [SSid_access.get_access_desc(access_type)][try_wildcard ? " with wildcard [try_wildcard]" : ""]") + return TRUE + + if(!can_add_wildcards(list(access_type), try_wildcard)) + to_chat(usr, "ID error: ID card rejected your attempted access modification.") + LOG_ID_ACCESS_CHANGE(usr, src, "failed to add [SSid_access.get_access_desc(access_type)][try_wildcard ? " with wildcard [try_wildcard]" : ""]") + return TRUE + + if(!add_access(list(access_type), try_wildcard)) + to_chat(usr, "ID error: ID card rejected your attempted access modification.") + LOG_ID_ACCESS_CHANGE(usr, src, "failed to add [SSid_access.get_access_desc(access_type)][try_wildcard ? " with wildcard [try_wildcard]" : ""]") + return TRUE + + if(access_type in ACCESS_ALERT_ADMINS) + message_admins("[ADMIN_LOOKUPFLW(usr)] just added [SSid_access.get_access_desc(access_type)] to an ID card [ADMIN_VV(src)] [(registered_name) ? "belonging to [registered_name]." : "with no registered name."]") + LOG_ID_ACCESS_CHANGE(usr, src, "added [SSid_access.get_access_desc(access_type)]") + return TRUE + +/obj/item/card/id/advanced/chameleon/attack_self(mob/user) + if(isliving(user) && user.mind) + var/popup_input = alert(user, "Choose Action", "Agent ID", "Show", "Forge/Reset", "Change Account ID") + if(user.incapacitated()) + return + if(!user.is_holding(src)) + return + if(popup_input == "Forge/Reset") + if(!forged) + var/input_name = stripped_input(user, "What name would you like to put on this card? Leave blank to randomise.", "Agent card name", registered_name ? registered_name : (ishuman(user) ? user.real_name : user.name), MAX_NAME_LEN) + input_name = sanitize_name(input_name) + if(!input_name) + // Invalid/blank names give a randomly generated one. + if(user.gender == MALE) + input_name = "[pick(GLOB.first_names_male)] [pick(GLOB.last_names)]" + else if(user.gender == FEMALE) + input_name = "[pick(GLOB.first_names_female)] [pick(GLOB.last_names)]" + else + input_name = "[pick(GLOB.first_names)] [pick(GLOB.last_names)]" + + registered_name = input_name + + var/change_trim = alert(user, "Adjust the appearance of your card's trim?", "Modify Trim", "Yes", "No") + if(change_trim == "Yes") + var/list/blacklist = typecacheof(type) + typecacheof(/obj/item/card/id/advanced/simple_bot) + var/list/trim_list = list() + for(var/trim_path in typesof(/datum/id_trim)) + if(blacklist[trim_path]) + continue + + var/datum/id_trim/trim = SSid_access.trim_singletons_by_path[trim_path] + + if(trim && trim.trim_state && trim.assignment) + var/fake_trim_name = "[trim.assignment] ([trim.trim_state])" + trim_list[fake_trim_name] = trim_path + + var/selected_trim_path + selected_trim_path = input("Select trim to apply to your card.\nNote: This will not grant any trim accesses.", "Forge Trim", selected_trim_path) as null|anything in sortList(trim_list, /proc/cmp_typepaths_asc) + if(selected_trim_path) + SSid_access.apply_trim_to_chameleon_card(src, trim_list[selected_trim_path]) + + var/target_occupation = stripped_input(user, "What occupation would you like to put on this card?\nNote: This will not grant any access levels.", "Agent card job assignment", assignment ? assignment : "Assistant", MAX_MESSAGE_LEN) + if(target_occupation) + assignment = target_occupation + + var/new_age = input(user, "Choose the ID's age:\n([AGE_MIN]-[AGE_MAX])", "Agent card age") as num|null + if(new_age) + registered_age = max(round(text2num(new_age)), 0) + + update_label() + update_icon() + forged = TRUE + to_chat(user, "You successfully forge the ID card.") + log_game("[key_name(user)] has forged \the [initial(name)] with name \"[registered_name]\", occupation \"[assignment]\" and trim \"[trim?.assignment]\".") + + if(!registered_account) + if(ishuman(user)) + var/mob/living/carbon/human/accountowner = user + + var/datum/bank_account/account = SSeconomy.bank_accounts_by_id["[accountowner.account_id]"] + if(account) + account.bank_cards += src + registered_account = account + to_chat(user, "Your account number has been automatically assigned.") + return + if(forged) + registered_name = initial(registered_name) + assignment = initial(assignment) + SSid_access.remove_trim_from_card(src) + log_game("[key_name(user)] has reset \the [initial(name)] named \"[src]\" to default.") + update_label() + update_icon() + forged = FALSE + to_chat(user, "You successfully reset the ID card.") + return + if (popup_input == "Change Account ID") + set_new_account(user) + return + return ..() + +/// A special variant of the classic chameleon ID card which accepts all access. +/obj/item/card/id/advanced/chameleon/black + icon_state = "card_black" + worn_icon_state = "card_black" + assigned_icon_state = "assigned_syndicate" + wildcard_slots = WILDCARD_LIMIT_GOLD + +/obj/item/card/id/advanced/engioutpost + registered_name = "George 'Plastic' Miller" + desc = "A card used to provide ID and determine access across the station. There's blood dripping from the corner. Ew." + trim = /datum/id_trim/engioutpost + registered_age = 47 + +/obj/item/card/id/advanced/simple_bot + name = "simple bot ID card" + desc = "An internal ID card used by the station's non-sentient bots. You should report this to a coder if you're holding it." + wildcard_slots = WILDCARD_LIMIT_ADMIN diff --git a/code/game/objects/items/control_wand.dm b/code/game/objects/items/control_wand.dm index 88e7f40ef78..17b1f7f2b94 100644 --- a/code/game/objects/items/control_wand.dm +++ b/code/game/objects/items/control_wand.dm @@ -12,13 +12,13 @@ desc = "Remotely controls airlocks." w_class = WEIGHT_CLASS_TINY var/mode = WAND_OPEN - var/region_access = 1 //See access.dm + var/region_access = REGION_GENERAL var/list/access_list network_id = NETWORK_DOOR_REMOTES /obj/item/door_remote/Initialize() . = ..() - access_list = get_region_accesses(region_access) + access_list = SSid_access.get_region_access_list(list(region_access)) RegisterSignal(src, COMSIG_COMPONENT_NTNET_NAK, .proc/bad_signal) RegisterSignal(src, COMSIG_COMPONENT_NTNET_ACK, .proc/good_signal) @@ -65,48 +65,47 @@ ntnet_send(data) - /obj/item/door_remote/omni name = "omni door remote" desc = "This control wand can access any door on the station." icon_state = "gangtool-yellow" - region_access = 0 + region_access = REGION_ALL_STATION /obj/item/door_remote/captain name = "command door remote" icon_state = "gangtool-yellow" - region_access = 7 + region_access = REGION_COMMAND /obj/item/door_remote/chief_engineer name = "engineering door remote" icon_state = "gangtool-orange" - region_access = 5 + region_access = REGION_ENGINEERING /obj/item/door_remote/research_director name = "research door remote" icon_state = "gangtool-purple" - region_access = 4 + region_access = REGION_RESEARCH /obj/item/door_remote/head_of_security name = "security door remote" icon_state = "gangtool-red" - region_access = 2 + region_access = REGION_SECURITY /obj/item/door_remote/quartermaster name = "supply door remote" desc = "Remotely controls airlocks. This remote has additional Vault access." icon_state = "gangtool-green" - region_access = 6 + region_access = REGION_SUPPLY /obj/item/door_remote/chief_medical_officer name = "medical door remote" icon_state = "gangtool-blue" - region_access = 3 + region_access = REGION_MEDBAY /obj/item/door_remote/civilian name = "civilian door remote" icon_state = "gangtool-white" - region_access = 1 + region_access = REGION_GENERAL #undef WAND_OPEN #undef WAND_BOLT diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index 31f1641aed1..98815fcb56b 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -12,12 +12,12 @@ GLOBAL_LIST_EMPTY(PDAs) #define PDA_SPAM_DELAY 2 MINUTES /obj/item/pda - name = "\improper PDA" + name = "\improper standard PDA" desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge." icon = 'icons/obj/pda.dmi' icon_state = "pda" inhand_icon_state = "electronic" - worn_icon_state = "electronic" + worn_icon_state = "pda" lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' item_flags = NOBLUDGEON @@ -151,6 +151,12 @@ GLOBAL_LIST_EMPTY(PDAs) else return ..() +/obj/item/pda/get_id_examine_strings(mob/user) + . = ..() + if(id) + . += "\The [src] is displaying [id]." + . += id.get_id_examine_strings(user) + /obj/item/pda/GetID() return id @@ -488,8 +494,6 @@ GLOBAL_LIST_EMPTY(PDAs) id_check(U) if("UpdateInfo") ownjob = id.assignment - if(istype(id, /obj/item/card/id/syndicate)) - owner = id.registered_name update_label() if(!silent) playsound(src, 'sound/machines/terminal_processing.ogg', 15, TRUE) @@ -709,6 +713,8 @@ GLOBAL_LIST_EMPTY(PDAs) if(H.wear_id == src) H.sec_hud_set_ID() + update_slot_icon() + /obj/item/pda/proc/msg_input(mob/living/U = usr) var/t = stripped_input(U, "Please enter message", name) @@ -960,6 +966,8 @@ GLOBAL_LIST_EMPTY(PDAs) else old_id.forceMove(get_turf(src)) + update_slot_icon() + /obj/item/pda/pre_attack(obj/target, mob/living/user, params) if(!ismachinery(target)) diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm index c106e634d3a..23c9eb391db 100644 --- a/code/game/objects/items/storage/boxes.dm +++ b/code/game/objects/items/storage/boxes.dm @@ -538,7 +538,7 @@ /obj/item/storage/box/ids/PopulateContents() for(var/i in 1 to 7) - new /obj/item/card/id(src) + new /obj/item/card/id/advanced(src) //Some spare PDAs in a box /obj/item/storage/box/pdas @@ -565,7 +565,7 @@ /obj/item/storage/box/silver_ids/PopulateContents() for(var/i in 1 to 7) - new /obj/item/card/id/silver(src) + new /obj/item/card/id/advanced/silver(src) /obj/item/storage/box/prisoner name = "box of prisoner IDs" @@ -575,13 +575,13 @@ /obj/item/storage/box/prisoner/PopulateContents() ..() - new /obj/item/card/id/prisoner/one(src) - new /obj/item/card/id/prisoner/two(src) - new /obj/item/card/id/prisoner/three(src) - new /obj/item/card/id/prisoner/four(src) - new /obj/item/card/id/prisoner/five(src) - new /obj/item/card/id/prisoner/six(src) - new /obj/item/card/id/prisoner/seven(src) + new /obj/item/card/id/advanced/prisoner/one(src) + new /obj/item/card/id/advanced/prisoner/two(src) + new /obj/item/card/id/advanced/prisoner/three(src) + new /obj/item/card/id/advanced/prisoner/four(src) + new /obj/item/card/id/advanced/prisoner/five(src) + new /obj/item/card/id/advanced/prisoner/six(src) + new /obj/item/card/id/advanced/prisoner/seven(src) /obj/item/storage/box/seccarts name = "box of PDA security cartridges" diff --git a/code/game/objects/items/storage/secure.dm b/code/game/objects/items/storage/secure.dm index 13e08514ef5..6e817bf28f4 100644 --- a/code/game/objects/items/storage/secure.dm +++ b/code/game/objects/items/storage/secure.dm @@ -21,6 +21,7 @@ var/l_setshort = FALSE var/l_hacking = FALSE var/open = FALSE + var/can_hack_open = TRUE w_class = WEIGHT_CLASS_NORMAL desc = "This shouldn't exist. If it does, create an issue report." @@ -35,30 +36,31 @@ . += "The service panel is currently [open ? "unscrewed" : "screwed shut"]." /obj/item/storage/secure/attackby(obj/item/W, mob/user, params) - if(SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)) + if(can_hack_open && SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)) if (W.tool_behaviour == TOOL_SCREWDRIVER) if (W.use_tool(src, user, 20)) - open =! open + open = !open to_chat(user, "You [open ? "open" : "close"] the service panel.") return if (W.tool_behaviour == TOOL_WIRECUTTER) to_chat(user, "[src] is protected from this sort of tampering, yet it appears the internal memory wires can still be pulsed.") - if ((W.tool_behaviour == TOOL_MULTITOOL) && (!l_hacking)) + return + if (W.tool_behaviour == TOOL_MULTITOOL) + if(l_hacking) + to_chat(user, "This safe is already being hacked.") + return if(open == TRUE) to_chat(user, "Now attempting to reset internal memory, please hold.") l_hacking = TRUE if (W.use_tool(src, user, 400)) to_chat(user, "Internal memory reset - lock has been disengaged.") l_set = FALSE - l_hacking = FALSE - else - l_hacking = FALSE - else - to_chat(user, "You must unscrew the service panel before you can pulse the wiring!") + + l_hacking = FALSE + return + + to_chat(user, "You must unscrew the service panel before you can pulse the wiring!") return - //At this point you have exhausted all the special things to do when locked - // ... but it's still locked. - return // -> storage/attackby() what with handle insertion, etc return ..() @@ -187,3 +189,35 @@ /obj/item/storage/secure/safe/hos name = "head of security's safe" + +/** + * This safe is meant to be damn robust. To break in, you're supposed to get creative, or use acid or an explosion. + * + * This makes the safe still possible to break in for someone who is prepared and capable enough, either through + * chemistry, botany or whatever else. + * + * The safe is also weak to explosions, so spending some early TC could allow an antag to blow it upen if they can + * get access to it. + */ +/obj/item/storage/secure/safe/caps_spare + name = "captain's spare ID safe" + desc = "In case of emergency, do not break glass. All Captains and Acting Captains are provided with codes to access this safe. \ +It is made out of the same material as the station's Black Box and is designed to resist all conventional weaponry. \ +There appears to be a small amount of surface corrosion. It doesn't look like it could withstand much of an explosion." + can_hack_open = FALSE + armor = list(MELEE = 100, BULLET = 100, LASER = 100, ENERGY = 100, BOMB = 70, BIO = 100, RAD = 100, FIRE = 80, ACID = 70) + max_integrity = 300 + color = "#ffdd33" + +/obj/item/storage/secure/safe/caps_spare/Initialize() + . = ..() + + l_code = SSid_access.spare_id_safe_code + l_set = TRUE + SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, TRUE) + +/obj/item/storage/secure/safe/caps_spare/PopulateContents() + new /obj/item/card/id/advanced/gold/captains_spare(src) + +/obj/item/storage/secure/safe/caps_spare/rust_heretic_act() + take_damage(damage_amount = 100, damage_type = BRUTE, damage_flag = MELEE, armour_penetration = 100) diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index 003b3360aab..e097add0dcb 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -16,7 +16,6 @@ if("bloodyspai") new /obj/item/clothing/under/chameleon(src) // 2 tc since it's not the full set new /obj/item/clothing/mask/chameleon(src) // Goes with above - new /obj/item/card/id/syndicate(src) // 2 tc new /obj/item/clothing/shoes/chameleon/noslip(src) // 2 tc new /obj/item/camera_bug(src) // 1 tc new /obj/item/multitool/ai_detect(src) // 1 tc @@ -79,7 +78,6 @@ new /obj/item/storage/toolbox/syndicate(src) new /obj/item/camera_bug(src) new /obj/item/clothing/glasses/thermal/syndi(src) - new /obj/item/card/id/syndicate(src) if("lordsingulo") new /obj/item/sbeacondrop(src) @@ -87,7 +85,7 @@ new /obj/item/clothing/head/helmet/space/syndicate/black/red(src) new /obj/item/card/emag(src) new /obj/item/storage/toolbox/syndicate(src) - new /obj/item/card/mining_access_card(src) + new /obj/item/card/id/advanced/mining(src) new /obj/item/stack/spacecash/c10000(src) new /obj/item/toy/spinningtoy(src) //lol @@ -127,7 +125,6 @@ new /obj/item/ammo_box/magazine/m9mm(src) new /obj/item/ammo_box/magazine/m9mm(src) new /obj/item/clothing/under/chameleon(src) - new /obj/item/card/id/syndicate(src) new /obj/item/reagent_containers/hypospray/medipen/stimulants(src) new /obj/item/reagent_containers/glass/rag(src) @@ -137,14 +134,12 @@ for(var/i in 1 to 6) new /obj/item/throwing_star(src) // ~5 tc for all 6 new /obj/item/storage/belt/chameleon(src) // Unique but worth at least 2 tc - new /obj/item/card/id/syndicate(src) // 2 tc new /obj/item/chameleon(src) // 7 tc if("darklord") new /obj/item/dualsaber(src) new /obj/item/dnainjector/telemut/darkbundle(src) new /obj/item/clothing/suit/hooded/chaplain_hoodie(src) - new /obj/item/card/id/syndicate(src) new /obj/item/clothing/shoes/chameleon/noslip(src) //because slipping while being a dark lord sucks new /obj/item/book/granter/spell/summonitem(src) @@ -259,7 +254,6 @@ new /obj/item/clothing/suit/space/syndicate/contract(src) new /obj/item/clothing/under/chameleon(src) new /obj/item/clothing/mask/chameleon(src) - new /obj/item/card/id/syndicate(src) new /obj/item/storage/fancy/cigarettes/cigpack_syndicate(src) new /obj/item/lighter(src) diff --git a/code/game/objects/items/storage/wallets.dm b/code/game/objects/items/storage/wallets.dm index e48c79cf596..94f9c04c665 100644 --- a/code/game/objects/items/storage/wallets.dm +++ b/code/game/objects/items/storage/wallets.dm @@ -42,27 +42,52 @@ /obj/item/storage/wallet/Exited(atom/movable/AM) . = ..() - refreshID() + refreshID(removed = TRUE) -/obj/item/storage/wallet/proc/refreshID() +/** + * Calculates the new front ID. + * + * Picks the ID card that has the most combined command or higher tier accesses. + * Arguments: + * * removed - If this proc was called because a card was removed. There's a chance we don't need to calculate the new front ID if a card was removed. + */ +/obj/item/storage/wallet/proc/refreshID(removed = FALSE) LAZYCLEARLIST(combined_access) - if(!(front_id in src)) - front_id = null - for(var/obj/item/card/id/I in contents) - if(!front_id) - front_id = I + + // If the front_id is still in our wallet an we removed a card, we can return early. + if((front_id in src) && removed) + return + + front_id = null + var/winning_tally = 0 + for(var/card in contents) + var/obj/item/card/id/id_card = card + if(!istype(id_card)) + continue + var/card_tally = SSid_access.tally_access(id_card, ACCESS_FLAG_COMMAND) + if(card_tally > winning_tally) + winning_tally = card_tally + front_id = id_card LAZYINITLIST(combined_access) - combined_access |= I.access + combined_access |= id_card.access + + // If we didn't pick a front ID - Maybe none of our cards have any command accesses? Just grab the first card (if we even have one). + // We could also have no ID card in the wallet at all, which will mean we end up with a null front_id and that's fine too. + if(!front_id) + front_id = (locate(/obj/item/card/id) in contents) + if(ishuman(loc)) - var/mob/living/carbon/human/H = loc - if(H.wear_id == src) - H.sec_hud_set_ID() - update_appearance() + var/mob/living/carbon/human/wearing_human = loc + if(wearing_human.wear_id == src) + wearing_human.sec_hud_set_ID() + update_label() + update_appearance(UPDATE_ICON) + update_slot_icon() /obj/item/storage/wallet/Entered(atom/movable/AM) . = ..() - refreshID() + refreshID(removed = FALSE) /obj/item/storage/wallet/update_overlays() . = ..() @@ -94,6 +119,12 @@ if(front_id) . += "Alt-click to remove the id." +/obj/item/storage/wallet/get_id_examine_strings(mob/user) + . = ..() + if(front_id) + . += "\The [src] is displaying [front_id]." + . += front_id.get_id_examine_strings(user) + /obj/item/storage/wallet/GetID() return front_id diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index bb03dda5599..32b01f20e49 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -499,7 +499,7 @@ open() else req_access = list() - req_access += pick(get_all_accesses()) + req_access += pick(SSid_access.get_region_access_list(list(REGION_ALL_STATION))) /obj/structure/closet/contents_explosion(severity, target) for(var/thing in contents) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index e562173bf36..93c513abee2 100755 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -31,7 +31,6 @@ new /obj/item/storage/belt/sabre(src) new /obj/item/gun/energy/e_gun(src) new /obj/item/door_remote/captain(src) - new /obj/item/card/id/captains_spare(src) new /obj/item/storage/photo_album/captain(src) /obj/structure/closet/secure_closet/hop diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm index 4e1e8763945..7f8c0d64ee7 100644 --- a/code/game/objects/structures/ghost_role_spawners.dm +++ b/code/game/objects/structures/ghost_role_spawners.dm @@ -434,7 +434,8 @@ ears = /obj/item/radio/headset/syndicate/alt back = /obj/item/storage/backpack implants = list(/obj/item/implant/weapons_auth) - id = /obj/item/card/id/syndicate + id = /obj/item/card/id/advanced/chameleon + id_trim = /datum/id_trim/chameleon/operative /datum/outfit/syndicate_empty/post_equip(mob/living/carbon/human/H) H.faction |= ROLE_SYNDICATE @@ -476,7 +477,6 @@ flavour_text = "Your job is to oversee your crew, defend the ship, and destroy Space Station 13. The ship has an armory, multiple ships, beam cannons, and multiple crewmembers to accomplish this goal." important_info = "As the captain, this whole operation falls on your shoulders. You do not need to nuke the station, causing sufficient damage and preventing your ship from being destroyed will be enough." outfit = /datum/outfit/syndicate_empty/battlecruiser/assault/captain - id_access_list = list(150,151) /datum/outfit/syndicate_empty/battlecruiser/assault/captain name = "Syndicate Battlecruiser Captain" @@ -488,6 +488,7 @@ head = /obj/item/clothing/head/hos/syndicate mask = /obj/item/clothing/mask/cigarette/cigar/havana glasses = /obj/item/clothing/glasses/thermal/eyepatch + id_trim = /datum/id_trim/battlecruiser/captain //Ancient cryogenic sleepers. Players become NT crewmen from a hundred year old space station, now on the verge of collapse. /obj/effect/mob_spawn/human/oldsec @@ -712,7 +713,7 @@ l_pocket = /obj/item/gun/ballistic/automatic/pistol r_pocket = /obj/item/kitchen/knife/combat/survival belt = /obj/item/storage/belt/military/assault - id = /obj/item/card/id/syndicate_command/crew_id + id = /obj/item/card/id/advanced/black/syndicate_command/crew_id implants = list(/obj/item/implant/weapons_auth) /datum/outfit/syndicatespace/syndicaptain @@ -726,7 +727,7 @@ back = /obj/item/storage/backpack r_pocket = /obj/item/kitchen/knife/combat/survival belt = /obj/item/storage/belt/military/assault - id = /obj/item/card/id/syndicate_command/captain_id + id = /obj/item/card/id/advanced/black/syndicate_command/captain_id implants = list(/obj/item/implant/weapons_auth) backpack_contents = list(/obj/item/documents/syndicate/red, /obj/item/paper/fluff/ruins/forgottenship/password, /obj/item/gun/ballistic/automatic/pistol/aps) @@ -747,7 +748,7 @@ flavour_text = "It's up to you to make sure nobody drowns or gets eaten by sharks and stuff." mob_gender = "female" name = "lifeguard sleeper" - id_job = "Lifeguard" + outfit = /datum/outfit/beachbum/lifeguard uniform = /obj/item/clothing/under/shorts/red /datum/outfit/beachbum @@ -756,7 +757,7 @@ r_pocket = /obj/item/storage/wallet/random l_pocket = /obj/item/food/pizzaslice/dank uniform = /obj/item/clothing/under/pants/youngfolksjeans - id = /obj/item/card/id + id = /obj/item/card/id/advanced /datum/outfit/beachbum/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) . = ..() @@ -764,6 +765,9 @@ return H.dna.add_mutation(STONER) +/datum/outfit/beachbum/lifeguard + id_trim = /datum/id_trim/lifeguard + /obj/effect/mob_spawn/human/bartender/alive death = FALSE roundstart = FALSE @@ -774,7 +778,6 @@ short_desc = "You are a space bartender!" flavour_text = "Time to mix drinks and change lives. Smoking space drugs makes it easier to understand your patrons' odd dialect." assignedrole = "Space Bartender" - id_job = "Bartender" /datum/outfit/spacebartender name = "Space Bartender" @@ -783,7 +786,8 @@ shoes = /obj/item/clothing/shoes/sneakers/black suit = /obj/item/clothing/suit/armor/vest glasses = /obj/item/clothing/glasses/sunglasses/reagent - id = /obj/item/card/id + id = /obj/item/card/id/advanced + id_trim = /datum/id_trim/space_bartender /datum/outfit/spacebartender/post_equip(mob/living/carbon/human/H, visualsOnly) . = ..() diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index f13c6ddf477..0495449c409 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -195,30 +195,32 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that var/mob/living/carbon/human/H = M var/obj/item/worn = H.wear_id var/obj/item/card/id/id = null + if(worn) id = worn.GetID() if(id) - id.icon_state = "gold" - id.access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access() - else - id = new /obj/item/card/id/gold(H.loc) - id.access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access() - id.registered_name = H.real_name - id.assignment = "Captain" - id.update_label() + if(id == worn) + worn = null + qdel(id) - if(worn) - if(istype(worn, /obj/item/pda)) - var/obj/item/pda/PDA = worn - PDA.id = id - id.forceMove(PDA) - else if(istype(worn, /obj/item/storage/wallet)) - var/obj/item/storage/wallet/W = worn - W.front_id = id - id.forceMove(W) - W.update_appearance() - else - H.equip_to_slot(id,ITEM_SLOT_ID) + id = new /obj/item/card/id/advanced/debug() + + id.registered_name = H.real_name + id.update_label() + id.update_icon() + + if(worn) + if(istype(worn, /obj/item/pda)) + var/obj/item/pda/PDA = worn + PDA.id = id + id.forceMove(PDA) + else if(istype(worn, /obj/item/storage/wallet)) + var/obj/item/storage/wallet/W = worn + W.front_id = id + id.forceMove(W) + W.update_icon() + else + H.equip_to_slot(id,ITEM_SLOT_ID) else alert("Invalid mob") diff --git a/code/modules/antagonists/fugitive/fugitive_outfits.dm b/code/modules/antagonists/fugitive/fugitive_outfits.dm index 0882e7109db..abd63eae07f 100644 --- a/code/modules/antagonists/fugitive/fugitive_outfits.dm +++ b/code/modules/antagonists/fugitive/fugitive_outfits.dm @@ -74,7 +74,7 @@ ears = /obj/item/radio/headset l_pocket = /obj/item/ammo_box/magazine/m45 r_pocket = /obj/item/restraints/handcuffs - id = /obj/item/card/id + id = /obj/item/card/id/advanced /datum/outfit/spacepol/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) if(visualsOnly) @@ -83,6 +83,7 @@ W.assignment = "Police Officer" W.registered_name = H.real_name W.update_label() + W.update_icon() /datum/outfit/russiancorpse/hunter ears = /obj/item/radio/headset @@ -104,7 +105,7 @@ glasses = /obj/item/clothing/glasses/sunglasses/garb ears = /obj/item/radio/headset r_pocket = /obj/item/restraints/handcuffs/cable - id = /obj/item/card/id + id = /obj/item/card/id/advanced l_hand = /obj/item/tank/internals/plasma/full r_hand = /obj/item/flamethrower/full/tank @@ -115,6 +116,7 @@ W.assignment = "Bounty Hunter" W.registered_name = H.real_name W.update_label() + W.update_icon() /datum/outfit/bountyhook name = "Bounty Hunter - Hook" @@ -126,7 +128,7 @@ shoes = /obj/item/clothing/shoes/jackboots mask = /obj/item/clothing/mask/scarecrow r_pocket = /obj/item/restraints/handcuffs/cable - id = /obj/item/card/id + id = /obj/item/card/id/advanced r_hand = /obj/item/gun/ballistic/shotgun/doublebarrel/hook backpack_contents = list( @@ -140,6 +142,7 @@ W.assignment = "Bounty Hunter" W.registered_name = H.real_name W.update_label() + W.update_icon() /datum/outfit/bountysynth name = "Bounty Hunter - Synth" @@ -150,7 +153,7 @@ glasses = /obj/item/clothing/glasses/eyepatch r_pocket = /obj/item/restraints/handcuffs/cable ears = /obj/item/radio/headset - id = /obj/item/card/id + id = /obj/item/card/id/advanced r_hand = /obj/item/storage/firstaid/regular l_hand = /obj/item/pinpointer/shuttle @@ -169,3 +172,4 @@ W.assignment = "Bounty Hunter" W.registered_name = H.real_name W.update_label() + W.update_icon() diff --git a/code/modules/antagonists/gang/outfits.dm b/code/modules/antagonists/gang/outfits.dm index 9b9fb67807d..82babe31609 100644 --- a/code/modules/antagonists/gang/outfits.dm +++ b/code/modules/antagonists/gang/outfits.dm @@ -3,10 +3,9 @@ return var/obj/item/card/id/W = H.wear_id - W.access = get_all_accesses() // I have a warrant. - W.assignment = "Space Police" W.registered_name = H.real_name W.update_label() + W.update_icon() ..() /datum/outfit/families_police/beatcop @@ -24,7 +23,7 @@ belt = /obj/item/gun/ballistic/automatic/pistol/m1911 r_pocket = /obj/item/lighter l_pocket = /obj/item/restraints/handcuffs - id = /obj/item/card/id + id = /obj/item/card/id/advanced/black r_hand = /obj/item/gun/energy/disabler backpack_contents = list(/obj/item/storage/box/handcuffs = 1, /obj/item/storage/box/teargas = 1, @@ -34,6 +33,8 @@ /obj/item/ammo_box/c45 = 2, /obj/item/storage/box/survival/security) + id_trim = /datum/id_trim/space_police + /datum/outfit/families_police/beatcop/armored name = "Families: Armored Beat Cop" suit = /obj/item/clothing/suit/armor/vest/blueshirt diff --git a/code/modules/antagonists/highlander/highlander.dm b/code/modules/antagonists/highlander/highlander.dm index 9e1dce422d6..c1e636e51e3 100644 --- a/code/modules/antagonists/highlander/highlander.dm +++ b/code/modules/antagonists/highlander/highlander.dm @@ -62,13 +62,11 @@ H.equip_to_slot_or_del(new /obj/item/pinpointer/nuke(H), ITEM_SLOT_LPOCKET) for(var/obj/item/pinpointer/nuke/P in H) P.attack_self(H) - var/obj/item/card/id/centcom/W = new(H) - W.access = get_all_accesses() - W.access += get_all_centcom_access() - W.assignment = "Highlander" + var/obj/item/card/id/advanced/highlander/W = new(H) W.registered_name = H.real_name ADD_TRAIT(W, TRAIT_NODROP, HIGHLANDER) W.update_label() + W.update_icon() H.equip_to_slot_or_del(W, ITEM_SLOT_ID) sword = new(H) diff --git a/code/modules/antagonists/traitor/equipment/contractor.dm b/code/modules/antagonists/traitor/equipment/contractor.dm index 9553bd04a10..d462ae5c257 100644 --- a/code/modules/antagonists/traitor/equipment/contractor.dm +++ b/code/modules/antagonists/traitor/equipment/contractor.dm @@ -188,8 +188,9 @@ mask = /obj/item/clothing/mask/cigarette/syndicate shoes = /obj/item/clothing/shoes/chameleon/noslip ears = /obj/item/radio/headset/chameleon - id = /obj/item/card/id/syndicate + id = /obj/item/card/id/advanced/chameleon r_hand = /obj/item/storage/toolbox/syndicate + id_trim = /datum/id_trim/chameleon/operative backpack_contents = list(/obj/item/storage/box/survival, /obj/item/implanter/uplink, /obj/item/clothing/mask/chameleon, /obj/item/storage/fancy/cigarettes/cigpack_syndicate, /obj/item/lighter) diff --git a/code/modules/awaymissions/capture_the_flag.dm b/code/modules/awaymissions/capture_the_flag.dm index 94fffc90c71..ab2c9555561 100644 --- a/code/modules/awaymissions/capture_the_flag.dm +++ b/code/modules/awaymissions/capture_the_flag.dm @@ -567,6 +567,7 @@ no_drops += W W.registered_name = H.real_name W.update_label() + W.update_icon() no_drops += H.get_item_by_slot(ITEM_SLOT_OCLOTHING) no_drops += H.get_item_by_slot(ITEM_SLOT_GLOVES) @@ -586,7 +587,7 @@ r_hand = /obj/item/gun/ballistic/automatic/laser/ctf/red l_pocket = /obj/item/ammo_box/magazine/recharge/ctf/red r_pocket = /obj/item/ammo_box/magazine/recharge/ctf/red - id = /obj/item/card/id/syndicate_command //it's red + id = /obj/item/card/id/advanced/black/syndicate_command //it's red /datum/outfit/ctf/red/instagib r_hand = /obj/item/gun/energy/laser/instakill/red @@ -597,7 +598,7 @@ r_hand = /obj/item/gun/ballistic/automatic/laser/ctf/blue l_pocket = /obj/item/ammo_box/magazine/recharge/ctf/blue r_pocket = /obj/item/ammo_box/magazine/recharge/ctf/blue - id = /obj/item/card/id/centcom //it's blue + id = /obj/item/card/id/advanced/centcom //it's blue /datum/outfit/ctf/blue/instagib r_hand = /obj/item/gun/energy/laser/instakill/blue diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index 40547868f9e..8656d29758f 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -147,10 +147,6 @@ var/datum/outfit/outfit = /datum/outfit //If this is a path, it will be instanced in Initialize() var/disable_pda = TRUE var/disable_sensors = TRUE - //All of these only affect the ID that the outfit has placed in the ID slot - var/id_job = null //Such as "Clown" or "Chef." This just determines what the ID reads as, not their access - var/id_access = null //This is for access. See access.dm for which jobs give what access. Use "Captain" if you want it to be all access. - var/id_access_list = null //Allows you to manually add access to an ID card. assignedrole = "Ghost Role" var/husk = null @@ -243,20 +239,9 @@ if(W) if(H.age) W.registered_age = H.age - if(id_access) - for(var/jobtype in typesof(/datum/job)) - var/datum/job/J = new jobtype - if(J.title == id_access) - W.access = J.get_access() - break - if(id_access_list) - if(!islist(W.access)) - W.access = list() - W.access |= id_access_list - if(id_job) - W.assignment = id_job W.registered_name = H.real_name W.update_label() + W.update_icon() //Instant version - use when spawning corpses during runtime /obj/effect/mob_spawn/human/corpse @@ -340,6 +325,11 @@ outfit = /datum/outfit/job/doctor icon_state = "corpsedoctor" +/obj/effect/mob_spawn/human/geneticist + name = "Geneticist" + outfit = /datum/outfit/job/geneticist + icon_state = "corpsescientist" + /obj/effect/mob_spawn/human/engineer name = "Engineer" outfit = /datum/outfit/job/engineer/gloved @@ -348,6 +338,9 @@ /obj/effect/mob_spawn/human/engineer/rig outfit = /datum/outfit/job/engineer/gloved/rig +/obj/effect/mob_spawn/human/engineer/rig/gunner + outfit = /datum/outfit/job/engineer/gloved/rig/gunner + /obj/effect/mob_spawn/human/clown name = "Clown" outfit = /datum/outfit/job/clown @@ -375,8 +368,6 @@ /obj/effect/mob_spawn/human/bartender name = "Space Bartender" - id_job = "Bartender" - id_access_list = list(ACCESS_BAR) outfit = /datum/outfit/spacebartender /obj/effect/mob_spawn/human/beach @@ -386,8 +377,6 @@ /obj/effect/mob_spawn/human/bridgeofficer name = "Bridge Officer" - id_job = "Bridge Officer" - id_access_list = list(ACCESS_CENT_CAPTAIN) outfit = /datum/outfit/nanotrasenbridgeofficercorpse /datum/outfit/nanotrasenbridgeofficercorpse @@ -397,12 +386,11 @@ suit = /obj/item/clothing/suit/armor/bulletproof shoes = /obj/item/clothing/shoes/sneakers/black glasses = /obj/item/clothing/glasses/sunglasses - id = /obj/item/card/id + id = /obj/item/card/id/advanced/centcom + id_trim = /datum/id_trim/centcom/corpse/bridge_officer /obj/effect/mob_spawn/human/commander name = "Commander" - id_job = "Commander" - id_access_list = list(ACCESS_CENT_CAPTAIN, ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_MEDICAL, ACCESS_CENT_STORAGE) outfit = /datum/outfit/nanotrasencommandercorpse /datum/outfit/nanotrasencommandercorpse @@ -416,12 +404,11 @@ gloves = /obj/item/clothing/gloves/tackler/combat shoes = /obj/item/clothing/shoes/combat/swat r_pocket = /obj/item/lighter - id = /obj/item/card/id + id = /obj/item/card/id/advanced/centcom + id_trim = /datum/id_trim/centcom/corpse/commander /obj/effect/mob_spawn/human/nanotrasensoldier name = "\improper Nanotrasen Private Security Officer" - id_job = "Private Security Force" - id_access_list = list(ACCESS_CENT_CAPTAIN, ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_MEDICAL, ACCESS_CENT_STORAGE, ACCESS_SECURITY, ACCESS_MECH_SECURITY) outfit = /datum/outfit/nanotrasensoldiercorpse /datum/outfit/nanotrasensoldiercorpse @@ -433,7 +420,8 @@ mask = /obj/item/clothing/mask/gas/sechailer/swat head = /obj/item/clothing/head/helmet/swat/nanotrasen back = /obj/item/storage/backpack/security - id = /obj/item/card/id + id = /obj/item/card/id/advanced + id_trim = /datum/id_trim/centcom/corpse/private_security /////////////////Spooky Undead////////////////////// //there are living variants of many of these, they're now in ghost_role_spawners.dm diff --git a/code/modules/awaymissions/mission_code/Academy.dm b/code/modules/awaymissions/mission_code/Academy.dm index efdb4dfa8c0..65f4c06aad6 100644 --- a/code/modules/awaymissions/mission_code/Academy.dm +++ b/code/modules/awaymissions/mission_code/Academy.dm @@ -343,7 +343,7 @@ if(18) //Captain ID T.visible_message("A golden identification card appears!") - new /obj/item/card/id/captains_spare(drop_location()) + new /obj/item/card/id/advanced/gold/captains_spare(drop_location()) do_smoke(0, drop_location()) if(19) //Instrinct Resistance diff --git a/code/modules/awaymissions/mission_code/snowdin.dm b/code/modules/awaymissions/mission_code/snowdin.dm index 45c506162a7..25f68606f04 100644 --- a/code/modules/awaymissions/mission_code/snowdin.dm +++ b/code/modules/awaymissions/mission_code/snowdin.dm @@ -595,9 +595,9 @@ shoes = /obj/item/clothing/shoes/combat/coldres ears = /obj/item/radio/headset/syndicate/alt r_pocket = /obj/item/gun/ballistic/automatic/pistol - id = /obj/item/card/id/syndicate + id = /obj/item/card/id/advanced/chameleon implants = list(/obj/item/implant/exile) - + id_trim = /datum/id_trim/chameleon/operative /obj/effect/mob_spawn/human/syndicatesoldier/coldres/alive/female mob_gender = FEMALE diff --git a/code/modules/cargo/order.dm b/code/modules/cargo/order.dm index fc63973efd4..e44801c0b38 100644 --- a/code/modules/cargo/order.dm +++ b/code/modules/cargo/order.dm @@ -51,7 +51,7 @@ P.info += "Order #[id]
" P.info += "Time of Order: [station_time_timestamp()]
" P.info += "Item: [pack.name]
" - P.info += "Access Restrictions: [get_access_desc(pack.access)]
" + P.info += "Access Restrictions: [SSid_access.get_access_desc(pack.access)]
" P.info += "Requested by: [orderer]
" if(paying_account) P.info += "Paid by: [paying_account.account_holder]
" diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm index c35b4534ffb..e00ff166f52 100644 --- a/code/modules/cargo/packs.dm +++ b/code/modules/cargo/packs.dm @@ -329,8 +329,6 @@ /obj/item/gun/energy/laser) crate_name = "laser crate" - - /datum/supply_pack/security/securitybarriers name = "Security Barrier Grenades" desc = "Stem the tide with four Security Barrier grenades. Requires Security access to open." @@ -1615,6 +1613,22 @@ /obj/item/reagent_containers/food/drinks/bottle/champagne) crate_name = "wedding crate" +/// Box of 7 grey IDs. +/datum/supply_pack/service/greyidbox + name = "Grey ID Card Multipack Cate" + desc = "A convenient crate containing a box of cheap ID cards in a handy wallet-sized form factor. Cards come in every colour you can imagne, as long as it's grey." + cost = CARGO_CRATE_VALUE * 3 + contains = list(/obj/item/storage/box/ids) + crate_name = "basic id card crate" + +/// Single silver ID. +/datum/supply_pack/service/silverid + name = "Silver ID Card Crate" + desc = "Did we forget to hire any Heads of Staff? Recruit your own with this high value ID card capable of holding advanced levels of access in a handy wallet-sized form factor" + cost = CARGO_CRATE_VALUE * 7 + contains = list(/obj/item/card/id/advanced/silver) + crate_name = "silver id card crate" + /datum/supply_pack/service/emptycrate name = "Empty Crate" desc = "It's an empty crate, for all your storage needs." diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm index f2479ac26d3..6dd0cb5bfaf 100644 --- a/code/modules/clothing/chameleon.dm +++ b/code/modules/clothing/chameleon.dm @@ -257,24 +257,89 @@ /datum/action/item_action/chameleon/change/id/update_item(obj/item/picked_item) ..() - var/obj/item/card/id/syndicate/agent_card = target + var/obj/item/card/id/advanced/chameleon/agent_card = target if(istype(agent_card)) var/obj/item/card/id/copied_card = picked_item - agent_card.uses_overlays = initial(copied_card.uses_overlays) - agent_card.id_type_name = initial(copied_card.id_type_name) + + // If the outfit comes with a special trim override, we'll steal some stuff from that. + var/new_trim = initial(copied_card.trim) + + if(new_trim) + SSid_access.apply_trim_to_chameleon_card(agent_card, new_trim, TRUE) + + // If the ID card hasn't been forged, we'll check if there has been an assignment set already by any new trim. + // If there has not, we set the assignment to the copied card's default as well as copying over the the + // default registered name from the copied card. if(!agent_card.forged) + if(!agent_card.assignment) + agent_card.assignment = initial(copied_card.assignment) + agent_card.registered_name = initial(copied_card.registered_name) - agent_card.assignment = initial(copied_card.assignment) + + agent_card.icon_state = initial(copied_card.icon_state) + if(ispath(copied_card, /obj/item/card/id/advanced)) + var/obj/item/card/id/advanced/copied_advanced_card = copied_card + agent_card.assigned_icon_state = initial(copied_advanced_card.assigned_icon_state) + agent_card.update_label() - if(!agent_card.forged) - agent_card.name = initial(copied_card.name) //e.g. captain's spare ID, not Captain's ID Card (Captain) + agent_card.update_icon() /datum/action/item_action/chameleon/change/id/apply_job_data(datum/job/job_datum) ..() - var/obj/item/card/id/syndicate/agent_card = target + var/obj/item/card/id/advanced/chameleon/agent_card = target if(istype(agent_card) && istype(job_datum)) agent_card.forged = TRUE - agent_card.assignment = job_datum.title + + // job_outfit is going to be a path. + var/datum/outfit/job/job_outfit = job_datum.outfit + if(!job_outfit) + return + + // copied_card is also going to be a path. + var/obj/item/card/id/copied_card = initial(job_outfit.id) + if(!copied_card) + return + + // If the outfit comes with a special trim override, we'll use that. Otherwise, use the card's default trim. Failing that, no trim at all. + var/new_trim = initial(job_outfit.id_trim) ? initial(job_outfit.id_trim) : initial(copied_card.trim) + + if(new_trim) + SSid_access.apply_trim_to_chameleon_card(agent_card, new_trim, FALSE) + else + agent_card.assignment = job_datum.title + + agent_card.icon_state = initial(copied_card.icon_state) + if(ispath(copied_card, /obj/item/card/id/advanced)) + var/obj/item/card/id/advanced/copied_advanced_card = copied_card + agent_card.assigned_icon_state = initial(copied_advanced_card.assigned_icon_state) + + agent_card.update_label() + agent_card.update_icon() + +/datum/action/item_action/chameleon/change/id_trim/initialize_disguises() + if(button) + button.name = "Change [chameleon_name] Appearance" + + chameleon_blacklist |= typecacheof(target.type) + for(var/trim_path in typesof(chameleon_type)) + if(ispath(trim_path) && ispath(trim_path, /datum/id_trim)) + if(chameleon_blacklist[trim_path]) + continue + + var/datum/id_trim/trim = SSid_access.trim_singletons_by_path[trim_path] + + if(trim && trim.trim_state && trim.assignment) + var/chameleon_item_name = "[trim.assignment] ([trim.trim_state])" + chameleon_list[chameleon_item_name] = trim_path + +/datum/action/item_action/chameleon/change/id_trim/update_item(picked_trim_path) + var/obj/item/card/id/advanced/chameleon/agent_card = target + + if(istype(agent_card)) + SSid_access.apply_trim_to_chameleon_card(agent_card, picked_trim_path, TRUE) + + agent_card.update_label() + agent_card.update_icon() /datum/action/item_action/chameleon/change/pda/update_item(obj/item/picked_item) ..() diff --git a/code/modules/clothing/outfits/ert.dm b/code/modules/clothing/outfits/ert.dm index fad9363db0a..9de70e4d1cd 100644 --- a/code/modules/clothing/outfits/ert.dm +++ b/code/modules/clothing/outfits/ert.dm @@ -19,12 +19,13 @@ if(W) W.registered_name = H.real_name W.update_label() - ..() + W.update_icon() + return ..() /datum/outfit/centcom/ert/commander name = "ERT Commander" - id = /obj/item/card/id/ert + id = /obj/item/card/id/advanced/centcom/ert suit = /obj/item/clothing/suit/space/hardsuit/ert suit_store = /obj/item/gun/energy/e_gun glasses = /obj/item/clothing/glasses/hud/security/sunglasses @@ -56,7 +57,7 @@ /datum/outfit/centcom/ert/security name = "ERT Security" - id = /obj/item/card/id/ert/security + id = /obj/item/card/id/advanced/centcom/ert/security suit = /obj/item/clothing/suit/space/hardsuit/ert/sec suit_store = /obj/item/gun/energy/e_gun/stun glasses = /obj/item/clothing/glasses/hud/security/sunglasses @@ -91,7 +92,7 @@ /datum/outfit/centcom/ert/medic name = "ERT Medic" - id = /obj/item/card/id/ert/medical + id = /obj/item/card/id/advanced/centcom/ert/medical suit = /obj/item/clothing/suit/space/hardsuit/ert/med suit_store = /obj/item/gun/energy/e_gun glasses = /obj/item/clothing/glasses/hud/health @@ -126,7 +127,7 @@ /datum/outfit/centcom/ert/engineer name = "ERT Engineer" - id = /obj/item/card/id/ert/engineer + id = /obj/item/card/id/advanced/centcom/ert/engineer suit = /obj/item/clothing/suit/space/hardsuit/ert/engi suit_store = /obj/item/gun/energy/e_gun glasses = /obj/item/clothing/glasses/meson/engine @@ -174,8 +175,9 @@ back = /obj/item/storage/backpack/satchel r_pocket = /obj/item/pda/heads l_hand = /obj/item/clipboard - id = /obj/item/card/id/centcom + id = /obj/item/card/id/advanced/centcom backpack_contents = list(/obj/item/stamp/centcom=1) + id_trim = /datum/id_trim/centcom/official /datum/outfit/centcom/centcom_official/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) if(visualsOnly) @@ -187,12 +189,10 @@ pda.update_label() var/obj/item/card/id/W = H.wear_id - W.access = get_centcom_access("CentCom Official") - W.access += ACCESS_WEAPONS - W.assignment = "CentCom Official" W.registered_name = H.real_name W.update_label() - ..() + W.update_icon() + return ..() /datum/outfit/centcom/ert/commander/inquisitor name = "Inquisition Commander" @@ -227,7 +227,7 @@ suit = /obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor // Chap role always gets this suit suit_store = /obj/item/gun/energy/e_gun - id = /obj/item/card/id/ert/chaplain + id = /obj/item/card/id/advanced/centcom/ert/chaplain glasses = /obj/item/clothing/glasses/hud/health back = /obj/item/storage/backpack/cultpack belt = /obj/item/storage/belt/soulstone @@ -255,7 +255,7 @@ /datum/outfit/centcom/ert/janitor name = "ERT Janitor" - id = /obj/item/card/id/ert/janitor + id = /obj/item/card/id/advanced/centcom/ert/janitor suit = /obj/item/clothing/suit/space/hardsuit/ert/jani glasses = /obj/item/clothing/glasses/night back = /obj/item/storage/backpack/ert/janitor @@ -295,7 +295,7 @@ suit = /obj/item/clothing/suit/space/hardsuit/ert/clown mask = /obj/item/clothing/mask/gas/clown_hat - id = /obj/item/card/id/ert/clown + id = /obj/item/card/id/advanced/centcom/ert/clown glasses = /obj/item/clothing/glasses/godeye back = /obj/item/storage/backpack/ert/clown belt = /obj/item/storage/belt/champion @@ -332,19 +332,19 @@ back = /obj/item/storage/backpack/satchel l_pocket = /obj/item/ammo_box/a762 r_pocket = /obj/item/ammo_box/a762 - id = /obj/item/card/id/centcom + id = /obj/item/card/id/advanced/centcom backpack_contents = list(/obj/item/storage/box/survival = 1) + id_trim = /datum/id_trim/centcom/intern /datum/outfit/centcom/centcom_intern/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) if(visualsOnly) return var/obj/item/card/id/W = H.wear_id - W.access = get_centcom_access(name) - W.access += ACCESS_WEAPONS - W.assignment = name W.registered_name = H.real_name W.update_label() + W.update_icon() + return ..() /datum/outfit/centcom/centcom_intern/unarmed name = "CentCom Intern (Unarmed)" diff --git a/code/modules/clothing/outfits/standard.dm b/code/modules/clothing/outfits/standard.dm index fbc4fe8ac5c..189d1635417 100644 --- a/code/modules/clothing/outfits/standard.dm +++ b/code/modules/clothing/outfits/standard.dm @@ -19,18 +19,17 @@ belt = /obj/item/gun/energy/pulse/pistol/m1911 r_pocket = /obj/item/lighter back = /obj/item/storage/backpack/satchel/leather - id = /obj/item/card/id/centcom + id = /obj/item/card/id/advanced/centcom + id_trim = /datum/id_trim/centcom/specops_officer /datum/outfit/centcom/spec_ops/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) if(visualsOnly) return var/obj/item/card/id/W = H.wear_id - W.access = get_all_accesses() - W.access += get_centcom_access("Special Ops Officer") - W.assignment = "Special Ops Officer" W.registered_name = H.real_name W.update_label() + W.update_icon() var/obj/item/radio/headset/R = H.ears R.set_frequency(FREQ_CENTCOM) @@ -138,6 +137,7 @@ if(outfit_id) outfit_id.registered_name = equipped.real_name outfit_id.update_label() + outfit_id.update_icon() /datum/outfit/pirate/captain name = "Space Pirate Captain" @@ -150,7 +150,7 @@ head = /obj/item/clothing/head/helmet/space/pirate/bandana mask = /obj/item/clothing/mask/breath suit_store = /obj/item/tank/internals/oxygen - id = /obj/item/card/id + id = /obj/item/card/id/advanced /datum/outfit/pirate/space/captain name = "Space Pirate Captain (EVA)" @@ -166,25 +166,14 @@ shoes = /obj/item/clothing/shoes/laceup suit = /obj/item/clothing/suit/armor/vest/alt gloves = /obj/item/clothing/gloves/color/black - - id = /obj/item/card/id/silver - - ///special fluff for the ID's job - var/scale_assignment = "Silver Scale Member" - -/datum/outfit/pirate/silverscale/post_equip(mob/living/carbon/human/equipped) - ..() - var/obj/item/card/id/outfit_id = equipped.wear_id - if(outfit_id) - outfit_id.assignment = scale_assignment - outfit_id.update_label() + id_trim = /datum/id_trim/pirate/silverscale + id = /obj/item/card/id/advanced/silver /datum/outfit/pirate/silverscale/captain mask = /obj/item/clothing/mask/cigarette/cigar/havana l_pocket = /obj/item/lighter head = /obj/item/clothing/head/crown - - scale_assignment = "Silver Scale VIP" + id_trim = /datum/id_trim/pirate/silverscale/captain /datum/outfit/tunnel_clown name = "Tunnel Clown" @@ -198,18 +187,18 @@ suit = /obj/item/clothing/suit/hooded/chaplain_hoodie l_pocket = /obj/item/food/grown/banana r_pocket = /obj/item/bikehorn - id = /obj/item/card/id + id = /obj/item/card/id/advanced/gold l_hand = /obj/item/fireaxe + id_trim = /datum/id_trim/tunnel_clown /datum/outfit/tunnel_clown/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) if(visualsOnly) return var/obj/item/card/id/W = H.wear_id - W.access = get_all_accesses() - W.assignment = "Tunnel Clown!" W.registered_name = H.real_name W.update_label() + W.update_icon() /datum/outfit/psycho name = "Masked Killer" @@ -243,8 +232,9 @@ glasses = /obj/item/clothing/glasses/sunglasses l_pocket = /obj/item/melee/transforming/energy/sword/saber l_hand = /obj/item/storage/secure/briefcase - id = /obj/item/card/id/syndicate + id = /obj/item/card/id/advanced/chameleon/black belt = /obj/item/pda/heads + id_trim = /datum/id_trim/reaper_assassin /datum/outfit/assassin/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) var/obj/item/clothing/under/U = H.w_uniform @@ -269,11 +259,10 @@ pda.ownjob = "Reaper" pda.update_label() - var/obj/item/card/id/syndicate/W = H.wear_id - W.access = get_all_accesses() - W.assignment = "Reaper" + var/obj/item/card/id/W = H.wear_id W.registered_name = H.real_name W.update_label() + W.update_icon() /datum/outfit/centcom/commander name = "CentCom Commander" @@ -290,18 +279,17 @@ r_pocket = /obj/item/lighter l_pocket = /obj/item/ammo_box/a357 back = /obj/item/storage/backpack/satchel/leather - id = /obj/item/card/id/centcom + id = /obj/item/card/id/advanced/centcom + id_trim = /datum/id_trim/centcom/commander /datum/outfit/centcom/commander/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) if(visualsOnly) return var/obj/item/card/id/W = H.wear_id - W.access = get_all_accesses() - W.access += get_centcom_access("CentCom Commander") - W.assignment = "CentCom Commander" W.registered_name = H.real_name W.update_label() + W.update_icon() ..() /datum/outfit/ghost_cultist @@ -366,18 +354,18 @@ back = /obj/item/storage/backpack/satchel/leather belt = /obj/item/gun/ballistic/revolver/mateba - id = /obj/item/card/id/centcom + id = /obj/item/card/id/advanced/centcom + id_trim = /datum/id_trim/centcom/admiral /datum/outfit/centcom/soviet/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) if(visualsOnly) return var/obj/item/card/id/W = H.wear_id - W.access = get_all_accesses() - W.access += get_centcom_access("Admiral") - W.assignment = "Admiral" + W.registered_name = H.real_name W.update_label() + W.update_icon() ..() /datum/outfit/mobster @@ -390,16 +378,17 @@ ears = /obj/item/radio/headset glasses = /obj/item/clothing/glasses/sunglasses l_hand = /obj/item/gun/ballistic/automatic/tommygun - id = /obj/item/card/id + id = /obj/item/card/id/advanced + id_trim = /datum/id_trim/mobster /datum/outfit/mobster/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) if(visualsOnly) return var/obj/item/card/id/W = H.wear_id - W.assignment = "Assistant" W.registered_name = H.real_name W.update_label() + W.update_icon() /datum/outfit/plasmaman name = "Plasmaman" @@ -426,7 +415,7 @@ suit_store = /obj/item/tank/internals/emergency_oxygen/double belt = /obj/item/gun/ballistic/revolver/mateba l_hand = /obj/item/gun/energy/pulse/loyalpin - id = /obj/item/card/id/ert/deathsquad + id = /obj/item/card/id/advanced/black/deathsquad ears = /obj/item/radio/headset/headset_cent/alt skillchips = list(/obj/item/skillchip/disk_verifier) @@ -438,6 +427,8 @@ /obj/item/flashlight=1,\ /obj/item/grenade/c4/x4=1) + id_trim = /datum/id_trim/centcom/deathsquad + /datum/outfit/centcom/death_commando/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) if(visualsOnly) return @@ -446,11 +437,9 @@ R.set_frequency(FREQ_CENTCOM) R.freqlock = TRUE var/obj/item/card/id/W = H.wear_id - W.access = get_all_accesses()//They get full station access. - W.access += get_centcom_access("Death Commando")//Let's add their alloted CentCom access. - W.assignment = "Death Commando" W.registered_name = H.real_name W.update_label() + W.update_icon() ..() /datum/outfit/centcom/death_commando/officer @@ -476,7 +465,7 @@ gloves = /obj/item/clothing/gloves/combat belt = /obj/item/storage/belt/utility/chief/full shoes = /obj/item/clothing/shoes/magboots/advance - id = /obj/item/card/id/debug + id = /obj/item/card/id/advanced/debug suit_store = /obj/item/tank/internals/oxygen back = /obj/item/storage/backpack/holding box = /obj/item/storage/box/debugtools @@ -494,6 +483,7 @@ var/obj/item/card/id/W = H.wear_id W.registered_name = H.real_name W.update_label() + W.update_icon() /datum/outfit/admin //for admeem shenanigans and testing things that arent related to equipment, not a subtype of debug just in case debug changes things name = "Admin outfit" @@ -505,7 +495,7 @@ gloves = /obj/item/clothing/gloves/combat belt = /obj/item/storage/belt/utility/chief/full shoes = /obj/item/clothing/shoes/magboots/advance - id = /obj/item/card/id/debug + id = /obj/item/card/id/advanced/debug suit_store = /obj/item/tank/internals/oxygen back = /obj/item/storage/backpack/holding box = /obj/item/storage/box/debugtools @@ -524,3 +514,4 @@ var/obj/item/card/id/W = H.wear_id W.registered_name = H.real_name W.update_label() + W.update_icon() diff --git a/code/modules/clothing/outfits/vr.dm b/code/modules/clothing/outfits/vr.dm index 520c4c0f8b7..0bb1a8b4fb5 100644 --- a/code/modules/clothing/outfits/vr.dm +++ b/code/modules/clothing/outfits/vr.dm @@ -3,27 +3,24 @@ uniform = /obj/item/clothing/under/color/random shoes = /obj/item/clothing/shoes/sneakers/black ears = /obj/item/radio/headset - id = /obj/item/card/id + id = /obj/item/card/id/advanced + id_trim = /datum/id_trim/vr /datum/outfit/vr/pre_equip(mob/living/carbon/human/H) H.dna.species.before_equip_job(null, H) -/datum/outfit/vr/post_equip(mob/living/carbon/human/H) - var/obj/item/card/id/id = H.wear_id - if (istype(id)) - id.access |= get_all_accesses() - /datum/outfit/vr/syndicate name = "Syndicate VR Operative - Basic" uniform = /obj/item/clothing/under/syndicate shoes = /obj/item/clothing/shoes/combat gloves = /obj/item/clothing/gloves/tackler/combat/insulated back = /obj/item/storage/backpack - id = /obj/item/card/id/syndicate + id = /obj/item/card/id/advanced/chameleon/black belt = /obj/item/gun/ballistic/automatic/pistol l_pocket = /obj/item/paper/fluff/vr/fluke_ops backpack_contents = list(/obj/item/storage/box/survival/syndie=1,\ /obj/item/kitchen/knife/combat/survival) + id_trim = /datum/id_trim/vr/operative /datum/outfit/vr/syndicate/post_equip(mob/living/carbon/human/H) . = ..() diff --git a/code/modules/clothing/outfits/vv_outfit.dm b/code/modules/clothing/outfits/vv_outfit.dm index 0c5d99cc748..7c4fcc96dbd 100644 --- a/code/modules/clothing/outfits/vv_outfit.dm +++ b/code/modules/clothing/outfits/vv_outfit.dm @@ -84,8 +84,11 @@ if(id_slot) O.stored_access |= id_slot.GetAccess() var/obj/item/card/id/ID = id_slot.GetID() - if(ID && ID.registered_name == real_name) - O.update_id_name = TRUE + if(ID) + if(ID.registered_name == real_name) + O.update_id_name = TRUE + if(ID.trim) + O.id_trim = ID.trim.type //Copy hands if(held_items.len >= 2) //Not in the mood to let outfits transfer amputees var/obj/item/left_hand = held_items[1] @@ -144,10 +147,11 @@ if(id_slot) var/obj/item/card/id/card = id_slot.GetID() if(istype(card)) - card.access |= stored_access + card.add_access(stored_access, mode = FORCE_ADD_ALL) if(update_id_name) card.registered_name = H.real_name card.update_label() + card.update_icon() /datum/outfit/varedit/get_json_data() . = .. () diff --git a/code/modules/economy/account.dm b/code/modules/economy/account.dm index 71c856d738f..9a43e0fc1c2 100644 --- a/code/modules/economy/account.dm +++ b/code/modules/economy/account.dm @@ -103,10 +103,9 @@ return for(var/obj/A in bank_cards) var/icon_source = A - if(istype(A, /obj/item/card/id)) - var/obj/item/card/id/id_card = A - if(id_card.uses_overlays) - icon_source = id_card.get_cached_flat_icon() + if(istype(A, /obj/item/card/id/advanced)) + var/obj/item/card/id/advanced/id_card = A + icon_source = id_card.get_cached_flat_icon() var/mob/card_holder = recursive_loc_check(A, /mob) if(ismob(card_holder)) //If on a mob if(!card_holder.client || (!(card_holder.client.prefs.chat_toggles & CHAT_BANKCARD) && !force)) diff --git a/code/modules/jobs/access.dm b/code/modules/jobs/access.dm index 49f92e7522f..46e6ac80384 100644 --- a/code/modules/jobs/access.dm +++ b/code/modules/jobs/access.dm @@ -102,291 +102,19 @@ /obj/proc/check_access_ntnet(list/passkey) return check_access_list(passkey) -/proc/get_centcom_access(job) - switch(job) - if("VIP Guest") - return list(ACCESS_CENT_GENERAL) - if("Custodian") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_STORAGE) - if("Thunderdome Overseer") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_THUNDER) - if("CentCom Official") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING) - if("CentCom Intern") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING) - if("CentCom Head Intern") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING) - if("Medical Officer") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL) - if("Death Commando") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_LIVING, ACCESS_CENT_STORAGE) - if("Research Officer") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_MEDICAL, ACCESS_CENT_TELEPORTER, ACCESS_CENT_STORAGE) - if("Special Ops Officer") - return get_all_centcom_access() - if("Admiral") - return get_all_centcom_access() - if("CentCom Commander") - return get_all_centcom_access() - if("Emergency Response Team Commander") - return get_ert_access("commander") - if("Security Response Officer") - return get_ert_access("sec") - if("Engineer Response Officer") - return get_ert_access("eng") - if("Medical Response Officer") - return get_ert_access("med") - if("CentCom Bartender") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_BAR) - -/proc/get_all_accesses() - return list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_FORENSICS_LOCKERS, ACCESS_COURT, - ACCESS_MEDICAL, ACCESS_GENETICS, ACCESS_MORGUE, ACCESS_RD, - ACCESS_RND, ACCESS_TOXINS, ACCESS_CHEMISTRY, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_MAINT_TUNNELS, - ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, - ACCESS_TELEPORTER, ACCESS_EVA, ACCESS_HEADS, ACCESS_CAPTAIN, ACCESS_ALL_PERSONAL_LOCKERS, - ACCESS_TECH_STORAGE, ACCESS_CHAPEL_OFFICE, ACCESS_ATMOSPHERICS, ACCESS_KITCHEN, - ACCESS_BAR, ACCESS_JANITOR, ACCESS_CREMATORIUM, ACCESS_ROBOTICS, ACCESS_CARGO, ACCESS_CONSTRUCTION, ACCESS_AUX_BASE, - ACCESS_HYDROPONICS, ACCESS_LIBRARY, ACCESS_LAWYER, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_QM, ACCESS_SURGERY, ACCESS_PSYCHOLOGY, - ACCESS_THEATRE, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_MAILSORTING, ACCESS_WEAPONS, - ACCESS_MECH_MINING, ACCESS_MECH_ENGINE, ACCESS_MECH_SCIENCE, ACCESS_MECH_SECURITY, ACCESS_MECH_MEDICAL, - ACCESS_VAULT, ACCESS_MINING_STATION, ACCESS_XENOBIOLOGY, ACCESS_CE, ACCESS_HOP, ACCESS_HOS, ACCESS_PHARMACY, ACCESS_RC_ANNOUNCE, - ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM, ACCESS_MINISAT, ACCESS_NETWORK, ACCESS_TOXINS_STORAGE) - -/proc/get_all_centcom_access() - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_THUNDER, ACCESS_CENT_SPECOPS, ACCESS_CENT_MEDICAL, ACCESS_CENT_LIVING, ACCESS_CENT_STORAGE, ACCESS_CENT_TELEPORTER, ACCESS_CENT_CAPTAIN) - -/proc/get_ert_access(class) - switch(class) - if("commander") - return get_all_centcom_access() - if("sec") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_LIVING) - if("eng") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_LIVING, ACCESS_CENT_STORAGE) - if("med") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_MEDICAL, ACCESS_CENT_LIVING) - -/proc/get_all_syndicate_access() - return list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER) - -/proc/get_region_accesses(code) - switch(code) - if(0) - return get_all_accesses() - if(1) //station general - return list(ACCESS_KITCHEN,ACCESS_BAR, ACCESS_HYDROPONICS, ACCESS_JANITOR, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_LIBRARY, ACCESS_THEATRE, ACCESS_LAWYER) - if(2) //security - return list(ACCESS_SEC_DOORS, ACCESS_WEAPONS, ACCESS_SECURITY, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_FORENSICS_LOCKERS, ACCESS_COURT, ACCESS_MECH_SECURITY, ACCESS_HOS) - if(3) //medbay - return list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_SURGERY, ACCESS_MECH_MEDICAL, ACCESS_CMO, ACCESS_PHARMACY, ACCESS_PSYCHOLOGY) - if(4) //research - return list(ACCESS_RESEARCH, ACCESS_RND, ACCESS_TOXINS, ACCESS_TOXINS_STORAGE, ACCESS_GENETICS, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_MECH_SCIENCE, ACCESS_MINISAT, ACCESS_RD, ACCESS_NETWORK) - if(5) //engineering and maintenance - return list(ACCESS_CONSTRUCTION, ACCESS_AUX_BASE, ACCESS_MAINT_TUNNELS, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_TECH_STORAGE, ACCESS_ATMOSPHERICS, ACCESS_MECH_ENGINE, ACCESS_TCOMSAT, ACCESS_MINISAT, ACCESS_CE) - if(6) //supply - return list(ACCESS_MAILSORTING, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MECH_MINING, ACCESS_MINERAL_STOREROOM, ACCESS_CARGO, ACCESS_QM, ACCESS_VAULT) - if(7) //command - return list(ACCESS_HEADS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_TELEPORTER, ACCESS_EVA, ACCESS_GATEWAY, ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_HOP, ACCESS_CAPTAIN, ACCESS_VAULT) - -/proc/get_region_accesses_name(code) - switch(code) - if(0) - return "All" - if(1) //station general - return "General" - if(2) //security - return "Security" - if(3) //medbay - return "Medbay" - if(4) //research - return "Research" - if(5) //engineering and maintenance - return "Engineering" - if(6) //supply - return "Supply" - if(7) //command - return "Command" - -/proc/get_access_desc(A) - switch(A) - if(ACCESS_CARGO) - return "Cargo Bay" - if(ACCESS_SECURITY) - return "Security" - if(ACCESS_BRIG) - return "Holding Cells" - if(ACCESS_COURT) - return "Courtroom" - if(ACCESS_FORENSICS_LOCKERS) - return "Forensics" - if(ACCESS_MEDICAL) - return "Medical" - if(ACCESS_GENETICS) - return "Genetics Lab" - if(ACCESS_MORGUE) - return "Morgue" - if(ACCESS_RND) - return "R&D Lab" - if(ACCESS_TOXINS) - return "Toxins Lab" - if(ACCESS_TOXINS_STORAGE) - return "Toxins Storage" - if(ACCESS_CHEMISTRY) - return "Chemistry Lab" - if(ACCESS_RD) - return "RD Office" - if(ACCESS_BAR) - return "Bar" - if(ACCESS_JANITOR) - return "Custodial Closet" - if(ACCESS_ENGINE) - return "Engineering" - if(ACCESS_ENGINE_EQUIP) - return "Power and Engineering Equipment" - if(ACCESS_MAINT_TUNNELS) - return "Maintenance" - if(ACCESS_EXTERNAL_AIRLOCKS) - return "External Airlocks" - if(ACCESS_CHANGE_IDS) - return "ID Console" - if(ACCESS_AI_UPLOAD) - return "AI Chambers" - if(ACCESS_TELEPORTER) - return "Teleporter" - if(ACCESS_EVA) - return "EVA" - if(ACCESS_HEADS) - return "Bridge" - if(ACCESS_CAPTAIN) - return "Captain" - if(ACCESS_ALL_PERSONAL_LOCKERS) - return "Personal Lockers" - if(ACCESS_CHAPEL_OFFICE) - return "Chapel Office" - if(ACCESS_TECH_STORAGE) - return "Technical Storage" - if(ACCESS_ATMOSPHERICS) - return "Atmospherics" - if(ACCESS_CREMATORIUM) - return "Crematorium" - if(ACCESS_ARMORY) - return "Armory" - if(ACCESS_CONSTRUCTION) - return "Construction" - if(ACCESS_KITCHEN) - return "Kitchen" - if(ACCESS_HYDROPONICS) - return "Hydroponics" - if(ACCESS_LIBRARY) - return "Library" - if(ACCESS_LAWYER) - return "Law Office" - if(ACCESS_ROBOTICS) - return "Robotics" - if(ACCESS_VIROLOGY) - return "Virology" - if(ACCESS_PSYCHOLOGY) - return "Psychology" - if(ACCESS_CMO) - return "CMO Office" - if(ACCESS_QM) - return "Quartermaster" - if(ACCESS_SURGERY) - return "Surgery" - if(ACCESS_THEATRE) - return "Theatre" - if(ACCESS_RESEARCH) - return "Science" - if(ACCESS_MINING) - return "Mining" - if(ACCESS_MAILSORTING) - return "Cargo Office" - if(ACCESS_VAULT) - return "Main Vault" - if(ACCESS_MINING_STATION) - return "Mining EVA" - if(ACCESS_XENOBIOLOGY) - return "Xenobiology Lab" - if(ACCESS_HOP) - return "HoP Office" - if(ACCESS_HOS) - return "HoS Office" - if(ACCESS_CE) - return "CE Office" - if(ACCESS_PHARMACY) - return "Pharmacy" - if(ACCESS_RC_ANNOUNCE) - return "RC Announcements" - if(ACCESS_KEYCARD_AUTH) - return "Keycode Auth." - if(ACCESS_TCOMSAT) - return "Telecommunications" - if(ACCESS_GATEWAY) - return "Gateway" - if(ACCESS_SEC_DOORS) - return "Brig" - if(ACCESS_MINERAL_STOREROOM) - return "Mineral Storage" - if(ACCESS_MINISAT) - return "AI Satellite" - if(ACCESS_WEAPONS) - return "Weapon Permit" - if(ACCESS_NETWORK) - return "Network Access" - if(ACCESS_MECH_MINING) - return "Mining Mech Access" - if(ACCESS_MECH_MEDICAL) - return "Medical Mech Access" - if(ACCESS_MECH_SECURITY) - return "Security Mech Access" - if(ACCESS_MECH_SCIENCE) - return "Science Mech Access" - if(ACCESS_MECH_ENGINE) - return "Engineering Mech Access" - if(ACCESS_AUX_BASE) - return "Auxiliary Base" - -/proc/get_centcom_access_desc(A) - switch(A) - if(ACCESS_CENT_GENERAL) - return "Code Grey" - if(ACCESS_CENT_THUNDER) - return "Code Yellow" - if(ACCESS_CENT_STORAGE) - return "Code Orange" - if(ACCESS_CENT_LIVING) - return "Code Green" - if(ACCESS_CENT_MEDICAL) - return "Code White" - if(ACCESS_CENT_TELEPORTER) - return "Code Blue" - if(ACCESS_CENT_SPECOPS) - return "Code Black" - if(ACCESS_CENT_CAPTAIN) - return "Code Gold" - if(ACCESS_CENT_BAR) - return "Code Scotch" - -/proc/get_all_jobs() - return 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") - -/proc/get_all_job_icons() //For all existing HUD icons - return get_all_jobs() + 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") - -/proc/get_all_centcom_jobs() - return 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/proc/GetJobName() //Used in secHUD icon generation - var/obj/item/card/id/I = GetID() - if(!I) + var/obj/item/card/id/id_card = GetID() + + if(!id_card) return - var/jobName = I.assignment - if(jobName in get_all_job_icons()) //Check if the job has a hud icon - return jobName - if(jobName in get_all_centcom_jobs()) //Return with the NT logo if it is a CentCom job + + var/card_assignment = id_card.trim?.assignment + + if(!card_assignment) + card_assignment = id_card.assignment + + if(card_assignment in (SSjob.station_jobs + SSjob.additional_jobs_with_icons)) //Check if the job has a hud icon + return card_assignment + if(card_assignment in SSjob.centcom_jobs) //Return with the NT logo if it is a CentCom job return "CentCom" return "Unknown" //Return unknown if none of the above apply diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm index ed919ac8649..9f6098c2680 100644 --- a/code/modules/jobs/job_types/_job.dm +++ b/code/modules/jobs/job_types/_job.dm @@ -2,10 +2,6 @@ //The name of the job , used for preferences, bans and more. Make sure you know what you're doing before changing this. var/title = "NOPE" - //Job access. The use of minimal_access or access is determined by a config setting: config.jobs_have_minimal_access - var/list/minimal_access = list() //Useful for servers which prefer to only have access given to the places a job absolutely needs (Larger server population) - var/list/access = list() //Useful for servers which either have fewer players, so each person needs to fill more than one role, or servers which like to give more access, so players can't hide forever in their super secure departments (I'm looking at you, chemistry!) - /// Innate skill levels unlocked at roundstart. Based on config.jobs_have_minimal_access config setting, for example with a skeleton crew. Format is list(/datum/skill/foo = SKILL_EXP_NOVICE) with exp as an integer or as per code/_DEFINES/skills.dm var/list/skills /// Innate skill levels unlocked at roundstart. Based on config.jobs_have_minimal_access config setting, for example with a full crew. Format is list(/datum/skill/foo = SKILL_EXP_NOVICE) with exp as an integer or as per code/_DEFINES/skills.dm @@ -76,18 +72,28 @@ /datum/job/New() . = ..() - var/list/jobs_changes = GetMapChanges() + var/list/jobs_changes = get_map_changes() if(!jobs_changes) return - if(isnum(jobs_changes["additional_access"])) - access += jobs_changes["additional_access"] - if(isnum(jobs_changes["additional_minimal_access"])) - minimal_access += jobs_changes["additional_minimal_access"] if(isnum(jobs_changes["spawn_positions"])) spawn_positions = jobs_changes["spawn_positions"] if(isnum(jobs_changes["total_positions"])) total_positions = jobs_changes["total_positions"] +/// Loads up map configs if necessary and returns job changes for this job. +/datum/job/proc/get_map_changes() + var/string_type = "[type]" + var/list/splits = splittext(string_type, "/") + var/endpart = splits[splits.len] + + SSmapping.HACK_LoadMapConfig() + + var/list/job_changes = SSmapping.config.job_changes + if(!(endpart in job_changes)) + return list() + + return job_changes[endpart] + //Only override this proc //H is usually a human unless an /equip override transformed it /datum/job/proc/after_spawn(mob/living/H, mob/M, latejoin = FALSE) @@ -121,7 +127,7 @@ for(var/i in roundstart_experience) experiencer.mind.adjust_experience(i, roundstart_experience[i], TRUE) -/datum/job/proc/announce(mob/living/carbon/human/H) +/datum/job/proc/announce(mob/living/carbon/human/H, announce_captaincy = FALSE) if(head_announce) announce_head(H, head_announce) @@ -138,7 +144,7 @@ return antag_rep //Don't override this unless the job transforms into a non-human (Silicons do this for example) -/datum/job/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, latejoin = FALSE, datum/outfit/outfit_override = null, client/preference_source) +/datum/job/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, latejoin = FALSE, datum/outfit/outfit_override = null, client/preference_source, is_captain = FALSE) if(!H) return FALSE if(CONFIG_GET(flag/enforce_human_authority) && (title in GLOB.command_positions)) @@ -156,24 +162,14 @@ if(outfit_override || outfit) H.equipOutfit(outfit_override ? outfit_override : outfit, visualsOnly) + if(!visualsOnly && is_captain) + var/is_acting_captain = (title != "Captain") + SSjob.promote_to_captain(H, is_acting_captain) + H.dna.species.after_equip_job(src, H, visualsOnly) if(!visualsOnly && announce) - announce(H) - -/datum/job/proc/get_access() - if(!config) //Needed for robots. - return src.minimal_access.Copy() - - . = list() - - if(CONFIG_GET(flag/jobs_have_minimal_access)) - . = src.minimal_access.Copy() - else - . = src.access.Copy() - - if(CONFIG_GET(flag/everyone_has_maint_access)) //Config has global maint access set - . |= list(ACCESS_MAINT_TUNNELS) + announce(H, is_captain) /datum/job/proc/announce_head(mob/living/carbon/human/H, channels) //tells the given channel that the given mob is the new department head. See communications.dm for valid channels. if(H && GLOB.announcement_systems.len) @@ -203,27 +199,11 @@ return TRUE /datum/job/proc/map_check() - var/list/job_changes = GetMapChanges() + var/list/job_changes = get_map_changes() if(!job_changes) return FALSE return TRUE -/** - * Gets the changes dictionary made to the job template by the map config. Returns null if job is removed. - */ -/datum/job/proc/GetMapChanges() - var/string_type = "[type]" - var/list/splits = splittext(string_type, "/") - var/endpart = splits[splits.len] - - SSmapping.HACK_LoadMapConfig() - - var/list/job_changes = SSmapping.config.job_changes - if(!(endpart in job_changes)) - return list() - - return job_changes[endpart] - /datum/job/proc/radio_help_message(mob/M) to_chat(M, "Prefix your message with :h to speak on your department's radio. To see other prefixes, look closely at your headset.") @@ -233,7 +213,7 @@ var/jobtype = null uniform = /obj/item/clothing/under/color/grey - id = /obj/item/card/id + id = /obj/item/card/id/advanced ears = /obj/item/radio/headset belt = /obj/item/pda back = /obj/item/storage/backpack @@ -283,13 +263,12 @@ var/obj/item/card/id/C = H.wear_id if(istype(C)) - C.access = J.get_access() shuffle_inplace(C.access) // Shuffle access list to make NTNet passkeys less predictable C.registered_name = H.real_name - C.assignment = J.title if(H.age) C.registered_age = H.age C.update_label() + C.update_icon() var/datum/bank_account/B = SSeconomy.bank_accounts_by_id["[H.account_id]"] if(B && B.account_id == H.account_id) C.registered_account = B @@ -313,9 +292,3 @@ types += satchel types += duffelbag return types - -//Warden and regular officers add this result to their get_access() -/datum/job/proc/check_config_for_sec_maint() - if(CONFIG_GET(flag/security_has_maint_access)) - return list(ACCESS_MAINT_TUNNELS) - return list() diff --git a/code/modules/jobs/job_types/assistant.dm b/code/modules/jobs/job_types/assistant.dm index 5097a756552..fc84ab2d867 100644 --- a/code/modules/jobs/job_types/assistant.dm +++ b/code/modules/jobs/job_types/assistant.dm @@ -8,8 +8,6 @@ Assistant spawn_positions = 5 supervisors = "absolutely everyone" selection_color = "#dddddd" - access = list() //See /datum/job/assistant/get_access() - minimal_access = list() //See /datum/job/assistant/get_access() outfit = /datum/outfit/job/assistant antag_rep = 7 paycheck = PAYCHECK_ASSISTANT // Get a job. Job reassignment changes your paycheck now. Get over it. @@ -20,16 +18,10 @@ Assistant paycheck_department = ACCOUNT_CIV display_order = JOB_DISPLAY_ORDER_ASSISTANT -/datum/job/assistant/get_access() - if(CONFIG_GET(flag/assistants_have_maint_access) || !CONFIG_GET(flag/jobs_have_minimal_access)) //Config has assistant maint access set - . = ..() - . |= list(ACCESS_MAINT_TUNNELS) - else - return ..() - /datum/outfit/job/assistant name = "Assistant" jobtype = /datum/job/assistant + id_trim = /datum/id_trim/job/assistant /datum/outfit/job/assistant/pre_equip(mob/living/carbon/human/H) ..() diff --git a/code/modules/jobs/job_types/atmospheric_technician.dm b/code/modules/jobs/job_types/atmospheric_technician.dm index f41358c0cd4..c42726ac58f 100644 --- a/code/modules/jobs/job_types/atmospheric_technician.dm +++ b/code/modules/jobs/job_types/atmospheric_technician.dm @@ -1,4 +1,4 @@ -/datum/job/atmos +/datum/job/atmospheric_technician title = "Atmospheric Technician" department_head = list("Chief Engineer") faction = "Station" @@ -11,9 +11,6 @@ outfit = /datum/outfit/job/atmos - access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_MECH_ENGINE, ACCESS_AUX_BASE, - ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_ATMOSPHERICS, ACCESS_MAINT_TUNNELS, ACCESS_AUX_BASE, ACCESS_CONSTRUCTION, ACCESS_MECH_ENGINE, ACCESS_MINERAL_STOREROOM) paycheck = PAYCHECK_MEDIUM paycheck_department = ACCOUNT_ENG @@ -25,7 +22,7 @@ /datum/outfit/job/atmos name = "Atmospheric Technician" - jobtype = /datum/job/atmos + jobtype = /datum/job/atmospheric_technician belt = /obj/item/storage/belt/utility/atmostech l_pocket = /obj/item/pda/atmos @@ -40,6 +37,8 @@ pda_slot = ITEM_SLOT_LPOCKET backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced/atmos=1) + id_trim = /datum/id_trim/job/atmospheric_technician + /datum/outfit/job/atmos/rig name = "Atmospheric Technician (Hardsuit)" diff --git a/code/modules/jobs/job_types/bartender.dm b/code/modules/jobs/job_types/bartender.dm index 9ac6fce45ca..00b261e8e3d 100644 --- a/code/modules/jobs/job_types/bartender.dm +++ b/code/modules/jobs/job_types/bartender.dm @@ -9,8 +9,6 @@ outfit = /datum/outfit/job/bartender - access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM, ACCESS_THEATRE) - minimal_access = list(ACCESS_BAR, ACCESS_MINERAL_STOREROOM, ACCESS_THEATRE) paycheck = PAYCHECK_EASY paycheck_department = ACCOUNT_SRV display_order = JOB_DISPLAY_ORDER_BARTENDER @@ -28,6 +26,7 @@ suit = /obj/item/clothing/suit/armor/vest backpack_contents = list(/obj/item/storage/box/beanbag=1) shoes = /obj/item/clothing/shoes/laceup + id_trim = /datum/id_trim/job/bartender /datum/outfit/job/bartender/post_equip(mob/living/carbon/human/H, visualsOnly) . = ..() diff --git a/code/modules/jobs/job_types/botanist.dm b/code/modules/jobs/job_types/botanist.dm index 5ae2c63b6fb..29f37048475 100644 --- a/code/modules/jobs/job_types/botanist.dm +++ b/code/modules/jobs/job_types/botanist.dm @@ -1,4 +1,4 @@ -/datum/job/hydro +/datum/job/botanist title = "Botanist" department_head = list("Head of Personnel") faction = "Station" @@ -9,8 +9,6 @@ outfit = /datum/outfit/job/botanist - access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_HYDROPONICS, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM) paycheck = PAYCHECK_EASY paycheck_department = ACCOUNT_SRV display_order = JOB_DISPLAY_ORDER_BOTANIST @@ -19,7 +17,7 @@ /datum/outfit/job/botanist name = "Botanist" - jobtype = /datum/job/hydro + jobtype = /datum/job/botanist belt = /obj/item/pda/botanist ears = /obj/item/radio/headset/headset_srv @@ -32,3 +30,4 @@ satchel = /obj/item/storage/backpack/satchel/hyd duffelbag = /obj/item/storage/backpack/duffelbag/hydroponics + id_trim = /datum/id_trim/job/botanist diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm old mode 100755 new mode 100644 index 21fa555fbb3..2603e8ff151 --- a/code/modules/jobs/job_types/captain.dm +++ b/code/modules/jobs/job_types/captain.dm @@ -15,8 +15,6 @@ outfit = /datum/outfit/job/captain - access = list() //See get_access() - minimal_access = list() //See get_access() paycheck = PAYCHECK_COMMAND paycheck_department = ACCOUNT_SEC @@ -25,18 +23,16 @@ display_order = JOB_DISPLAY_ORDER_CAPTAIN departments = DEPARTMENT_COMMAND -/datum/job/captain/get_access() - return get_all_accesses() - -/datum/job/captain/announce(mob/living/carbon/human/H) +/datum/job/captain/announce(mob/living/carbon/human/H, announce_captaincy = TRUE) ..() - SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "Captain [H.real_name] on deck!")) + if(announce_captaincy) + SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "Captain [H.real_name] on deck!")) /datum/outfit/job/captain name = "Captain" jobtype = /datum/job/captain - id = /obj/item/card/id/gold + id = /obj/item/card/id/advanced/gold belt = /obj/item/pda/captain glasses = /obj/item/clothing/glasses/sunglasses ears = /obj/item/radio/headset/heads/captain/alt @@ -58,6 +54,8 @@ chameleon_extras = list(/obj/item/gun/energy/e_gun, /obj/item/stamp/captain) + id_trim = /datum/id_trim/job/captain + /datum/outfit/job/captain/hardsuit name = "Captain (Hardsuit)" diff --git a/code/modules/jobs/job_types/cargo_technician.dm b/code/modules/jobs/job_types/cargo_technician.dm index fe3420953f8..f124da49d72 100644 --- a/code/modules/jobs/job_types/cargo_technician.dm +++ b/code/modules/jobs/job_types/cargo_technician.dm @@ -1,4 +1,4 @@ -/datum/job/cargo_tech +/datum/job/cargo_technician title = "Cargo Technician" department_head = list("Head of Personnel") faction = "Station" @@ -9,8 +9,6 @@ outfit = /datum/outfit/job/cargo_tech - access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_QM, ACCESS_MINING, ACCESS_MECH_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_CARGO, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM, ACCESS_MECH_MINING) paycheck = PAYCHECK_EASY paycheck_department = ACCOUNT_CAR display_order = JOB_DISPLAY_ORDER_CARGO_TECHNICIAN @@ -19,10 +17,11 @@ /datum/outfit/job/cargo_tech name = "Cargo Technician" - jobtype = /datum/job/cargo_tech + jobtype = /datum/job/cargo_technician belt = /obj/item/pda/cargo ears = /obj/item/radio/headset/headset_cargo uniform = /obj/item/clothing/under/rank/cargo/tech l_hand = /obj/item/export_scanner backpack_contents = list(/obj/item/modular_computer/tablet/preset/cargo=1) + id_trim = /datum/id_trim/job/cargo_technician diff --git a/code/modules/jobs/job_types/chaplain.dm b/code/modules/jobs/job_types/chaplain.dm index 613d38ef8de..3f2da02d779 100644 --- a/code/modules/jobs/job_types/chaplain.dm +++ b/code/modules/jobs/job_types/chaplain.dm @@ -9,8 +9,6 @@ outfit = /datum/outfit/job/chaplain - access = list(ACCESS_MORGUE, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_THEATRE) - minimal_access = list(ACCESS_MORGUE, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_THEATRE) paycheck = PAYCHECK_EASY paycheck_department = ACCOUNT_SRV @@ -136,3 +134,5 @@ satchel = /obj/item/storage/backpack/cultpack chameleon_extras = /obj/item/stamp/chap + + id_trim = /datum/id_trim/job/chaplain diff --git a/code/modules/jobs/job_types/chemist.dm b/code/modules/jobs/job_types/chemist.dm index 758877f1756..721d7019645 100644 --- a/code/modules/jobs/job_types/chemist.dm +++ b/code/modules/jobs/job_types/chemist.dm @@ -11,8 +11,6 @@ outfit = /datum/outfit/job/chemist - access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_PHARMACY, ACCESS_VIROLOGY, ACCESS_MECH_MEDICAL, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_CHEMISTRY, ACCESS_MECH_MEDICAL, ACCESS_MINERAL_STOREROOM, ACCESS_PHARMACY) paycheck = PAYCHECK_MEDIUM paycheck_department = ACCOUNT_MED @@ -41,3 +39,5 @@ chameleon_extras = /obj/item/gun/syringe + id_trim = /datum/id_trim/job/chemist + diff --git a/code/modules/jobs/job_types/chief_engineer.dm b/code/modules/jobs/job_types/chief_engineer.dm index 4eae471579e..8c7e0297511 100644 --- a/code/modules/jobs/job_types/chief_engineer.dm +++ b/code/modules/jobs/job_types/chief_engineer.dm @@ -17,14 +17,6 @@ outfit = /datum/outfit/job/ce departments = DEPARTMENT_ENGINEERING | DEPARTMENT_COMMAND - access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, - ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ATMOSPHERICS, ACCESS_EVA, ACCESS_AUX_BASE, - ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS, ACCESS_MINISAT, ACCESS_MECH_ENGINE, - ACCESS_CE, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINERAL_STOREROOM, ACCESS_TELEPORTER) - minimal_access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, - ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ATMOSPHERICS, ACCESS_EVA, ACCESS_AUX_BASE, - ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS, ACCESS_MINISAT, ACCESS_MECH_ENGINE, - ACCESS_CE, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINERAL_STOREROOM) paycheck = PAYCHECK_COMMAND paycheck_department = ACCOUNT_ENG @@ -33,11 +25,16 @@ display_order = JOB_DISPLAY_ORDER_CHIEF_ENGINEER bounty_types = CIV_JOB_ENG +/datum/job/chief_engineer/announce(mob/living/carbon/human/H, announce_captaincy = FALSE) + ..() + if(announce_captaincy) + SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "Due to staffing shortages, newly promoted Acting Captain [H.real_name] on deck!")) + /datum/outfit/job/ce name = "Chief Engineer" jobtype = /datum/job/chief_engineer - id = /obj/item/card/id/silver + id = /obj/item/card/id/advanced/silver belt = /obj/item/storage/belt/utility/chief/full l_pocket = /obj/item/pda/heads/ce ears = /obj/item/radio/headset/heads/ce @@ -56,6 +53,8 @@ skillchips = list(/obj/item/skillchip/job/engineer) + id_trim = /datum/id_trim/job/chief_engineer + /datum/outfit/job/ce/rig name = "Chief Engineer (Hardsuit)" diff --git a/code/modules/jobs/job_types/chief_medical_officer.dm b/code/modules/jobs/job_types/chief_medical_officer.dm index d4f7af74494..bf0e4f4a671 100644 --- a/code/modules/jobs/job_types/chief_medical_officer.dm +++ b/code/modules/jobs/job_types/chief_medical_officer.dm @@ -1,4 +1,4 @@ -/datum/job/cmo +/datum/job/chief_medical_officer title = "Chief Medical Officer" department_head = list("Captain") auto_deadmin_role_flags = DEADMIN_POSITION_HEAD @@ -17,12 +17,6 @@ outfit = /datum/outfit/job/cmo departments = DEPARTMENT_MEDICAL | DEPARTMENT_COMMAND - access = list(ACCESS_MEDICAL, ACCESS_PSYCHOLOGY, ACCESS_MORGUE, ACCESS_PHARMACY, ACCESS_HEADS, ACCESS_MINERAL_STOREROOM, - ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE, ACCESS_MECH_MEDICAL, - ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_MAINT_TUNNELS, ACCESS_EVA, ACCESS_TELEPORTER) - minimal_access = list(ACCESS_MEDICAL, ACCESS_PSYCHOLOGY, ACCESS_MORGUE, ACCESS_PHARMACY, ACCESS_HEADS, ACCESS_MINERAL_STOREROOM, - ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE, ACCESS_MECH_MEDICAL, - ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_MAINT_TUNNELS, ACCESS_EVA) paycheck = PAYCHECK_COMMAND paycheck_department = ACCOUNT_MED @@ -31,11 +25,16 @@ display_order = JOB_DISPLAY_ORDER_CHIEF_MEDICAL_OFFICER bounty_types = CIV_JOB_MED +/datum/job/chief_medical_officer/announce(mob/living/carbon/human/H, announce_captaincy = FALSE) + ..() + if(announce_captaincy) + SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "Due to staffing shortages, newly promoted Acting Captain [H.real_name] on deck!")) + /datum/outfit/job/cmo name = "Chief Medical Officer" - jobtype = /datum/job/cmo + jobtype = /datum/job/chief_medical_officer - id = /obj/item/card/id/silver + id = /obj/item/card/id/advanced/silver belt = /obj/item/pda/heads/cmo l_pocket = /obj/item/pinpointer/crew ears = /obj/item/radio/headset/heads/cmo @@ -55,6 +54,8 @@ chameleon_extras = list(/obj/item/gun/syringe, /obj/item/stamp/cmo) + id_trim = /datum/id_trim/job/chief_medical_officer + /datum/outfit/job/cmo/hardsuit name = "Chief Medical Officer (Hardsuit)" diff --git a/code/modules/jobs/job_types/clown.dm b/code/modules/jobs/job_types/clown.dm index 7382d5864e2..6aa04273f56 100644 --- a/code/modules/jobs/job_types/clown.dm +++ b/code/modules/jobs/job_types/clown.dm @@ -9,8 +9,6 @@ outfit = /datum/outfit/job/clown - access = list(ACCESS_THEATRE) - minimal_access = list(ACCESS_THEATRE) paycheck = PAYCHECK_MINIMAL paycheck_department = ACCOUNT_SRV @@ -51,12 +49,13 @@ chameleon_extras = /obj/item/stamp/clown + id_trim = /datum/id_trim/job/clown + /datum/outfit/job/clown/pre_equip(mob/living/carbon/human/H, visualsOnly) . = ..() if(HAS_TRAIT(SSstation, STATION_TRAIT_BANANIUM_SHIPMENTS)) backpack_contents[/obj/item/stack/sheet/mineral/bananium/five] = 1 - /datum/outfit/job/clown/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) ..() if(visualsOnly) diff --git a/code/modules/jobs/job_types/cook.dm b/code/modules/jobs/job_types/cook.dm index cd39a3d43e4..a4085b8e7a2 100644 --- a/code/modules/jobs/job_types/cook.dm +++ b/code/modules/jobs/job_types/cook.dm @@ -10,8 +10,6 @@ outfit = /datum/outfit/job/cook - access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM) paycheck = PAYCHECK_EASY paycheck_department = ACCOUNT_SRV @@ -37,6 +35,8 @@ ) skillchips = list(/obj/item/skillchip/job/chef) + id_trim = /datum/id_trim/job/cook + /datum/outfit/job/cook/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) ..() var/datum/job/cook/J = SSjob.GetJobType(jobtype) diff --git a/code/modules/jobs/job_types/curator.dm b/code/modules/jobs/job_types/curator.dm index 8f4834e0c28..bc11268c004 100644 --- a/code/modules/jobs/job_types/curator.dm +++ b/code/modules/jobs/job_types/curator.dm @@ -9,8 +9,6 @@ outfit = /datum/outfit/job/curator - access = list(ACCESS_LIBRARY, ACCESS_AUX_BASE, ACCESS_MINING_STATION) - minimal_access = list(ACCESS_LIBRARY, ACCESS_AUX_BASE, ACCESS_MINING_STATION) paycheck = PAYCHECK_EASY paycheck_department = ACCOUNT_SRV @@ -35,6 +33,8 @@ /obj/item/barcodescanner = 1 ) + id_trim = /datum/id_trim/job/curator + /datum/outfit/job/curator/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) ..() diff --git a/code/modules/jobs/job_types/detective.dm b/code/modules/jobs/job_types/detective.dm index 449a9c97a38..5b085edeb45 100644 --- a/code/modules/jobs/job_types/detective.dm +++ b/code/modules/jobs/job_types/detective.dm @@ -14,8 +14,6 @@ outfit = /datum/outfit/job/detective departments = DEPARTMENT_SECURITY - access = list(ACCESS_SEC_DOORS, ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_MECH_SECURITY, ACCESS_COURT, ACCESS_BRIG, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_SEC_DOORS, ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_MECH_SECURITY, ACCESS_COURT, ACCESS_BRIG, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) paycheck = PAYCHECK_MEDIUM paycheck_department = ACCOUNT_SEC @@ -47,6 +45,8 @@ chameleon_extras = list(/obj/item/gun/ballistic/revolver/detective, /obj/item/clothing/glasses/sunglasses) + id_trim = /datum/id_trim/job/detective + /datum/outfit/job/detective/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) ..() var/obj/item/clothing/mask/cigarette/cig = H.wear_mask diff --git a/code/modules/jobs/job_types/geneticist.dm b/code/modules/jobs/job_types/geneticist.dm index acf84f77b03..77eae91e6bd 100644 --- a/code/modules/jobs/job_types/geneticist.dm +++ b/code/modules/jobs/job_types/geneticist.dm @@ -12,8 +12,6 @@ outfit = /datum/outfit/job/geneticist departments = DEPARTMENT_MEDICAL - access = list(ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_MECH_SCIENCE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_ROBOTICS, ACCESS_MINERAL_STOREROOM, ACCESS_TECH_STORAGE, ACCESS_RND) - minimal_access = list(ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_MECH_SCIENCE, ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM, ACCESS_RND) paycheck = PAYCHECK_MEDIUM paycheck_department = ACCOUNT_SCI @@ -35,3 +33,5 @@ backpack = /obj/item/storage/backpack/genetics satchel = /obj/item/storage/backpack/satchel/gen duffelbag = /obj/item/storage/backpack/duffelbag/genetics + + id_trim = /datum/id_trim/job/geneticist diff --git a/code/modules/jobs/job_types/head_of_personnel.dm b/code/modules/jobs/job_types/head_of_personnel.dm index 002e932e739..9a16df83d73 100644 --- a/code/modules/jobs/job_types/head_of_personnel.dm +++ b/code/modules/jobs/job_types/head_of_personnel.dm @@ -1,4 +1,4 @@ -/datum/job/hop +/datum/job/head_of_personnel title = "Head of Personnel" auto_deadmin_role_flags = DEADMIN_POSITION_HEAD department_head = list("Captain") @@ -17,20 +17,6 @@ outfit = /datum/outfit/job/hop departments = DEPARTMENT_COMMAND | DEPARTMENT_SERVICE - access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_COURT, ACCESS_WEAPONS, - ACCESS_MEDICAL, ACCESS_PSYCHOLOGY, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_EVA, ACCESS_HEADS, - ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, - ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, - ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_VAULT, ACCESS_MINING_STATION, - ACCESS_MECH_MINING, ACCESS_MECH_ENGINE, ACCESS_MECH_SCIENCE, ACCESS_MECH_SECURITY, ACCESS_MECH_MEDICAL, - ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM, ACCESS_AUX_BASE, ACCESS_TELEPORTER) - minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_COURT, ACCESS_WEAPONS, - ACCESS_MEDICAL, ACCESS_PSYCHOLOGY, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_EVA, ACCESS_HEADS, - ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, - ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, - ACCESS_MECH_MINING, ACCESS_MECH_ENGINE, ACCESS_MECH_SCIENCE, ACCESS_MECH_SECURITY, ACCESS_MECH_MEDICAL, - ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_VAULT, ACCESS_MINING_STATION, - ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM, ACCESS_AUX_BASE, ACCESS_TELEPORTER) paycheck = PAYCHECK_COMMAND paycheck_department = ACCOUNT_SRV bounty_types = CIV_JOB_RANDOM @@ -39,11 +25,16 @@ display_order = JOB_DISPLAY_ORDER_HEAD_OF_PERSONNEL +/datum/job/head_of_personnel/announce(mob/living/carbon/human/H, announce_captaincy = FALSE) + ..() + if(announce_captaincy) + SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "Due to staffing shortages, newly promoted Acting Captain [H.real_name] on deck!")) + /datum/outfit/job/hop name = "Head of Personnel" - jobtype = /datum/job/hop + jobtype = /datum/job/head_of_personnel - id = /obj/item/card/id/silver + id = /obj/item/card/id/advanced/silver belt = /obj/item/pda/heads/hop ears = /obj/item/radio/headset/heads/hop uniform = /obj/item/clothing/under/rank/civilian/head_of_personnel @@ -54,8 +45,33 @@ chameleon_extras = list(/obj/item/gun/energy/e_gun, /obj/item/stamp/hop) + id_trim = /datum/id_trim/job/head_of_personnel + /datum/outfit/job/hop/pre_equip(mob/living/carbon/human/H) ..() if(locate(/datum/holiday/ianbirthday) in SSevents.holidays) undershirt = /datum/sprite_accessory/undershirt/ian + +/obj/item/paper/fluff/ids_for_dummies + name = "Memo: New IDs and You" + desc = "It looks like this was left by the last Head of Personnel to man this station. It explains some information about new IDs." + info = {" +

Dummy's Guide To New IDs

+

The Basics

+

Card Trim - This is the job assigned to the card. The card's trim decides what Basic accesses the card can hold. Basic accesses cost nothing! Grey ID cards cannot hold Head of Staff or Captain trims. Silver ID cards can hold Head of Staff trims but not Captain trims and are in a box in the Head of Personnel's office and orderable from cargo. Gold ID cards can hold all access. The only guaranteed Gold ID card is the Captain's Spare, held in a golden safe on the bridge with access codes given to the station's highest ranking officer. All other gold ID cards are carried exclusively by Captains.

+

Wildcards - These are any additional accesses a card has that are not part of the card's trim. Lower quality ID cards have fewer wildcards and the wildcards they do have are of lower rarity.

+

Job Changes - To change a job, you need to go to the PDA & ID Painter that's in every Head of Personnel office. This can be used to apply a new trim to an ID card, but this will wipe all that card's accesses in the process. You then take this ID card to any modular computer with the Plexus Access Management app and when logged in with the appropriate Head of Staff or ID Console access can then select from Templates to quick-fill accesses or apply them manually.

+

Firing Staff - Terminating a staff member's employment with wipe any trim from their card, remove all access and instantly set them as demoted.

+

Changing Jobs - Step by Step

+
    +
  1. Grab an appropriate ID card. Head of Staff jobs require a silver ID card. Captain requires a gold ID card.
  2. +
  3. Insert the ID card into the combined PDA Painter and ID Trimmer in the HoP office.
  4. +
  5. Select to appropriate trim then hit the button to apply it to the card. This will wipe all the card's access.
  6. +
  7. Remove the ID from the PDA/ID Painter and open up any modular computer with the Plexagon Access Management application downloaded.
  8. +
  9. Login to the app using an ID card with any Head of Staff private office access for limited access or the ID Console access for unlimited access.
  10. +
  11. Select a template from the drop-down. This will apply as many basic accesses as possible based on the trim of the ID card and may apply wildcard accesses for Head of Staff and Captain trims. For best results, match the template to the ID card's trim.
  12. +
  13. Manually tweak any other accesses as necessary. Add wildcard accesses. Tweak basic accesses.
  14. +
  15. Don't forget to set a custom occupation! SecHUDs now interface direct with the ID card's trim and display the trim's job icon even when a custom assignment is set.
  16. +
+ "} diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm index b5fdd8bee17..a67b21ab587 100644 --- a/code/modules/jobs/job_types/head_of_security.dm +++ b/code/modules/jobs/job_types/head_of_security.dm @@ -1,4 +1,4 @@ -/datum/job/hos +/datum/job/head_of_security title = "Head of Security" auto_deadmin_role_flags = DEADMIN_POSITION_HEAD|DEADMIN_POSITION_SECURITY department_head = list("Captain") @@ -20,25 +20,22 @@ mind_traits = list(TRAIT_DONUT_LOVER) liver_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM, TRAIT_ROYAL_METABOLISM) - access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_MECH_SECURITY, - ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_AUX_BASE, - ACCESS_RESEARCH, ACCESS_ENGINE, ACCESS_MINING, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_MAILSORTING, ACCESS_EVA, ACCESS_TELEPORTER, - ACCESS_HEADS, ACCESS_HOS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_MECH_SECURITY, - ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_AUX_BASE, - ACCESS_RESEARCH, ACCESS_ENGINE, ACCESS_MINING, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_MAILSORTING, ACCESS_EVA, - ACCESS_HEADS, ACCESS_HOS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) paycheck = PAYCHECK_COMMAND paycheck_department = ACCOUNT_SEC display_order = JOB_DISPLAY_ORDER_HEAD_OF_SECURITY bounty_types = CIV_JOB_SEC +/datum/job/head_of_security/announce(mob/living/carbon/human/H, announce_captaincy = FALSE) + ..() + if(announce_captaincy) + SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "Due to staffing shortages, newly promoted Acting Captain [H.real_name] on deck!")) + /datum/outfit/job/hos name = "Head of Security" - jobtype = /datum/job/hos + jobtype = /datum/job/head_of_security - id = /obj/item/card/id/silver + id = /obj/item/card/id/advanced/silver belt = /obj/item/pda/heads/hos ears = /obj/item/radio/headset/heads/hos/alt uniform = /obj/item/clothing/under/rank/security/head_of_security @@ -61,6 +58,8 @@ chameleon_extras = list(/obj/item/gun/energy/e_gun/hos, /obj/item/stamp/hos) + id_trim = /datum/id_trim/job/head_of_security + /datum/outfit/job/hos/hardsuit name = "Head of Security (Hardsuit)" diff --git a/code/modules/jobs/job_types/janitor.dm b/code/modules/jobs/job_types/janitor.dm index bfb108eab53..1db8a299a4e 100644 --- a/code/modules/jobs/job_types/janitor.dm +++ b/code/modules/jobs/job_types/janitor.dm @@ -9,8 +9,6 @@ outfit = /datum/outfit/job/janitor - access = list(ACCESS_JANITOR, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_JANITOR, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) paycheck = PAYCHECK_EASY paycheck_department = ACCOUNT_SRV @@ -26,6 +24,8 @@ uniform = /obj/item/clothing/under/rank/civilian/janitor backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced=1) + id_trim = /datum/id_trim/job/janitor + /datum/outfit/job/janitor/pre_equip(mob/living/carbon/human/H, visualsOnly) . = ..() if(GARBAGEDAY in SSevents.holidays) diff --git a/code/modules/jobs/job_types/lawyer.dm b/code/modules/jobs/job_types/lawyer.dm index a4dfbfcd3a4..73fd263e5be 100644 --- a/code/modules/jobs/job_types/lawyer.dm +++ b/code/modules/jobs/job_types/lawyer.dm @@ -10,8 +10,6 @@ outfit = /datum/outfit/job/lawyer - access = list(ACCESS_LAWYER, ACCESS_COURT, ACCESS_SEC_DOORS) - minimal_access = list(ACCESS_LAWYER, ACCESS_COURT, ACCESS_SEC_DOORS) paycheck = PAYCHECK_EASY paycheck_department = ACCOUNT_SRV @@ -36,6 +34,7 @@ chameleon_extras = /obj/item/stamp/law + id_trim = /datum/id_trim/job/lawyer /datum/outfit/job/lawyer/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) if(visualsOnly) diff --git a/code/modules/jobs/job_types/medical_doctor.dm b/code/modules/jobs/job_types/medical_doctor.dm index ce74e3aa463..993d811d106 100644 --- a/code/modules/jobs/job_types/medical_doctor.dm +++ b/code/modules/jobs/job_types/medical_doctor.dm @@ -9,8 +9,6 @@ outfit = /datum/outfit/job/doctor - access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_PHARMACY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_MECH_MEDICAL, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_MECH_MEDICAL, ACCESS_MINERAL_STOREROOM, ACCESS_PHARMACY) paycheck = PAYCHECK_MEDIUM paycheck_department = ACCOUNT_MED @@ -40,3 +38,5 @@ skillchips = list(/obj/item/skillchip/entrails_reader, /obj/item/skillchip/quickcarry) chameleon_extras = /obj/item/gun/syringe + + id_trim = /datum/id_trim/job/medical_doctor diff --git a/code/modules/jobs/job_types/mime.dm b/code/modules/jobs/job_types/mime.dm index e6b7dd647de..a142cd58cba 100644 --- a/code/modules/jobs/job_types/mime.dm +++ b/code/modules/jobs/job_types/mime.dm @@ -9,8 +9,6 @@ outfit = /datum/outfit/job/mime - access = list(ACCESS_THEATRE) - minimal_access = list(ACCESS_THEATRE) paycheck = PAYCHECK_MINIMAL paycheck_department = ACCOUNT_SRV @@ -42,6 +40,8 @@ chameleon_extras = /obj/item/stamp/mime + id_trim = /datum/id_trim/job/mime + /datum/outfit/job/mime/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) ..() diff --git a/code/modules/jobs/job_types/paramedic.dm b/code/modules/jobs/job_types/paramedic.dm index 96dfaf37d1e..0daf982d7c3 100644 --- a/code/modules/jobs/job_types/paramedic.dm +++ b/code/modules/jobs/job_types/paramedic.dm @@ -9,10 +9,6 @@ outfit = /datum/outfit/job/paramedic - access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_MECH_MEDICAL, ACCESS_MINERAL_STOREROOM, ACCESS_MAINT_TUNNELS, - ACCESS_EVA, ACCESS_ENGINE, ACCESS_CONSTRUCTION, ACCESS_CARGO, ACCESS_HYDROPONICS, ACCESS_RESEARCH, ACCESS_AUX_BASE) - minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_MECH_MEDICAL, ACCESS_MINERAL_STOREROOM, ACCESS_MAINT_TUNNELS, ACCESS_EVA, - ACCESS_ENGINE, ACCESS_CONSTRUCTION, ACCESS_CARGO, ACCESS_HYDROPONICS, ACCESS_RESEARCH, ACCESS_AUX_BASE) paycheck = PAYCHECK_MEDIUM paycheck_department = ACCOUNT_MED @@ -33,7 +29,7 @@ suit = /obj/item/clothing/suit/toggle/labcoat/paramedic gloves = /obj/item/clothing/gloves/color/latex/nitrile belt = /obj/item/storage/belt/medical/paramedic - id = /obj/item/card/id + id = /obj/item/card/id/advanced l_pocket = /obj/item/pda/medical suit_store = /obj/item/flashlight/pen/paramedic backpack_contents = list(/obj/item/roller=1) @@ -47,3 +43,5 @@ box = /obj/item/storage/box/survival/medical chameleon_extras = /obj/item/gun/syringe + + id_trim = /datum/id_trim/job/paramedic diff --git a/code/modules/jobs/job_types/prisoner.dm b/code/modules/jobs/job_types/prisoner.dm index 0b79464487f..7c7a879f2d9 100644 --- a/code/modules/jobs/job_types/prisoner.dm +++ b/code/modules/jobs/job_types/prisoner.dm @@ -17,6 +17,8 @@ uniform = /obj/item/clothing/under/rank/prisoner shoes = /obj/item/clothing/shoes/sneakers/orange - id = /obj/item/card/id/prisoner + id = /obj/item/card/id/advanced/prisoner ears = null belt = null + + id_trim = /datum/id_trim/job/prisoner diff --git a/code/modules/jobs/job_types/psychologist.dm b/code/modules/jobs/job_types/psychologist.dm index 5a117db8a1f..59df39cfc72 100644 --- a/code/modules/jobs/job_types/psychologist.dm +++ b/code/modules/jobs/job_types/psychologist.dm @@ -9,8 +9,6 @@ outfit = /datum/outfit/job/psychologist - access = list(ACCESS_MEDICAL, ACCESS_PSYCHOLOGY) - minimal_access = list(ACCESS_MEDICAL, ACCESS_PSYCHOLOGY) paycheck = PAYCHECK_MEDIUM paycheck_department = ACCOUNT_SRV @@ -26,7 +24,7 @@ ears = /obj/item/radio/headset/headset_srvmed uniform = /obj/item/clothing/under/suit/black shoes = /obj/item/clothing/shoes/laceup - id = /obj/item/card/id + id = /obj/item/card/id/advanced belt = /obj/item/pda/medical pda_slot = ITEM_SLOT_BELT l_hand = /obj/item/clipboard @@ -38,3 +36,5 @@ backpack = /obj/item/storage/backpack/medic satchel = /obj/item/storage/backpack/satchel/med duffelbag = /obj/item/storage/backpack/duffelbag/med + + id_trim = /datum/id_trim/job/psychologist diff --git a/code/modules/jobs/job_types/quartermaster.dm b/code/modules/jobs/job_types/quartermaster.dm index 3a0ef3d216d..4a9eadc734c 100644 --- a/code/modules/jobs/job_types/quartermaster.dm +++ b/code/modules/jobs/job_types/quartermaster.dm @@ -1,4 +1,4 @@ -/datum/job/qm +/datum/job/quartermaster title = "Quartermaster" department_head = list("Head of Personnel") faction = "Station" @@ -10,8 +10,6 @@ outfit = /datum/outfit/job/quartermaster - access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_QM, ACCESS_MINING, ACCESS_MECH_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_VAULT, ACCESS_AUX_BASE) - minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_QM, ACCESS_MINING, ACCESS_MECH_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_VAULT, ACCESS_AUX_BASE) paycheck = PAYCHECK_MEDIUM paycheck_department = ACCOUNT_CAR @@ -21,9 +19,14 @@ bounty_types = CIV_JOB_RANDOM departments = DEPARTMENT_CARGO +/datum/job/quartermaster/announce(mob/living/carbon/human/H, announce_captaincy = FALSE) + ..() + if(announce_captaincy) + SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "Due to extreme staffing shortages, newly promoted Acting Captain [H.real_name] on deck!")) + /datum/outfit/job/quartermaster name = "Quartermaster" - jobtype = /datum/job/qm + jobtype = /datum/job/quartermaster belt = /obj/item/pda/quartermaster ears = /obj/item/radio/headset/headset_cargo @@ -35,3 +38,4 @@ chameleon_extras = /obj/item/stamp/qm + id_trim = /datum/id_trim/job/quartermaster diff --git a/code/modules/jobs/job_types/research_director.dm b/code/modules/jobs/job_types/research_director.dm index 047f801e1ad..fd50251fd2a 100644 --- a/code/modules/jobs/job_types/research_director.dm +++ b/code/modules/jobs/job_types/research_director.dm @@ -1,4 +1,4 @@ -/datum/job/rd +/datum/job/research_director title = "Research Director" auto_deadmin_role_flags = DEADMIN_POSITION_HEAD department_head = list("Captain") @@ -17,29 +17,22 @@ outfit = /datum/outfit/job/rd departments = DEPARTMENT_SCIENCE | DEPARTMENT_COMMAND - access = list(ACCESS_RD, ACCESS_HEADS, ACCESS_RND, ACCESS_GENETICS, ACCESS_MORGUE, - ACCESS_TOXINS, ACCESS_TELEPORTER, ACCESS_SEC_DOORS, ACCESS_MECH_SCIENCE, - ACCESS_RESEARCH, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_AI_UPLOAD, - ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM, - ACCESS_TECH_STORAGE, ACCESS_MINISAT, ACCESS_MAINT_TUNNELS, ACCESS_NETWORK, - ACCESS_TOXINS_STORAGE, ACCESS_AUX_BASE, ACCESS_EVA) - minimal_access = list(ACCESS_RD, ACCESS_HEADS, ACCESS_RND, ACCESS_GENETICS, ACCESS_MORGUE, - ACCESS_TOXINS, ACCESS_TELEPORTER, ACCESS_SEC_DOORS, ACCESS_MECH_SCIENCE, - ACCESS_RESEARCH, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_AI_UPLOAD, - ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM, - ACCESS_TECH_STORAGE, ACCESS_MINISAT, ACCESS_MAINT_TUNNELS, ACCESS_NETWORK, - ACCESS_TOXINS_STORAGE, ACCESS_AUX_BASE, ACCESS_EVA) paycheck = PAYCHECK_COMMAND paycheck_department = ACCOUNT_SCI display_order = JOB_DISPLAY_ORDER_RESEARCH_DIRECTOR bounty_types = CIV_JOB_SCI +/datum/job/research_director/announce(mob/living/carbon/human/H, announce_captaincy = FALSE) + ..() + if(announce_captaincy) + SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "Due to staffing shortages, newly promoted Acting Captain [H.real_name] on deck!")) + /datum/outfit/job/rd name = "Research Director" - jobtype = /datum/job/rd + jobtype = /datum/job/research_director - id = /obj/item/card/id/silver + id = /obj/item/card/id/advanced/silver belt = /obj/item/pda/heads/rd ears = /obj/item/radio/headset/heads/rd uniform = /obj/item/clothing/under/rank/rnd/research_director @@ -57,6 +50,8 @@ chameleon_extras = /obj/item/stamp/rd + id_trim = /datum/id_trim/job/research_director + /datum/outfit/job/rd/rig name = "Research Director (Hardsuit)" diff --git a/code/modules/jobs/job_types/roboticist.dm b/code/modules/jobs/job_types/roboticist.dm index 6974d560a24..c57ccdc978f 100644 --- a/code/modules/jobs/job_types/roboticist.dm +++ b/code/modules/jobs/job_types/roboticist.dm @@ -13,10 +13,6 @@ outfit = /datum/outfit/job/roboticist departments = DEPARTMENT_SCIENCE - access = list(ACCESS_ROBOTICS, ACCESS_RND, ACCESS_TOXINS, ACCESS_TOXINS_STORAGE, ACCESS_TECH_STORAGE, ACCESS_MORGUE, ACCESS_MECH_SCIENCE, - ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM, ACCESS_XENOBIOLOGY, ACCESS_GENETICS, ACCESS_AUX_BASE) - minimal_access = list(ACCESS_ROBOTICS, ACCESS_TECH_STORAGE, ACCESS_MORGUE, ACCESS_RESEARCH, ACCESS_MECH_SCIENCE, ACCESS_MINERAL_STOREROOM, - ACCESS_RND, ACCESS_AUX_BASE) paycheck = PAYCHECK_MEDIUM paycheck_department = ACCOUNT_SCI @@ -39,3 +35,5 @@ pda_slot = ITEM_SLOT_LPOCKET skillchips = list(/obj/item/skillchip/job/roboticist) + + id_trim = /datum/id_trim/job/roboticist diff --git a/code/modules/jobs/job_types/scientist.dm b/code/modules/jobs/job_types/scientist.dm index de1eb4e3bfa..574fdfdb95e 100644 --- a/code/modules/jobs/job_types/scientist.dm +++ b/code/modules/jobs/job_types/scientist.dm @@ -9,13 +9,8 @@ exp_requirements = 60 exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/scientist - access = list(ACCESS_ROBOTICS, ACCESS_RND, ACCESS_TOXINS, ACCESS_TOXINS_STORAGE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, - ACCESS_MECH_SCIENCE, ACCESS_MINERAL_STOREROOM, ACCESS_TECH_STORAGE, ACCESS_GENETICS, ACCESS_AUX_BASE) - minimal_access = list(ACCESS_RND, ACCESS_TOXINS, ACCESS_TOXINS_STORAGE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_MECH_SCIENCE, - ACCESS_MINERAL_STOREROOM, ACCESS_AUX_BASE) paycheck = PAYCHECK_MEDIUM paycheck_department = ACCOUNT_SCI @@ -37,6 +32,8 @@ satchel = /obj/item/storage/backpack/satchel/tox duffelbag = /obj/item/storage/backpack/duffelbag/toxins + id_trim = /datum/id_trim/job/scientist + /datum/outfit/job/scientist/pre_equip(mob/living/carbon/human/H) ..() if(prob(0.4)) diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm index 9372b6f25ad..f9436b134d3 100644 --- a/code/modules/jobs/job_types/security_officer.dm +++ b/code/modules/jobs/job_types/security_officer.dm @@ -1,4 +1,4 @@ -/datum/job/officer +/datum/job/security_officer title = "Security Officer" auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY department_head = list("Head of Security") @@ -13,8 +13,6 @@ outfit = /datum/outfit/job/security - access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_MECH_SECURITY, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_FORENSICS_LOCKERS, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_MECH_SECURITY, ACCESS_MINERAL_STOREROOM) // See /datum/job/officer/get_access() paycheck = PAYCHECK_HARD paycheck_department = ACCOUNT_SEC @@ -25,14 +23,9 @@ bounty_types = CIV_JOB_SEC departments = DEPARTMENT_SECURITY -/datum/job/officer/get_access() - var/list/L = list() - L |= ..() | check_config_for_sec_maint() - return L - GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, SEC_DEPT_SCIENCE, SEC_DEPT_SUPPLY)) -/datum/job/officer/after_spawn(mob/living/carbon/human/H, mob/M) +/datum/job/security_officer/after_spawn(mob/living/carbon/human/H, mob/M) . = ..() // Assign department security var/department @@ -46,31 +39,31 @@ GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, S department = pick_n_take(GLOB.available_depts) var/ears = null var/accessory = null - var/list/dep_access = null + var/list/dep_trim = null var/destination = null var/spawn_point = null switch(department) if(SEC_DEPT_SUPPLY) ears = /obj/item/radio/headset/headset_sec/alt/department/supply - dep_access = list(ACCESS_MAILSORTING, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_CARGO, ACCESS_AUX_BASE) + dep_trim = /datum/id_trim/job/security_officer/supply destination = /area/security/checkpoint/supply spawn_point = locate(/obj/effect/landmark/start/depsec/supply) in GLOB.department_security_spawns accessory = /obj/item/clothing/accessory/armband/cargo if(SEC_DEPT_ENGINEERING) ears = /obj/item/radio/headset/headset_sec/alt/department/engi - dep_access = list(ACCESS_CONSTRUCTION, ACCESS_ENGINE, ACCESS_ATMOSPHERICS, ACCESS_AUX_BASE) + dep_trim = /datum/id_trim/job/security_officer/engineering destination = /area/security/checkpoint/engineering spawn_point = locate(/obj/effect/landmark/start/depsec/engineering) in GLOB.department_security_spawns accessory = /obj/item/clothing/accessory/armband/engine if(SEC_DEPT_MEDICAL) ears = /obj/item/radio/headset/headset_sec/alt/department/med - dep_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY) + dep_trim = /datum/id_trim/job/security_officer/medical destination = /area/security/checkpoint/medical spawn_point = locate(/obj/effect/landmark/start/depsec/medical) in GLOB.department_security_spawns accessory = /obj/item/clothing/accessory/armband/medblue if(SEC_DEPT_SCIENCE) ears = /obj/item/radio/headset/headset_sec/alt/department/sci - dep_access = list(ACCESS_RESEARCH, ACCESS_RND, ACCESS_AUX_BASE) + dep_trim = /datum/id_trim/job/security_officer/science destination = /area/security/checkpoint/science spawn_point = locate(/obj/effect/landmark/start/depsec/science) in GLOB.department_security_spawns accessory = /obj/item/clothing/accessory/armband/science @@ -83,8 +76,11 @@ GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, S qdel(H.ears) H.equip_to_slot_or_del(new ears(H),ITEM_SLOT_EARS) - var/obj/item/card/id/W = H.wear_id - W.access |= dep_access + // If there's a departmental sec trim to apply to the card, overwrite. + if(dep_trim) + var/obj/item/card/id/worn_id = H.wear_id + SSid_access.apply_trim_to_card(worn_id, dep_trim) + H.sec_hud_set_ID() var/teleport = 0 if(!CONFIG_GET(flag/sec_start_brig)) @@ -112,7 +108,7 @@ GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, S /datum/outfit/job/security name = "Security Officer" - jobtype = /datum/job/officer + jobtype = /datum/job/security_officer belt = /obj/item/pda/security ears = /obj/item/radio/headset/headset_sec/alt @@ -136,6 +132,7 @@ GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, S chameleon_extras = list(/obj/item/gun/energy/disabler, /obj/item/clothing/glasses/hud/security/sunglasses, /obj/item/clothing/head/helmet) //The helmet is necessary because /obj/item/clothing/head/helmet/sec is overwritten in the chameleon list by the standard helmet, which has the same name and icon state + id_trim = /datum/id_trim/job/security_officer /obj/item/radio/headset/headset_sec/alt/department/Initialize() . = ..() diff --git a/code/modules/jobs/job_types/shaft_miner.dm b/code/modules/jobs/job_types/shaft_miner.dm index 66072891370..db6783a10a8 100644 --- a/code/modules/jobs/job_types/shaft_miner.dm +++ b/code/modules/jobs/job_types/shaft_miner.dm @@ -1,4 +1,4 @@ -/datum/job/mining +/datum/job/shaft_miner title = "Shaft Miner" department_head = list("Head of Personnel") faction = "Station" @@ -9,8 +9,6 @@ outfit = /datum/outfit/job/miner - access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_QM, ACCESS_MINING, ACCESS_MECH_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_AUX_BASE) - minimal_access = list(ACCESS_MINING, ACCESS_MECH_MINING, ACCESS_MINING_STATION, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM, ACCESS_AUX_BASE) paycheck = PAYCHECK_MEDIUM paycheck_department = ACCOUNT_CAR @@ -20,7 +18,7 @@ /datum/outfit/job/miner name = "Shaft Miner" - jobtype = /datum/job/mining + jobtype = /datum/job/shaft_miner belt = /obj/item/pda/shaftminer ears = /obj/item/radio/headset/headset_cargo/mining @@ -42,6 +40,8 @@ chameleon_extras = /obj/item/gun/energy/kinetic_accelerator + id_trim = /datum/id_trim/job/shaft_miner + /datum/outfit/job/miner/equipped name = "Shaft Miner (Equipment)" suit = /obj/item/clothing/suit/hooded/explorer diff --git a/code/modules/jobs/job_types/station_engineer.dm b/code/modules/jobs/job_types/station_engineer.dm index 390861d6343..fff1cf42b87 100644 --- a/code/modules/jobs/job_types/station_engineer.dm +++ b/code/modules/jobs/job_types/station_engineer.dm @@ -1,4 +1,4 @@ -/datum/job/engineer +/datum/job/station_engineer title = "Station Engineer" department_head = list("Chief Engineer") faction = "Station" @@ -11,10 +11,6 @@ outfit = /datum/outfit/job/engineer - access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_MECH_ENGINE, ACCESS_AUX_BASE, - ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_TCOMSAT, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_MECH_ENGINE, ACCESS_AUX_BASE, - ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_TCOMSAT, ACCESS_MINERAL_STOREROOM) paycheck = PAYCHECK_MEDIUM paycheck_department = ACCOUNT_ENG @@ -26,7 +22,7 @@ /datum/outfit/job/engineer name = "Station Engineer" - jobtype = /datum/job/engineer + jobtype = /datum/job/station_engineer belt = /obj/item/storage/belt/utility/full/engi l_pocket = /obj/item/pda/engineering @@ -45,6 +41,8 @@ skillchips = list(/obj/item/skillchip/job/engineer) + id_trim = /datum/id_trim/job/station_engineer + /datum/outfit/job/engineer/gloved name = "Station Engineer (Gloves)" gloves = /obj/item/clothing/gloves/color/yellow @@ -57,3 +55,8 @@ head = null internals_slot = ITEM_SLOT_SUITSTORE +/datum/outfit/job/engineer/gloved/gunner + id_trim = /datum/id_trim/job/station_engineer/gunner + +/datum/outfit/job/engineer/gloved/rig/gunner + id_trim = /datum/id_trim/job/station_engineer/gunner diff --git a/code/modules/jobs/job_types/virologist.dm b/code/modules/jobs/job_types/virologist.dm index 926735e3083..ff5e3443bbe 100644 --- a/code/modules/jobs/job_types/virologist.dm +++ b/code/modules/jobs/job_types/virologist.dm @@ -11,8 +11,6 @@ outfit = /datum/outfit/job/virologist - access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_MECH_MEDICAL, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_MEDICAL, ACCESS_VIROLOGY, ACCESS_MECH_MEDICAL, ACCESS_MINERAL_STOREROOM) paycheck = PAYCHECK_MEDIUM paycheck_department = ACCOUNT_MED @@ -38,3 +36,5 @@ satchel = /obj/item/storage/backpack/satchel/vir duffelbag = /obj/item/storage/backpack/duffelbag/virology box = /obj/item/storage/box/survival/medical + + id_trim = /datum/id_trim/job/virologist diff --git a/code/modules/jobs/job_types/warden.dm b/code/modules/jobs/job_types/warden.dm index 175807bde86..878621c1faf 100644 --- a/code/modules/jobs/job_types/warden.dm +++ b/code/modules/jobs/job_types/warden.dm @@ -13,8 +13,6 @@ outfit = /datum/outfit/job/warden - access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_MECH_SECURITY, ACCESS_MAINT_TUNNELS, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_FORENSICS_LOCKERS, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_MECH_SECURITY, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) // See /datum/job/warden/get_access() paycheck = PAYCHECK_HARD paycheck_department = ACCOUNT_SEC @@ -25,11 +23,6 @@ bounty_types = CIV_JOB_SEC departments = DEPARTMENT_SECURITY -/datum/job/warden/get_access() - var/list/L = list() - L = ..() | check_config_for_sec_maint() - return L - /datum/outfit/job/warden name = "Warden" jobtype = /datum/job/warden @@ -54,3 +47,4 @@ implants = list(/obj/item/implant/mindshield) + id_trim = /datum/id_trim/job/warden diff --git a/code/modules/mining/laborcamp/laborstacker.dm b/code/modules/mining/laborcamp/laborstacker.dm index e6b7119b8a9..ac845e9da81 100644 --- a/code/modules/mining/laborcamp/laborstacker.dm +++ b/code/modules/mining/laborcamp/laborstacker.dm @@ -56,8 +56,8 @@ GLOBAL_LIST(labor_sheet_values) if(isliving(usr)) var/mob/living/L = usr I = L.get_idcard(TRUE) - if(istype(I, /obj/item/card/id/prisoner)) - var/obj/item/card/id/prisoner/P = I + if(istype(I, /obj/item/card/id/advanced/prisoner)) + var/obj/item/card/id/advanced/prisoner/P = I data["id_points"] = P.points if(P.points >= P.goal) can_go_home = TRUE @@ -86,8 +86,8 @@ GLOBAL_LIST(labor_sheet_values) if(isliving(M)) var/mob/living/L = M I = L.get_idcard(TRUE) - if(istype(I, /obj/item/card/id/prisoner)) - var/obj/item/card/id/prisoner/P = I + if(istype(I, /obj/item/card/id/advanced/prisoner)) + var/obj/item/card/id/advanced/prisoner/P = I P.points += stacking_machine.points stacking_machine.points = 0 to_chat(M, "Points transferred.") @@ -157,8 +157,8 @@ GLOBAL_LIST(labor_sheet_values) /obj/machinery/mineral/labor_points_checker/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/card/id)) - if(istype(I, /obj/item/card/id/prisoner)) - var/obj/item/card/id/prisoner/prisoner_id = I + if(istype(I, /obj/item/card/id/advanced/prisoner)) + var/obj/item/card/id/advanced/prisoner/prisoner_id = I to_chat(user, "ID: [prisoner_id.registered_name]") to_chat(user, "Points Collected:[prisoner_id.points]") to_chat(user, "Point Quota: [prisoner_id.goal]") diff --git a/code/modules/mining/machine_vending.dm b/code/modules/mining/machine_vending.dm index 9f1218f34bd..8859d7a2cdd 100644 --- a/code/modules/mining/machine_vending.dm +++ b/code/modules/mining/machine_vending.dm @@ -212,7 +212,7 @@ /obj/machinery/mineral/equipment_vendor/golem/Initialize() desc += "\nIt seems a few selections have been added." prize_list += list( - new /datum/data/mining_equipment("Extra Id", /obj/item/card/id/mining, 250), + new /datum/data/mining_equipment("Extra Id", /obj/item/card/id/advanced/mining, 250), new /datum/data/mining_equipment("Science Goggles", /obj/item/clothing/glasses/science, 250), new /datum/data/mining_equipment("Monkey Cube", /obj/item/food/monkeycube, 300), new /datum/data/mining_equipment("Toolbelt", /obj/item/storage/belt/utility, 350), @@ -257,24 +257,6 @@ ..() to_chat(user, "There's [points] point\s on the card.") -///Conscript kit -/obj/item/card/mining_access_card - name = "mining access card" - desc = "A small card, that when used on any ID, will add mining access." - icon_state = "data_1" - -/obj/item/card/mining_access_card/afterattack(atom/movable/AM, mob/user, proximity) - . = ..() - if(istype(AM, /obj/item/card/id) && proximity) - var/obj/item/card/id/I = AM - I.access |= ACCESS_MINING - I.access |= ACCESS_MINING_STATION - I.access |= ACCESS_MECH_MINING - I.access |= ACCESS_MINERAL_STOREROOM - I.access |= ACCESS_CARGO - to_chat(user, "You upgrade [I] with mining access.") - qdel(src) - /obj/item/storage/backpack/duffelbag/mining_conscript name = "mining conscription kit" desc = "A kit containing everything a crewmember needs to support a shaft miner in the field." @@ -288,7 +270,7 @@ new /obj/item/clothing/suit/hooded/explorer(src) new /obj/item/encryptionkey/headset_mining(src) new /obj/item/clothing/mask/gas/explorer(src) - new /obj/item/card/mining_access_card(src) + new /obj/item/card/id/advanced/mining(src) new /obj/item/gun/energy/kinetic_accelerator(src) new /obj/item/kitchen/knife/combat/survival(src) new /obj/item/flashlight/seclite(src) diff --git a/code/modules/mining/minebot.dm b/code/modules/mining/minebot.dm index 3d8ad9f9d2d..154a81ad02c 100644 --- a/code/modules/mining/minebot.dm +++ b/code/modules/mining/minebot.dm @@ -55,13 +55,11 @@ var/obj/item/implant/radio/mining/imp = new(src) imp.implant(src) - access_card = new /obj/item/card/id(src) - var/datum/job/mining/M = new - access_card.access = M.get_access() + access_card = new /obj/item/card/id/advanced/gold(src) + SSid_access.apply_trim_to_card(access_card, /datum/id_trim/job/shaft_miner) SetCollectBehavior() - /mob/living/simple_animal/hostile/mining_drone/Destroy() for (var/datum/action/innate/minedrone/action in actions) qdel(action) diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 1ee2a966097..0e3d174e5a8 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -316,7 +316,16 @@ SSjob.AssignRole(src, rank, 1) var/mob/living/character = create_character(TRUE) //creates the human and transfers vars and mind - var/equip = SSjob.EquipRank(character, rank, TRUE) + + var/is_captain = FALSE + // If we don't have an assigned cap yet, check if this person qualifies for some from of captaincy. + if(!SSjob.assigned_captain && ishuman(character) && SSjob.chain_of_command[rank] && !is_banned_from(ckey, list("Captain"))) + is_captain = TRUE + // If we already have a captain, are they a "Captain" rank and are we allowing multiple of them to be assigned? + else if(SSjob.always_promote_captain_job && (rank == "Captain")) + is_captain = TRUE + + var/equip = SSjob.EquipRank(character, rank, TRUE, is_captain) if(isliving(equip)) //Borgs get borged in the equip, so we need to make sure we handle the new mob. character = equip diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index baae6952bf6..f2af6ff2475 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -55,8 +55,6 @@ if(num_hands) . += "[t_He] [t_has] [num_hands > 1 ? "" : "a"] blood-stained hand[num_hands > 1 ? "s" : ""]!" - //handcuffed? - //handcuffed? if(handcuffed) if(istype(handcuffed, /obj/item/restraints/handcuffs/cable)) @@ -94,6 +92,12 @@ if(wear_id && !(wear_id.item_flags & EXAMINE_SKIP)) . += "[t_He] [t_is] wearing [wear_id.get_examine_string(user)]." + . += wear_id.get_id_examine_strings(user) + //var/list/extended_id_examine = wear_id.get_id_examine_strings(user) + + //for(var/examine_string in extended_id_examine) + // . += examine_string + //Status effects var/list/status_examines = status_effect_examines() if (length(status_examines)) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 92829523d88..4624aa98441 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -612,10 +612,6 @@ if(HAS_TRAIT(src, TRAIT_MINDSHIELD)) threatcount -= 1 - //Agent cards lower threatlevel. - if(istype(idcard, /obj/item/card/id/syndicate)) - threatcount -= 5 - return threatcount diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index 2365994163c..85584f5b247 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -90,8 +90,6 @@ /mob/living/carbon/human/can_track(mob/living/user) - if(wear_id && istype(wear_id.GetID(), /obj/item/card/id/syndicate)) - return 0 if(istype(head, /obj/item/clothing/head)) var/obj/item/clothing/head/hat = head if(hat.blockTracking) diff --git a/code/modules/mob/living/carbon/human/human_update_icons.dm b/code/modules/mob/living/carbon/human/human_update_icons.dm index 69e8b043e66..e85564869a6 100644 --- a/code/modules/mob/living/carbon/human/human_update_icons.dm +++ b/code/modules/mob/living/carbon/human/human_update_icons.dm @@ -143,6 +143,7 @@ There are several things that need to be remembered: /mob/living/carbon/human/update_inv_wear_id() remove_overlay(ID_LAYER) + remove_overlay(ID_CARD_LAYER) if(client && hud_used) var/atom/movable/screen/inventory/inv = hud_used.inv_slots[TOBITSHIFT(ITEM_SLOT_ID) + 1] @@ -157,13 +158,24 @@ There are several things that need to be remembered: update_observer_view(wear_id) //TODO: add an icon file for ID slot stuff, so it's less snowflakey - id_overlay = wear_id.build_worn_icon(default_layer = ID_LAYER, default_icon_file = 'icons/mob/mob.dmi') + id_overlay = wear_id.build_worn_icon(default_layer = ID_LAYER, default_icon_file = 'icons/mob/clothing/id.dmi') if(OFFSET_ID in dna.species.offset_features) id_overlay.pixel_x += dna.species.offset_features[OFFSET_ID][1] id_overlay.pixel_y += dna.species.offset_features[OFFSET_ID][2] overlays_standing[ID_LAYER] = id_overlay + var/obj/item/card/id/shown_id = wear_id.GetID() + if(shown_id) + var/mutable_appearance/id_card_overlay = overlays_standing[ID_CARD_LAYER] + id_card_overlay = shown_id.build_worn_icon(default_layer = ID_CARD_LAYER, default_icon_file = 'icons/mob/clothing/id_card.dmi') + if(OFFSET_ID in dna.species.offset_features) + id_card_overlay.pixel_x += dna.species.offset_features[OFFSET_ID][1] + id_card_overlay.pixel_y += dna.species.offset_features[OFFSET_ID][2] + + overlays_standing[ID_CARD_LAYER] = id_card_overlay + apply_overlay(ID_LAYER) + apply_overlay(ID_CARD_LAYER) /mob/living/carbon/human/update_inv_gloves() diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index ce13bf6bba3..66d54555ace 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -152,9 +152,10 @@ /mob/living/simple_animal/bot/Initialize() . = ..() GLOB.bots_list += src - access_card = new /obj/item/card/id(src) -//This access is so bots can be immediately set to patrol and leave Robotics, instead of having to be let out first. - access_card.access += ACCESS_ROBOTICS + // Give bots a fancy new ID card that can hold any access. + access_card = new /obj/item/card/id/advanced/simple_bot(src) + // This access is so bots can be immediately set to patrol and leave Robotics, instead of having to be let out first. + access_card.set_access(list(ACCESS_ROBOTICS)) set_custom_texts() Radio = new/obj/item/radio(src) if(radio_key) @@ -552,15 +553,13 @@ Pass a positive integer as an argument to override a bot's default speed. /mob/living/simple_animal/bot/proc/check_bot_access() if(mode != BOT_SUMMON && mode != BOT_RESPONDING) - access_card.access = prev_access + access_card.set_access(prev_access) /mob/living/simple_animal/bot/proc/call_bot(caller, turf/waypoint, message=TRUE) bot_reset() //Reset a bot before setting it to call mode. - //For giving the bot temporary all-access. - var/obj/item/card/id/all_access = new /obj/item/card/id - var/datum/job/captain/All = new/datum/job/captain - all_access.access = All.get_access() + //For giving the bot temporary all-access. This method is bad and makes me feel bad. Refactoring access to a component is for another PR. + var/obj/item/card/id/all_access = new /obj/item/card/id/advanced/gold/captains_spare() set_path(get_path_to(src, waypoint, 200, id=all_access)) calling_ai = caller //Link the AI to the bot! @@ -604,7 +603,7 @@ Pass a positive integer as an argument to override a bot's default speed. set_path(null) summon_target = null pathset = 0 - access_card.access = prev_access + access_card.set_access(prev_access) tries = 0 mode = BOT_IDLE diag_hud_set_botstat() @@ -742,7 +741,7 @@ Pass a positive integer as an argument to override a bot's default speed. bot_reset() summon_target = get_turf(user) if(user_access.len != 0) - access_card.access = user_access + prev_access //Adds the user's access, if any. + access_card.set_access(user_access + prev_access) //Adds the user's access, if any. mode = BOT_SUMMON speak("Responding.", radio_channel) @@ -997,7 +996,7 @@ Pass a positive integer as an argument to override a bot's default speed. . = ..() if(!. || !client) return FALSE - access_card.access += player_access + access_card.add_access(player_access) diag_hud_set_botmode() /mob/living/simple_animal/bot/Logout() diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm index 127f5d83154..40e60f6a4a4 100644 --- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm +++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm @@ -105,9 +105,10 @@ get_targets() icon_state = "cleanbot[on]" - var/datum/job/janitor/J = new/datum/job/janitor - access_card.access += J.get_access() - prev_access = access_card.access + // Doing this hurts my soul, but simplebot access reworks are for another day. + var/datum/id_trim/job/jani_trim = SSid_access.trim_singletons_by_path[/datum/id_trim/job/janitor] + access_card.add_access(jani_trim.access + jani_trim.wildcard_access) + prev_access = access_card.access.Copy() stolen_valor = list() prefixes = list(command, security, engineering) diff --git a/code/modules/mob/living/simple_animal/bot/firebot.dm b/code/modules/mob/living/simple_animal/bot/firebot.dm index 2ad3ba0fe99..4bcb7045032 100644 --- a/code/modules/mob/living/simple_animal/bot/firebot.dm +++ b/code/modules/mob/living/simple_animal/bot/firebot.dm @@ -41,10 +41,12 @@ /mob/living/simple_animal/bot/firebot/Initialize() . = ..() ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT) - update_appearance() - var/datum/job/engineer/J = new/datum/job/engineer - access_card.access += J.get_access() - prev_access = access_card.access + update_appearance(UPDATE_ICON) + + // Doing this hurts my soul, but simplebot access reworks are for another day. + var/datum/id_trim/job/engi_trim = SSid_access.trim_singletons_by_path[/datum/id_trim/job/station_engineer] + access_card.add_access(engi_trim.access + engi_trim.wildcard_access) + prev_access = access_card.access.Copy() create_extinguisher() diff --git a/code/modules/mob/living/simple_animal/bot/floorbot.dm b/code/modules/mob/living/simple_animal/bot/floorbot.dm index c0ba903a853..0e3ef0b4740 100644 --- a/code/modules/mob/living/simple_animal/bot/floorbot.dm +++ b/code/modules/mob/living/simple_animal/bot/floorbot.dm @@ -43,10 +43,13 @@ . = ..() ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT) toolbox_color = new_toolbox_color - update_appearance() - var/datum/job/engineer/J = new/datum/job/engineer - access_card.access += J.get_access() - prev_access = access_card.access + update_appearance(UPDATE_ICON) + + // Doing this hurts my soul, but simplebot access reworks are for another day. + var/datum/id_trim/job/engi_trim = SSid_access.trim_singletons_by_path[/datum/id_trim/job/station_engineer] + access_card.add_access(engi_trim.access + engi_trim.wildcard_access) + prev_access = access_card.access.Copy() + if(toolbox_color == "s") health = 100 maxHealth = 100 diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm index 511c1f448a8..3c1f50b255b 100644 --- a/code/modules/mob/living/simple_animal/bot/honkbot.dm +++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm @@ -41,9 +41,11 @@ . = ..() update_appearance() auto_patrol = TRUE - var/datum/job/clown/J = new/datum/job/clown - access_card.access += J.get_access() - prev_access = access_card.access + + // Doing this hurts my soul, but simplebot access reworks are for another day. + var/datum/id_trim/job/clown_trim = SSid_access.trim_singletons_by_path[/datum/id_trim/job/clown] + access_card.add_access(clown_trim.access + clown_trim.wildcard_access) + prev_access = access_card.access.Copy() /mob/living/simple_animal/bot/honkbot/proc/limiting_spam_false() //used for addtimer limiting_spam = FALSE diff --git a/code/modules/mob/living/simple_animal/bot/hygienebot.dm b/code/modules/mob/living/simple_animal/bot/hygienebot.dm index f324849b5f6..2b35777cb5f 100644 --- a/code/modules/mob/living/simple_animal/bot/hygienebot.dm +++ b/code/modules/mob/living/simple_animal/bot/hygienebot.dm @@ -40,10 +40,12 @@ /mob/living/simple_animal/bot/hygienebot/Initialize() . = ..() - update_appearance() - var/datum/job/janitor/J = new/datum/job/janitor - access_card.access += J.get_access() - prev_access = access_card.access + update_appearance(UPDATE_ICON) + + // Doing this hurts my soul, but simplebot access reworks are for another day. + var/datum/id_trim/job/jani_trim = SSid_access.trim_singletons_by_path[/datum/id_trim/job/janitor] + access_card.add_access(jani_trim.access + jani_trim.wildcard_access) + prev_access = access_card.access.Copy() /mob/living/simple_animal/bot/hygienebot/explode() walk_to(src,0) diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm index 8a0281af41c..0b066ca0017 100644 --- a/code/modules/mob/living/simple_animal/bot/medbot.dm +++ b/code/modules/mob/living/simple_animal/bot/medbot.dm @@ -107,10 +107,12 @@ /mob/living/simple_animal/bot/medbot/Initialize(mapload, new_skin) . = ..() - var/datum/job/paramedic/J = new /datum/job/paramedic - access_card.access += J.get_access() - prev_access = access_card.access - qdel(J) + + // Doing this hurts my soul, but simplebot access reworks are for another day. + var/datum/id_trim/job/para_trim = SSid_access.trim_singletons_by_path[/datum/id_trim/job/paramedic] + access_card.add_access(para_trim.access + para_trim.wildcard_access) + prev_access = access_card.access.Copy() + skin = new_skin update_appearance() linked_techweb = SSresearch.science_tech diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index eaa6c9a4517..a79ea75efe8 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -64,9 +64,12 @@ new /mob/living/simple_animal/bot/mulebot/paranormal(loc) return INITIALIZE_HINT_QDEL wires = new /datum/wires/mulebot(src) - var/datum/job/cargo_tech/J = new/datum/job/cargo_tech - access_card.access = J.get_access() - prev_access = access_card.access + + // Doing this hurts my soul, but simplebot access reworks are for another day. + var/datum/id_trim/job/cargo_trim = SSid_access.trim_singletons_by_path[/datum/id_trim/job/cargo_technician] + access_card.add_access(cargo_trim.access + cargo_trim.wildcard_access) + prev_access = access_card.access.Copy() + cell = new /obj/item/stock_parts/cell/upgraded(src, 2000) var/static/mulebot_count = 0 diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm index 76da77aacf6..0c969ef54ba 100644 --- a/code/modules/mob/living/simple_animal/bot/secbot.dm +++ b/code/modules/mob/living/simple_animal/bot/secbot.dm @@ -77,10 +77,12 @@ /mob/living/simple_animal/bot/secbot/Initialize() . = ..() weapon = new baton_type() - update_appearance() - var/datum/job/detective/J = new/datum/job/detective - access_card.access += J.get_access() - prev_access = access_card.access + update_appearance(UPDATE_ICON) + + // Doing this hurts my soul, but simplebot access reworks are for another day. + var/datum/id_trim/job/det_trim = SSid_access.trim_singletons_by_path[/datum/id_trim/job/detective] + access_card.add_access(det_trim.access + det_trim.wildcard_access) + prev_access = access_card.access.Copy() //SECHUD var/datum/atom_hud/secsensor = GLOB.huds[DATA_HUD_SECURITY_ADVANCED] diff --git a/code/modules/mob/living/simple_animal/corpse.dm b/code/modules/mob/living/simple_animal/corpse.dm index 578240a3816..83fd6fb57b4 100644 --- a/code/modules/mob/living/simple_animal/corpse.dm +++ b/code/modules/mob/living/simple_animal/corpse.dm @@ -10,7 +10,6 @@ /obj/effect/mob_spawn/human/corpse/syndicatesoldier name = "Syndicate Operative" - id_job = "Operative" hairstyle = "Bald" facial_hairstyle = "Shaved" outfit = /datum/outfit/syndicatesoldiercorpse @@ -25,11 +24,11 @@ mask = /obj/item/clothing/mask/gas head = /obj/item/clothing/head/helmet/swat back = /obj/item/storage/backpack - id = /obj/item/card/id/syndicate + id = /obj/item/card/id/advanced/chameleon + id_trim = /datum/id_trim/chameleon/operative /obj/effect/mob_spawn/human/corpse/syndicatecommando name = "Syndicate Commando" - id_job = "Operative" hairstyle = "Bald" facial_hairstyle = "Shaved" outfit = /datum/outfit/syndicatecommandocorpse @@ -44,12 +43,12 @@ mask = /obj/item/clothing/mask/gas/syndicate back = /obj/item/tank/jetpack/oxygen r_pocket = /obj/item/tank/internals/emergency_oxygen - id = /obj/item/card/id/syndicate + id = /obj/item/card/id/advanced/chameleon + id_trim = /datum/id_trim/chameleon/operative /obj/effect/mob_spawn/human/corpse/syndicatestormtrooper name = "Syndicate Stormtrooper" - id_job = "Operative" hairstyle = "Bald" facial_hairstyle = "Shaved" outfit = /datum/outfit/syndicatestormtroopercorpse @@ -63,8 +62,8 @@ ears = /obj/item/radio/headset mask = /obj/item/clothing/mask/gas/syndicate back = /obj/item/tank/jetpack/oxygen/harness - id = /obj/item/card/id/syndicate - + id = /obj/item/card/id/advanced/chameleon + id_trim = /datum/id_trim/chameleon/operative /obj/effect/mob_spawn/human/clown/corpse roundstart = FALSE @@ -167,8 +166,6 @@ /obj/effect/mob_spawn/human/corpse/nanotrasensoldier name = "\improper Nanotrasen Private Security Officer" - id_job = "Private Security Force" - id_access = "Security Officer" outfit = /datum/outfit/nanotrasensoldiercorpse2 hairstyle = "Bald" facial_hairstyle = "Shaved" @@ -183,12 +180,11 @@ mask = /obj/item/clothing/mask/gas/sechailer/swat head = /obj/item/clothing/head/helmet/swat/nanotrasen back = /obj/item/storage/backpack/security - id = /obj/item/card/id + id = /obj/item/card/id/advanced/centcom + id_trim = /datum/id_trim/centcom/corpse/private_security/tradepost_officer /obj/effect/mob_spawn/human/corpse/nanotrasenassaultsoldier name = "Nanotrasen Private Security Officer" - id_job = "Nanotrasen Assault Force" - id_access = "Security Officer" outfit = /datum/outfit/nanotrasenassaultsoldiercorpse hairstyle = "Bald" facial_hairstyle = "Shaved" @@ -203,12 +199,11 @@ mask = /obj/item/clothing/mask/gas/sechailer/swat head = /obj/item/clothing/head/helmet/swat/nanotrasen back = /obj/item/storage/backpack/security - id = /obj/item/card/id + id = /obj/item/card/id/advanced/centcom + id_trim = /datum/id_trim/centcom/corpse/assault /obj/effect/mob_spawn/human/corpse/cat_butcher name = "The Cat Surgeon" - id_job = "Cat Surgeon" - id_access_list = list(ACCESS_AWAY_GENERAL, ACCESS_AWAY_MAINT) hairstyle = "Cut Hair" facial_hairstyle = "Watson Mustache" skin_tone = "caucasian1" @@ -222,8 +217,9 @@ gloves = /obj/item/clothing/gloves/color/latex/nitrile ears = /obj/item/radio/headset back = /obj/item/storage/backpack/satchel/med - id = /obj/item/card/id + id = /obj/item/card/id/advanced glasses = /obj/item/clothing/glasses/hud/health + id_trim = /datum/id_trim/away/cat_surgeon /obj/effect/mob_spawn/human/corpse/bee_terrorist name = "BLF Operative" @@ -237,6 +233,6 @@ gloves = /obj/item/clothing/gloves/color/yellow ears = /obj/item/radio/headset belt = /obj/item/storage/belt/fannypack/yellow/bee_terrorist - id = /obj/item/card/id + id = /obj/item/card/id/advanced l_pocket = /obj/item/paper/fluff/bee_objectives mask = /obj/item/clothing/mask/animal/rat/bee diff --git a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm index 693b5e86078..3dc85a604f0 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm @@ -125,9 +125,11 @@ /mob/living/simple_animal/drone/Initialize() . = ..() GLOB.drones_list += src - access_card = new /obj/item/card/id(src) - var/datum/job/captain/C = new /datum/job/captain - access_card.access = C.get_access() + access_card = new /obj/item/card/id/advanced/simple_bot(src) + + // Doing this hurts my soul, but simple_animal access reworks are for another day. + var/datum/id_trim/job/cap_trim = SSid_access.trim_singletons_by_path[/datum/id_trim/job/captain] + access_card.add_access(cap_trim.access + cap_trim.wildcard_access) if(default_storage) var/obj/item/I = new default_storage(src) diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm index 4e2d30f2bd1..5719ae251e3 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm @@ -371,7 +371,6 @@ r_pocket = /obj/item/tank/internals/emergency_oxygen mask = /obj/item/clothing/mask/breath if("Operative") - id_job = "Operative" outfit = /datum/outfit/syndicatecommandocorpse if("Shadow") mob_species = /datum/species/shadow diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index d110e613f58..495e271e82a 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1128,6 +1128,7 @@ if(ID.registered_name == oldname) ID.registered_name = newname ID.update_label() + ID.update_icon() if(ID.registered_account?.account_holder == oldname) ID.registered_account.account_holder = newname if(!search_pda) diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index fc289de56c7..481e866f7a5 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -97,14 +97,60 @@ /obj/item/modular_computer/GetID() var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] - if(card_slot) - return card_slot.GetID() + var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2] + + var/obj/item/card/id/first_id = card_slot?.GetID() + var/obj/item/card/id/second_id = card_slot2?.GetID() + + // We have two IDs, pick the one with the most command accesses, preferring the primary slot. + if(first_id && second_id) + var/first_id_tally = SSid_access.tally_access(first_id, ACCESS_FLAG_COMMAND) + var/second_id_tally = SSid_access.tally_access(second_id, ACCESS_FLAG_COMMAND) + + return (first_id_tally >= second_id_tally) ? first_id : second_id + + // If we don't have both ID slots filled, pick the one that is filled. + if(first_id) + return first_id + if(second_id) + return second_id + + // Otherwise, we have no ID at all. return ..() +/obj/item/modular_computer/get_id_examine_strings(mob/user) + . = ..() + + var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2] + var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] + + var/obj/item/card/id/id_card1 = card_slot?.GetID() + var/obj/item/card/id/id_card2 = card_slot2?.GetID() + + if(id_card1 || id_card2) + if(id_card1 && id_card2) + . += "\The [src] is displaying [id_card1] and [id_card2]." + var/list/id_icons = list() + id_icons += id_card1.get_id_examine_strings(user) + id_icons += id_card2.get_id_examine_strings(user) + . += id_icons.Join(" ") + else if(id_card1) + . += "\The [src] is displaying [id_card1]." + . += id_card1.get_id_examine_strings(user) + else + . += "\The [src] is displaying [id_card2]." + . += id_card2.get_id_examine_strings(user) + /obj/item/modular_computer/RemoveID() var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2] var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] - return (card_slot2?.try_eject() || card_slot?.try_eject()) //Try the secondary one first. + + var/removed_id = (card_slot2?.try_eject() || card_slot?.try_eject()) + if(removed_id) + update_slot_icon() + return removed_id + + return ..() /obj/item/modular_computer/InsertID(obj/item/inserting_item) var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] @@ -117,7 +163,9 @@ return FALSE if((card_slot?.try_insert(inserting_id)) || (card_slot2?.try_insert(inserting_id))) + update_slot_icon() return TRUE + return FALSE /obj/item/modular_computer/MouseDrop(obj/over_object, src_location, over_location) diff --git a/code/modules/modular_computers/file_system/programs/card.dm b/code/modules/modular_computers/file_system/programs/card.dm index 8bc3db57576..8aba016aa38 100644 --- a/code/modules/modular_computers/file_system/programs/card.dm +++ b/code/modules/modular_computers/file_system/programs/card.dm @@ -1,11 +1,3 @@ -#define CARDCON_DEPARTMENT_SERVICE "Service" -#define CARDCON_DEPARTMENT_SECURITY "Security" -#define CARDCON_DEPARTMENT_MEDICAL "Medical" -#define CARDCON_DEPARTMENT_SUPPLY "Supply" -#define CARDCON_DEPARTMENT_SCIENCE "Science" -#define CARDCON_DEPARTMENT_ENGINEERING "Engineering" -#define CARDCON_DEPARTMENT_COMMAND "Command" - /datum/computer_file/program/card_mod filename = "plexagonidwriter" filedesc = "Plexagon Access Management" @@ -18,78 +10,67 @@ tgui_id = "NtosCard" program_icon = "id-card" + /// If TRUE, this program only modifies Centcom accesses. var/is_centcom = FALSE + /// If TRUE, this program is authenticated with limited departmental access. var/minor = FALSE - var/authenticated = FALSE - var/list/region_access - var/list/head_subordinates - ///Which departments this computer has access to. Defined as access regions. null = all departments + /// The name/assignment combo of the ID card used to authenticate. + var/authenticated_user + /// The regions this program has access to based on the authenticated ID. + var/list/region_access = list() + /// List of subordinate jobs for head roles on the authenticated ID. + var/list/head_subordinates = list() + /// List of job templates that can be applied to ID cards from this program. + var/list/job_templates = list() + /// Which departments this program has access to. See region defines. var/target_dept - //For some reason everything was exploding if this was static. - var/list/sub_managers - -/datum/computer_file/program/card_mod/New(obj/item/modular_computer/comp) - . = ..() - sub_managers = list( - "[ACCESS_HOP]" = list( - "department" = list(CARDCON_DEPARTMENT_SERVICE, CARDCON_DEPARTMENT_COMMAND), - "region" = 1, - "head" = "Head of Personnel" - ), - "[ACCESS_HOS]" = list( - "department" = CARDCON_DEPARTMENT_SECURITY, - "region" = 2, - "head" = "Head of Security" - ), - "[ACCESS_CMO]" = list( - "department" = CARDCON_DEPARTMENT_MEDICAL, - "region" = 3, - "head" = "Chief Medical Officer" - ), - "[ACCESS_RD]" = list( - "department" = CARDCON_DEPARTMENT_SCIENCE, - "region" = 4, - "head" = "Research Director" - ), - "[ACCESS_CE]" = list( - "department" = CARDCON_DEPARTMENT_ENGINEERING, - "region" = 5, - "head" = "Chief Engineer" - ) - ) - +/** + * Authenticates the program based on the specific ID card. + * + * If the card has ACCESS_CHANGE_IDs, it authenticates with all options. + * Otherwise, it authenticates depending on SSid_access.sub_department_managers_tgui + * compared to the access on the supplied ID card. + * Arguments: + * * user - Program's user. + * * id_card - The ID card to attempt to authenticate under. + */ /datum/computer_file/program/card_mod/proc/authenticate(mob/user, obj/item/card/id/id_card) if(!id_card) return - region_access = list() + region_access.Cut() + // If the console isn't locked to a specific department and we have ACCESS_CHANGE_IDS in our auth card, we're not minor. if(!target_dept && (ACCESS_CHANGE_IDS in id_card.access)) minor = FALSE - authenticated = TRUE + authenticated_user = "[id_card.name]" + job_templates = SSid_access.station_job_templates.Copy() update_static_data(user) return TRUE + // Otherwise, we're minor and now we have to build a list of restricted departments we can change access for. + job_templates.Cut() var/list/head_types = list() - for(var/access_text in sub_managers) - var/list/info = sub_managers[access_text] - var/access = text2num(access_text) - if((access in id_card.access) && ((info["region"] in target_dept) || !length(target_dept))) - region_access += info["region"] - //I don't even know what I'm doing anymore - head_types += info["head"] + var/list/managers = SSid_access.sub_department_managers_tgui + for(var/access_as_text in managers) + var/list/info = managers[access_as_text] + var/access = text2num(access_as_text) + if((access in id_card.access) && ((target_dept in info["regions"]) || !target_dept)) + region_access |= info["regions"] + head_types |= info["head"] + job_templates |= info["templates"] - head_subordinates = list() + head_subordinates.Cut() if(length(head_types)) - for(var/j in SSjob.occupations) - var/datum/job/job = j - for(var/head in head_types)//god why + for(var/occupation in SSjob.occupations) + var/datum/job/job = occupation + for(var/head in head_types) if(head in job.department_head) head_subordinates += job.title if(length(region_access)) minor = TRUE - authenticated = TRUE + authenticated_user = "[id_card.name] \[LIMITED ACCESS\]" update_static_data(user) return TRUE @@ -112,26 +93,28 @@ var/mob/user = usr var/obj/item/card/id/user_id_card = card_slot.stored_card - var/obj/item/card/id/target_id_card = card_slot2.stored_card switch(action) + // Log in. if("PRG_authenticate") if(!computer || !user_id_card) playsound(computer, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE) - return + return TRUE if(authenticate(user, user_id_card)) playsound(computer, 'sound/machines/terminal_on.ogg', 50, FALSE) return TRUE + // Log out. if("PRG_logout") - authenticated = FALSE + authenticated_user = null playsound(computer, 'sound/machines/terminal_off.ogg', 50, FALSE) return TRUE + // Print a report. if("PRG_print") if(!computer || !printer) - return - if(!authenticated) - return + return TRUE + if(!authenticated_user) + return TRUE var/contents = {"

Access Report

Prepared By: [user_id_card?.registered_name ? user_id_card.registered_name : "Unknown"]
For: [target_id_card.registered_name ? target_id_card.registered_name : "Unregistered"]
@@ -140,21 +123,32 @@ Access:
"} - var/known_access_rights = get_all_accesses() + var/list/known_access_rights = SSid_access.get_region_access_list(list(REGION_ALL_STATION)) for(var/A in target_id_card.access) if(A in known_access_rights) - contents += " [get_access_desc(A)]" + contents += " [SSid_access.get_access_desc(A)]" if(!printer.print_text(contents,"access report")) to_chat(usr, "Hardware error: Printer was unable to print the file. It may be out of paper.") - return + return TRUE else playsound(computer, 'sound/machines/terminal_on.ogg', 50, FALSE) computer.visible_message("\The [computer] prints out a paper.") return TRUE - if("PRG_eject") + // Eject the ID used to log on to the ID app. + if("PRG_ejectauthid") + if(!computer || !card_slot) + return TRUE + if(user_id_card) + return card_slot.try_eject(user) + else + var/obj/item/I = user.get_active_held_item() + if(istype(I, /obj/item/card/id)) + return card_slot.try_insert(I, user) + // Eject the ID being modified. + if("PRG_ejectmodid") if(!computer || !card_slot2) - return + return TRUE if(target_id_card) GLOB.data_core.manifest_modify(target_id_card.registered_name, target_id_card.assignment) return card_slot2.try_eject(user) @@ -162,156 +156,113 @@ var/obj/item/I = user.get_active_held_item() if(istype(I, /obj/item/card/id)) return card_slot2.try_insert(I, user) - return FALSE + return TRUE + // Used to fire someone. Wipes all access from their card and modifies their assignment. if("PRG_terminate") - if(!computer || !authenticated) - return + if(!computer || !authenticated_user) + return TRUE if(minor) - if(!(target_id_card.assignment in head_subordinates) && target_id_card.assignment != "Assistant") - return + if(!(target_id_card.trim?.assignment in head_subordinates) && target_id_card.trim?.assignment != "Assistant") + to_chat(usr, "Software error: You do not have the necessary permissions to demote this card.") + return TRUE + + // Set the new assignment then remove the trim. + target_id_card.assignment = "Demoted" + SSid_access.remove_trim_from_card(target_id_card) - target_id_card.access -= get_all_centcom_access() + get_all_accesses() - target_id_card.assignment = "Unassigned" - target_id_card.update_label() playsound(computer, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE) return TRUE + // Change ID card assigned name. if("PRG_edit") - if(!computer || !authenticated || !target_id_card) - return + if(!computer || !authenticated_user || !target_id_card) + return TRUE + + var/old_name = target_id_card.registered_name // Sanitize the name first. We're not using the full sanitize_name proc as ID cards can have a wider variety of things on them that // would not pass as a formal character name, but would still be valid on an ID card created by a player. var/new_name = sanitize(params["name"]) + + if(!new_name) + target_id_card.registered_name = null + playsound(computer, "terminal_type", 50, FALSE) + target_id_card.update_label() + // We had a name before and now we have no name, so this will unassign the card and we update the icon. + if(old_name) + target_id_card.update_icon() + return TRUE + // However, we are going to reject bad names overall including names with invalid characters in them, while allowing numbers. new_name = reject_bad_name(new_name, allow_numbers = TRUE) if(!new_name) to_chat(usr, "Software error: The ID card rejected the new name as it contains prohibited characters.") - return + return TRUE target_id_card.registered_name = new_name - target_id_card.update_label() playsound(computer, "terminal_type", 50, FALSE) + target_id_card.update_label() + // Card wasn't assigned before and now it is, so update the icon accordingly. + if(!old_name) + target_id_card.update_icon() return TRUE + // Change age if("PRG_age") - if(!computer || !authenticated || !target_id_card) - return + if(!computer || !authenticated_user || !target_id_card) + return TRUE target_id_card.registered_age = params["id_age"] playsound(computer, "terminal_type", 50, FALSE) return TRUE + // Change assignment if("PRG_assign") - if(!computer || !authenticated || !target_id_card) - return - var/target = params["assign_target"] - if(!target) - return - - if(target == "Custom") - // Sanitize the custom assignment name first. - var/custom_name = sanitize(params["custom_name"]) - // However, we are going to assignments containing bad text overall. - custom_name = reject_bad_text(custom_name) - - if(!custom_name) - to_chat(usr, "Software error: The ID card rejected the new custom assignment as it contains prohibited characters.") - else - target_id_card.assignment = custom_name - target_id_card.update_label() - else - if(minor && !(target in head_subordinates)) - return - var/list/new_access = list() - if(is_centcom) - new_access = get_centcom_access(target) - else - var/datum/job/job - for(var/jobtype in subtypesof(/datum/job)) - var/datum/job/J = new jobtype - if(J.title == target) - job = J - break - if(!job) - to_chat(user, "No class exists for this job: [target]") - return - new_access = job.get_access() - for(var/logged_access in ACCESS_ALERT_ADMINS) - if(logged_access in new_access) - message_admins("[ADMIN_LOOKUPFLW(user)] assigned the job [job.title] to an ID card [ADMIN_VV(target_id_card)] [(target_id_card.registered_name) ? "belonging to [target_id_card.registered_name]." : "with no registered name."]") - break - LOG_ID_ACCESS_CHANGE(usr, target_id_card, "assigned the job [job.title]") - target_id_card.access -= get_all_centcom_access() + get_all_accesses() - target_id_card.access |= new_access - target_id_card.assignment = target - target_id_card.update_label() - playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) - return TRUE - if("PRG_access") - if(!computer || !authenticated) - return - var/access_type = text2num(params["access_target"]) - if(access_type in (is_centcom ? get_all_centcom_access() : get_all_accesses())) - if(access_type in target_id_card.access) - target_id_card.access -= access_type - else - target_id_card.access |= access_type - if(access_type in ACCESS_ALERT_ADMINS) - message_admins("[ADMIN_LOOKUPFLW(user)] just added [get_access_desc(access_type)] to an ID card [ADMIN_VV(target_id_card)] [(target_id_card.registered_name) ? "belonging to [target_id_card.registered_name]." : "with no registered name."]") - LOG_ID_ACCESS_CHANGE(user, target_id_card, "added [get_access_desc(access_type)]") - playsound(computer, "terminal_type", 50, FALSE) + if(!computer || !authenticated_user || !target_id_card) return TRUE - if("PRG_grantall") - if(!computer || !authenticated || minor) - return - target_id_card.access |= (is_centcom ? get_all_centcom_access() : get_all_accesses()) - - message_admins("[ADMIN_LOOKUPFLW(user)] just added All Access to an ID card [ADMIN_VV(target_id_card)] [(target_id_card.registered_name) ? "belonging to [target_id_card.registered_name]." : "with no registered name."]") - LOG_ID_ACCESS_CHANGE(user, target_id_card, "added All Access") - - playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) + target_id_card.assignment = params["assignment"] + playsound(computer, "terminal_type", 50, FALSE) + target_id_card.update_label() return TRUE - if("PRG_denyall") - if(!computer || !authenticated || minor) - return - target_id_card.access.Cut() - playsound(computer, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE) + // Add/remove access. + if("PRG_access") + if(!computer || !authenticated_user || !target_id_card) + return TRUE + playsound(computer, "terminal_type", 50, FALSE) + var/access_type = params["access_target"] + var/try_wildcard = params["access_wildcard"] + if(access_type in (is_centcom ? SSid_access.get_region_access_list(list(REGION_CENTCOM)) : SSid_access.get_region_access_list(list(REGION_ALL_STATION)))) + if(access_type in target_id_card.access) + target_id_card.remove_access(list(access_type)) + LOG_ID_ACCESS_CHANGE(user, target_id_card, "removed [SSid_access.get_access_desc(access_type)]") + return TRUE + + if(!target_id_card.add_access(list(access_type), try_wildcard)) + to_chat(usr, "ID error: ID card rejected your attempted access modification.") + LOG_ID_ACCESS_CHANGE(user, target_id_card, "failed to add [SSid_access.get_access_desc(access_type)][try_wildcard ? " with wildcard [try_wildcard]" : ""]") + return TRUE + + if(access_type in ACCESS_ALERT_ADMINS) + message_admins("[ADMIN_LOOKUPFLW(user)] just added [SSid_access.get_access_desc(access_type)] to an ID card [ADMIN_VV(target_id_card)] [(target_id_card.registered_name) ? "belonging to [target_id_card.registered_name]." : "with no registered name."]") + LOG_ID_ACCESS_CHANGE(user, target_id_card, "added [SSid_access.get_access_desc(access_type)]") return TRUE - if("PRG_grantregion") - if(!computer || !authenticated) - return - var/region = text2num(params["region"]) - if(isnull(region)) - return + // Apply template to ID card. + if("PRG_template") + if(!computer || !authenticated_user || !target_id_card) + return TRUE - if(is_centcom) - target_id_card.access |= get_all_centcom_access() - message_admins("[ADMIN_LOOKUPFLW(user)] just added CentCom Access to an ID card [ADMIN_VV(target_id_card)] [(target_id_card.registered_name) ? "belonging to [target_id_card.registered_name]." : "with no registered name."]") - LOG_ID_ACCESS_CHANGE(user, target_id_card, "added CentCom access") - else - var/list/region_accesses = get_region_accesses(region) - target_id_card.access |= region_accesses + playsound(computer, "terminal_type", 50, FALSE) + var/template_name = params["name"] - for(var/logged_access in ACCESS_ALERT_ADMINS) - if(logged_access in region_accesses) - message_admins("[ADMIN_LOOKUPFLW(user)] just added [get_region_accesses_name(region)] region access to an ID card [ADMIN_VV(target_id_card)] [(target_id_card.registered_name) ? "belonging to [target_id_card.registered_name]." : "with no registered name."]") + if(!template_name) + return TRUE - LOG_ID_ACCESS_CHANGE(user, target_id_card, "added [get_region_accesses_name(region)] region access") + for(var/trim_path in job_templates) + var/datum/id_trim/trim = SSid_access.trim_singletons_by_path[trim_path] + if(trim.assignment != template_name) + continue + SSid_access.add_trim_access_to_card(target_id_card, trim_path) + return TRUE - playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) return TRUE - if("PRG_denyregion") - if(!computer || !authenticated) - return - var/region = text2num(params["region"]) - if(isnull(region)) - return - if(is_centcom) - target_id_card.access -= get_all_centcom_access() - target_id_card.access -= get_region_accesses(region) - playsound(computer, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE) - return TRUE - - /datum/computer_file/program/card_mod/ui_static_data(mob/user) var/list/data = list() @@ -319,69 +270,25 @@ data["centcom_access"] = is_centcom data["minor"] = target_dept || minor ? TRUE : FALSE - var/list/departments = target_dept - if(is_centcom) - departments = list("CentCom" = get_all_centcom_jobs()) - else if(isnull(departments)) - departments = list( - CARDCON_DEPARTMENT_COMMAND = list("Captain"),//lol - CARDCON_DEPARTMENT_ENGINEERING = GLOB.engineering_positions, - CARDCON_DEPARTMENT_MEDICAL = GLOB.medical_positions, - CARDCON_DEPARTMENT_SCIENCE = GLOB.science_positions, - CARDCON_DEPARTMENT_SECURITY = GLOB.security_positions, - CARDCON_DEPARTMENT_SUPPLY = GLOB.supply_positions, - CARDCON_DEPARTMENT_SERVICE = GLOB.service_positions - ) - data["jobs"] = list() - for(var/department in departments) - var/list/job_list = departments[department] - var/list/department_jobs = list() - for(var/job in job_list) - if(minor && !(job in head_subordinates)) - continue - department_jobs += list(list( - "display_name" = replacetext(job, " ", " "), - "job" = job - )) - if(length(department_jobs)) - data["jobs"][department] = department_jobs - var/list/regions = list() + var/list/tgui_region_data = SSid_access.all_region_access_tgui if(is_centcom) - var/list/accesses = list() - for(var/access in get_all_centcom_access()) - if (get_centcom_access_desc(access)) - accesses += list(list( - "desc" = replacetext(get_centcom_access_desc(access), " ", " "), - "ref" = access, - )) - - regions += list(list( - "name" = "CentCom", - "regid" = 0, - "accesses" = accesses - )) + regions += list(tgui_region_data[REGION_CENTCOM]) else - for(var/i in 1 to 7) - if((minor || target_dept) && !(i in region_access)) + for(var/region in SSid_access.station_regions) + if((minor || target_dept) && !(region in region_access)) continue - - var/list/accesses = list() - for(var/access in get_region_accesses(i)) - if (get_access_desc(access)) - accesses += list(list( - "desc" = replacetext(get_access_desc(access), " ", " "), - "ref" = access, - )) - - regions += list(list( - "name" = get_region_accesses_name(i), - "regid" = i, - "accesses" = accesses - )) + regions += tgui_region_data[region] data["regions"] = regions + + data["accessFlags"] = SSid_access.flags_by_access + data["wildcardFlags"] = SSid_access.wildcard_flags_by_wildcard + data["accessFlagNames"] = SSid_access.access_flag_string_by_flag + data["showBasic"] = TRUE + data["templates"] = job_templates + return data /datum/computer_file/program/card_mod/ui_data(mob/user) @@ -389,22 +296,29 @@ data["station_name"] = station_name() + var/obj/item/computer_hardware/card_slot/card_slot var/obj/item/computer_hardware/card_slot/card_slot2 var/obj/item/computer_hardware/printer/printer if(computer) + card_slot = computer.all_components[MC_CARD] card_slot2 = computer.all_components[MC_CARD2] printer = computer.all_components[MC_PRINT] + data["have_auth_card"] = !!(card_slot) data["have_id_slot"] = !!(card_slot2) data["have_printer"] = !!(printer) else data["have_id_slot"] = FALSE data["have_printer"] = FALSE - data["authenticated"] = authenticated if(!card_slot2) return data //We're just gonna error out on the js side at this point anyway + var/obj/item/card/id/auth_card = card_slot.stored_card + data["authIDName"] = auth_card ? auth_card.name : "-----" + + data["authenticatedUser"] = authenticated_user + var/obj/item/card/id/id_card = card_slot2.stored_card data["has_id"] = !!id_card data["id_name"] = id_card ? id_card.name : "-----" @@ -412,16 +326,17 @@ data["id_rank"] = id_card.assignment ? id_card.assignment : "Unassigned" data["id_owner"] = id_card.registered_name ? id_card.registered_name : "-----" data["access_on_card"] = id_card.access + data["wildcardSlots"] = id_card.wildcard_slots data["id_age"] = id_card.registered_age + if(id_card.trim) + var/datum/id_trim/card_trim = id_card.trim + data["hasTrim"] = TRUE + data["trimAssignment"] = card_trim.assignment ? card_trim.assignment : "" + data["trimAccess"] = card_trim.access ? card_trim.access : list() + else + data["hasTrim"] = FALSE + data["trimAssignment"] = "" + data["trimAccess"] = list() + return data - - - -#undef CARDCON_DEPARTMENT_SERVICE -#undef CARDCON_DEPARTMENT_SECURITY -#undef CARDCON_DEPARTMENT_MEDICAL -#undef CARDCON_DEPARTMENT_SCIENCE -#undef CARDCON_DEPARTMENT_SUPPLY -#undef CARDCON_DEPARTMENT_ENGINEERING -#undef CARDCON_DEPARTMENT_COMMAND diff --git a/code/modules/modular_computers/hardware/card_slot.dm b/code/modules/modular_computers/hardware/card_slot.dm index db5a660a2e6..725b0128ca5 100644 --- a/code/modules/modular_computers/hardware/card_slot.dm +++ b/code/modules/modular_computers/hardware/card_slot.dm @@ -65,6 +65,8 @@ var/mob/living/carbon/human/H = user H.sec_hud_set_ID() + holder.update_slot_icon() + return TRUE @@ -86,11 +88,16 @@ for(var/p in holder.idle_threads) var/datum/computer_file/program/computer_program = p computer_program.event_idremoved(1) + + holder.update_slot_icon() + if(ishuman(user)) var/mob/living/carbon/human/human_user = user human_user.sec_hud_set_ID() + to_chat(user, "You remove the card from \the [src].") playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) + return TRUE /obj/item/computer_hardware/card_slot/attackby(obj/item/I, mob/living/user) diff --git a/code/modules/paperwork/handlabeler.dm b/code/modules/paperwork/handlabeler.dm index 5f7974f4fe9..0618fe7173b 100644 --- a/code/modules/paperwork/handlabeler.dm +++ b/code/modules/paperwork/handlabeler.dm @@ -25,6 +25,7 @@ their_card.registered_name = user.real_name their_card.update_label() + their_card.update_icon() // NOT EVEN DEATH WILL TAKE AWAY THE STAIN user.mind.name += " (suicide)" diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm index 15e6c109b34..e57f0ad81ec 100644 --- a/code/modules/research/designs/misc_designs.dm +++ b/code/modules/research/designs/misc_designs.dm @@ -288,11 +288,11 @@ /datum/design/id name = "Identification Card" - desc = "A card used to provide ID and determine access across the station." + desc = "A card used to provide ID and determine access across the station. Has an integrated digital display and advanced microchips." id = "idcard" build_type = PROTOLATHE materials = list(/datum/material/iron=200, /datum/material/glass = 100) - build_path = /obj/item/card/id + build_path = /obj/item/card/id/advanced category = list("Electronics") departmental_flags = DEPARTMENTAL_FLAG_SERVICE diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index 1789609da15..89dcfe3cda3 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -734,7 +734,9 @@ var/obj/item/implant/radio/syndicate/imp = new(src) imp.implant(SM, user) - SM.access_card = new /obj/item/card/id/syndicate(SM) + // Ugly as sin. Simble mob accesses are for another time. + SM.access_card = new /obj/item/card/id/advanced/chameleon(SM) + SSid_access.apply_trim_to_card(SM, /datum/id_trim/chameleon) ADD_TRAIT(SM.access_card, TRAIT_NODROP, ABSTRACT_ITEM_TRAIT) /obj/item/slimepotion/transference diff --git a/code/modules/ruins/lavaland_ruin_code.dm b/code/modules/ruins/lavaland_ruin_code.dm index 6c88c523470..706117b1fed 100644 --- a/code/modules/ruins/lavaland_ruin_code.dm +++ b/code/modules/ruins/lavaland_ruin_code.dm @@ -133,8 +133,9 @@ ears = /obj/item/radio/headset/syndicate/alt back = /obj/item/storage/backpack r_pocket = /obj/item/gun/ballistic/automatic/pistol - id = /obj/item/card/id/syndicate/anyone + id = /obj/item/card/id/advanced/chameleon implants = list(/obj/item/implant/weapons_auth) + id_trim = /datum/id_trim/chameleon/operative /datum/outfit/lavaland_syndicate/post_equip(mob/living/carbon/human/H) H.faction |= ROLE_SYNDICATE diff --git a/code/modules/ruins/spaceruin_code/hilbertshotel.dm b/code/modules/ruins/spaceruin_code/hilbertshotel.dm index d4aa0a748df..60632c71db1 100644 --- a/code/modules/ruins/spaceruin_code/hilbertshotel.dm +++ b/code/modules/ruins/spaceruin_code/hilbertshotel.dm @@ -496,15 +496,16 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999)) ghost_usable = FALSE oxy_damage = 500 mob_species = /datum/species/skeleton - id_job = "Head Researcher" - id_access = ACCESS_RESEARCH - id_access_list = list(ACCESS_AWAY_GENERIC3, ACCESS_RESEARCH) instant = TRUE - id = /obj/item/card/id/silver + outfit = /datum/outfit/doctorhilbert + +/datum/outfit/doctorhilbert + id = /obj/item/card/id/advanced/silver uniform = /obj/item/clothing/under/rank/rnd/research_director/doctor_hilbert shoes = /obj/item/clothing/shoes/sneakers/brown back = /obj/item/storage/backpack/satchel/leather suit = /obj/item/clothing/suit/toggle/labcoat + id_trim = /datum/id_trim/away/hilbert /obj/item/paper/crumpled/docslogs name = "Research Logs" diff --git a/code/modules/shuttle/arrivals.dm b/code/modules/shuttle/arrivals.dm index e0c982907d4..2da7802598b 100644 --- a/code/modules/shuttle/arrivals.dm +++ b/code/modules/shuttle/arrivals.dm @@ -194,6 +194,14 @@ while(mode != SHUTTLE_CALL && !damaged) stoplag() +/** + * Queues an announcement arrival. + * + * Arguments: + * * mob - The arriving mob. + * * rank - The job of the arriving mob. + * * announce_acting_captain - Bool. If TRUE, the arriving mob is also an acting captain. + */ /obj/docking_port/mobile/arrivals/proc/QueueAnnounce(mob, rank) if(mode != SHUTTLE_CALL) AnnounceArrival(mob, rank) diff --git a/code/modules/shuttle/special.dm b/code/modules/shuttle/special.dm index 77b5a8dcd73..0ff61ce7a6e 100644 --- a/code/modules/shuttle/special.dm +++ b/code/modules/shuttle/special.dm @@ -144,7 +144,7 @@ /mob/living/simple_animal/drone/snowflake/bardrone/Initialize() . = ..() - access_card.access |= ACCESS_CENT_BAR + access_card.add_access(list(ACCESS_CENT_BAR)) become_area_sensitive(ROUNDSTART_TRAIT) RegisterSignal(src, COMSIG_ENTER_AREA, .proc/check_barstaff_godmode) check_barstaff_godmode() @@ -161,10 +161,12 @@ /mob/living/simple_animal/hostile/alien/maid/barmaid/Initialize() . = ..() - access_card = new /obj/item/card/id(src) - var/datum/job/captain/C = new /datum/job/captain - access_card.access = C.get_access() - access_card.access |= ACCESS_CENT_BAR + // Simple bot ID card that can hold all accesses. Someone turn access into a component at some point, please. + access_card = new /obj/item/card/id/advanced/simple_bot(src) + + var/datum/id_trim/job/cap_trim = SSid_access.trim_singletons_by_path[/datum/id_trim/job/captain] + access_card.add_access(cap_trim.access + cap_trim.wildcard_access + list(ACCESS_CENT_BAR)) + ADD_TRAIT(access_card, TRAIT_NODROP, ABSTRACT_ITEM_TRAIT) become_area_sensitive(ROUNDSTART_TRAIT) RegisterSignal(src, COMSIG_ENTER_AREA, .proc/check_barstaff_godmode) diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index d774c37f3bb..fa571a281a3 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -1176,12 +1176,11 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/stealthy_tools/agent_card name = "Agent Identification Card" - desc = "Agent cards prevent artificial intelligences from tracking the wearer, and can copy access \ - from other identification cards. The access is cumulative, so scanning one card does not erase the \ - access gained from another. In addition, they can be forged to display a new assignment and name. \ + desc = "Agent cards prevent artificial intelligences from tracking the wearer, and hold up to 5 wildcards \ + from other identification cards. In addition, they can be forged to display a new assignment, name and trim. \ This can be done an unlimited amount of times. Some Syndicate areas and devices can only be accessed \ with these cards." - item = /obj/item/card/id/syndicate + item = /obj/item/card/id/advanced/chameleon cost = 2 /datum/uplink_item/stealthy_tools/ai_detector diff --git a/code/modules/vehicles/mecha/mecha_topic.dm b/code/modules/vehicles/mecha/mecha_topic.dm index d597875fd04..8a4604e1104 100644 --- a/code/modules/vehicles/mecha/mecha_topic.dm +++ b/code/modules/vehicles/mecha/mecha_topic.dm @@ -173,12 +173,12 @@

Following keycodes are present in this system:

"} for(var/a in operation_req_access) - . += "[get_access_desc(a)] - Delete
" + . += "[SSid_access.get_access_desc(a)] - Delete
" . += "

Following keycodes were detected on portable device:

" for(var/a in id_card.access) if(a in operation_req_access) continue - var/a_name = get_access_desc(a) + var/a_name = SSid_access.get_access_desc(a) if(!a_name) continue //there's some strange access without a name . += "[a_name] - Add
" diff --git a/icons/mob/clothing/belt.dmi b/icons/mob/clothing/belt.dmi index f12803c66b5..4cdc732ac90 100644 Binary files a/icons/mob/clothing/belt.dmi and b/icons/mob/clothing/belt.dmi differ diff --git a/icons/mob/clothing/id.dmi b/icons/mob/clothing/id.dmi new file mode 100644 index 00000000000..c97c4d77bfd Binary files /dev/null and b/icons/mob/clothing/id.dmi differ diff --git a/icons/mob/clothing/id_card.dmi b/icons/mob/clothing/id_card.dmi new file mode 100644 index 00000000000..caced4584a9 Binary files /dev/null and b/icons/mob/clothing/id_card.dmi differ diff --git a/icons/mob/hud.dmi b/icons/mob/hud.dmi index 1c43f5141e6..54bfaa9d48f 100644 Binary files a/icons/mob/hud.dmi and b/icons/mob/hud.dmi differ diff --git a/icons/obj/card.dmi b/icons/obj/card.dmi index a50f1f2062e..6f57eb78946 100644 Binary files a/icons/obj/card.dmi and b/icons/obj/card.dmi differ diff --git a/tgstation.dme b/tgstation.dme index cea829cf440..5e82a1ad350 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -64,6 +64,7 @@ #include "code\__DEFINES\forensics.dm" #include "code\__DEFINES\hud.dm" #include "code\__DEFINES\icon_smoothing.dm" +#include "code\__DEFINES\id_cards.dm" #include "code\__DEFINES\injection.dm" #include "code\__DEFINES\instruments.dm" #include "code\__DEFINES\interaction_flags.dm" @@ -300,6 +301,7 @@ #include "code\controllers\subsystem\fire_burning.dm" #include "code\controllers\subsystem\garbage.dm" #include "code\controllers\subsystem\icon_smooth.dm" +#include "code\controllers\subsystem\id_access.dm" #include "code\controllers\subsystem\idlenpcpool.dm" #include "code\controllers\subsystem\input.dm" #include "code\controllers\subsystem\ipintel.dm" @@ -665,6 +667,13 @@ #include "code\datums\helper_datums\icon_snapshot.dm" #include "code\datums\helper_datums\stack_end_detector.dm" #include "code\datums\helper_datums\teleport.dm" +#include "code\datums\id_trim\_id_trim.dm" +#include "code\datums\id_trim\centcom.dm" +#include "code\datums\id_trim\families.dm" +#include "code\datums\id_trim\jobs.dm" +#include "code\datums\id_trim\misc.dm" +#include "code\datums\id_trim\ruins.dm" +#include "code\datums\id_trim\syndicate.dm" #include "code\datums\keybinding\_defines.dm" #include "code\datums\keybinding\_keybindings.dm" #include "code\datums\keybinding\admin.dm" diff --git a/tgui/packages/tgui-panel/styles/goon/chat-dark.scss b/tgui/packages/tgui-panel/styles/goon/chat-dark.scss index c57ba432581..b663e5fc013 100644 --- a/tgui/packages/tgui-panel/styles/goon/chat-dark.scss +++ b/tgui/packages/tgui-panel/styles/goon/chat-dark.scss @@ -774,6 +774,10 @@ em { width: auto; } +.bigicon { + font-size: 5em; +} + .memo { color: #638500; text-align: center; diff --git a/tgui/packages/tgui-panel/styles/goon/chat-light.scss b/tgui/packages/tgui-panel/styles/goon/chat-light.scss index 53b93065aaf..949a6c43507 100644 --- a/tgui/packages/tgui-panel/styles/goon/chat-light.scss +++ b/tgui/packages/tgui-panel/styles/goon/chat-light.scss @@ -814,6 +814,11 @@ h1.alert, h2.alert { width: auto; } +.bigicon { + height: 4em; + width: auto; +} + .memo { color: #638500; text-align: center; diff --git a/tgui/packages/tgui/interfaces/AirlockElectronics.js b/tgui/packages/tgui/interfaces/AirlockElectronics.js index a448f55e9b2..c37d8513196 100644 --- a/tgui/packages/tgui/interfaces/AirlockElectronics.js +++ b/tgui/packages/tgui/interfaces/AirlockElectronics.js @@ -1,7 +1,7 @@ -import { useBackend } from '../backend'; -import { Button, LabeledList, Section } from '../components'; +import { useBackend, useLocalState } from '../backend'; +import { Button, Flex, Grid, LabeledList, Section, Tabs } from '../components'; import { Window } from '../layouts'; -import { AccessList } from './common/AccessList'; +import { sortBy } from 'common/collections'; export const AirlockElectronics = (props, context) => { const { act, data } = useBackend(context); @@ -58,7 +58,7 @@ export const AirlockElectronics = (props, context) => { - act('set', { @@ -76,3 +76,132 @@ export const AirlockElectronics = (props, context) => { ); }; + + +const diffMap = { + 0: { + icon: 'times-circle', + color: 'bad', + }, + 1: { + icon: 'stop-circle', + color: null, + }, + 2: { + icon: 'check-circle', + color: 'good', + }, +}; + +export const AirlockAccessList = (props, context) => { + const { + accesses = [], + selectedList = [], + accessMod, + grantAll, + denyAll, + grantDep, + denyDep, + } = props; + const [ + selectedAccessName, + setSelectedAccessName, + ] = useLocalState(context, 'accessName', accesses[0]?.name); + const selectedAccess = accesses + .find(access => access.name === selectedAccessName); + const selectedAccessEntries = sortBy( + entry => entry.desc, + )(selectedAccess?.accesses || []); + + const checkAccessIcon = accesses => { + let oneAccess = false; + let oneInaccess = false; + for (let element of accesses) { + if (selectedList.includes(element.ref)) { + oneAccess = true; + } + else { + oneInaccess = true; + } + } + if (!oneAccess && oneInaccess) { + return 0; + } + else if (oneAccess && oneInaccess) { + return 1; + } + else { + return 2; + } + }; + + return ( +
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ChameleonCard.js b/tgui/packages/tgui/interfaces/ChameleonCard.js new file mode 100644 index 00000000000..7a57bcd769e --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChameleonCard.js @@ -0,0 +1,68 @@ +import { useBackend } from '../backend'; +import { AccessList } from './common/AccessList'; +import { Window } from '../layouts'; + +export const ChameleonCard = (props, context) => { + const { act, data } = useBackend(context); + + const { + accesses, + selectedList, + wildcardFlags, + wildcardSlots, + trimAccess, + accessFlags, + accessFlagNames, + showBasic, + ourAccess, + theftAccess, + ourTrimAccess, + } = data; + + const parsedAccess = accesses.flatMap(region => { + const regionName = region.name; + const regionAccess = region.accesses; + const parsedRegion = { + name: regionName, + accesses: [], + }; + parsedRegion.accesses = regionAccess.filter(access => { + // Snip everything that's part of our trim. + if (ourTrimAccess.includes(access.ref)) { + return false; + } + // Add anything not part of our trim that's an access (assumed wildcard) + // Also add any access on the ID card we're stealing from. + if (ourAccess.includes(access.ref) || theftAccess.includes(access.ref)) { + return true; + } + return false; + }); + if (parsedRegion.accesses.length) { + return parsedRegion; + } + return []; + }); + + return ( + + + act('mod_access', { + access_target: ref, + access_wildcard: wildcard, + })} /> + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/NtosCard.js b/tgui/packages/tgui/interfaces/NtosCard.js index acb2c62793d..058565f66c4 100644 --- a/tgui/packages/tgui/interfaces/NtosCard.js +++ b/tgui/packages/tgui/interfaces/NtosCard.js @@ -1,5 +1,5 @@ -import { useBackend, useLocalState } from '../backend'; -import { Box, Button, Flex, Input, NoticeBox, NumberInput, Section, Tabs } from '../components'; +import { useBackend, useSharedState } from '../backend'; +import { Box, Button, Dropdown, Input, NoticeBox, NumberInput, Section, Stack, Tabs } from '../components'; import { NtosWindow } from '../layouts'; import { AccessList } from './common/AccessList'; @@ -7,7 +7,7 @@ export const NtosCard = (props, context) => { return ( + height={670}> @@ -17,24 +17,22 @@ export const NtosCard = (props, context) => { export const NtosCardContent = (props, context) => { const { act, data } = useBackend(context); - const [tab, setTab] = useLocalState(context, 'tab', 1); const { - authenticated, + authenticatedUser, regions = [], access_on_card = [], - jobs = {}, - id_rank, - id_owner, has_id, - have_printer, have_id_slot, - id_name, - id_age, + wildcardSlots, + wildcardFlags, + trimAccess, + accessFlags, + accessFlagNames, + showBasic, } = data; - const [ - selectedDepartment, - setSelectedDepartment, - ] = useLocalState(context, 'department', Object.keys(jobs)[0]); + + const { templates } = data || {}; + if (!have_id_slot) { return ( @@ -42,19 +40,179 @@ export const NtosCardContent = (props, context) => { ); } - const departmentJobs = jobs[selectedDepartment] || []; + + const [ + selectedTab, + ] = useSharedState(context, "selectedTab", "login"); + return ( <> -
+ + + + + + {(selectedTab === "login") && ( + + ) || (selectedTab === "modify") && ( + + )} + + + {(!!has_id && !!authenticatedUser) && ( +
+ } > + +
)} + + + {(!!has_id && !!authenticatedUser) && ( + + act('PRG_terminate')} /> + } + accessMod={(ref, wildcard) => act('PRG_access', { + access_target: ref, + access_wildcard: wildcard, + })} /> + + )} + + + + ); +}; + +const IDCardTabs = (props, context) => { + const [ + selectedTab, + setSelectedTab, + ] = useSharedState(context, "selectedTab", "login"); + + return ( + + setSelectedTab("login")}> + Login ID + + setSelectedTab("modify")}> + Target ID + + + ); +}; + +export const IDCardLogin = (props, context) => { + const { act, data } = useBackend(context); + const { + authenticatedUser, + has_id, + have_printer, + authIDName, + } = data; + + return ( +
+
+ ); +}; + +const IDCardTarget = (props, context) => { + const { act, data } = useBackend(context); + const { + authenticatedUser, + id_rank, + id_owner, + has_id, + id_name, + id_age, + } = data; + + return ( +
+
- {(!!has_id && !!authenticated) && ( - - - setTab(1)}> - Access - - setTab(2)}> - Jobs - - - {tab === 1 && ( - act('PRG_access', { - access_target: ref, - })} - grantAll={() => act('PRG_grantall')} - denyAll={() => act('PRG_denyall')} - grantDep={dep => act('PRG_grantregion', { - region: dep, - })} - denyDep={dep => act('PRG_denyregion', { - region: dep, - })} /> - )} - {tab === 2 && ( -
act('PRG_terminate')} /> - )}> - + + + + Assignment: + + + act('PRG_assign', { - assign_target: 'Custom', - custom_name: value, + mt={1} + value={id_rank} + onInput={(e, value) => act('PRG_assign', { + assignment: value, })} /> - - - - {Object.keys(jobs).map(department => ( - setSelectedDepartment(department)}> - {department} - - ))} - - - - {departmentJobs.map(job => ( -
- )} -
+ + + )} - +
+ ); +}; + +const TemplateDropdown = (props, context) => { + const { act } = useBackend(context); + const { + templates, + } = props; + + const templateKeys = Object.keys(templates); + + if (!templateKeys.length) { + return; + } + + return ( + + + { + return templates[path]; + }) + } + onSelected={sel => act("PRG_template", { + name: sel, + })} /> + + ); }; diff --git a/tgui/packages/tgui/interfaces/PaintingMachine.js b/tgui/packages/tgui/interfaces/PaintingMachine.js new file mode 100644 index 00000000000..1e817937982 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PaintingMachine.js @@ -0,0 +1,128 @@ +import { useBackend, useSharedState } from '../backend'; +import { Window } from '../layouts'; +import { Button, Dropdown, Section, Stack } from '../components'; + +export const PaintingMachine = (props, context) => { + const { act, data } = useBackend(context); + + const { + pdaTypes, + cardTrims, + hasPDA, + pdaName, + hasID, + idName, + } = data; + + const [ + selectedPDA, + ] = useSharedState(context, "pdaSelection"); + + const [ + selectedTrim, + ] = useSharedState(context, "trimSelection"); + + return ( + + +
act("trim_pda", { + selection: selectedPDA, + })} /> + }> + + + act("eject_pda")} /> + + + + + +
+
+ act("trim_card", { + selection: selectedTrim, + })} /> +
+
+
+ ); +}; + +export const EjectButton = (props, context) => { + const { + name, + onClickEject, + } = props; + + return ( +