From a05f7e863a31dd0a411184b3a4ea7b7426892d42 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Wed, 3 Mar 2021 02:54:25 +0100 Subject: [PATCH] [MIRROR] Fully implements the ID Card design document (#3729) * Fully implements the ID Card design document * Oh fuck that hurt. Co-authored-by: Timberpoes Co-authored-by: Gandalf2k15 --- .../RandomRuins/AnywhereRuins/golem_ship.dmm | 4 +- .../IceRuins/icemoon_surface_engioutpost.dmm | 9 +- .../icemoon_underground_abandoned_village.dmm | 2 +- _maps/RandomRuins/SpaceRuins/bigderelict1.dmm | 1 - .../RandomRuins/SpaceRuins/forgottenship.dmm | 2 +- _maps/RandomZLevels/research.dmm | 32 +- _maps/RandomZLevels/spacebattle.dmm | 18 +- .../map_files/Deltastation/DeltaStation2.dmm | 9 +- .../map_files/IceBoxStation/IceBoxStation.dmm | 17 +- _maps/map_files/KiloStation/KiloStation.dmm | 9 +- _maps/map_files/MetaStation/MetaStation.dmm | 12 +- .../MetaStation/MetaStation_skyrat.dmm | 2 +- _maps/map_files/debug/multiz.dmm | 2 +- _maps/map_files/debug/runtimestation.dmm | 8 +- _maps/map_files/generic/CentCom_skyrat_z2.dmm | 10 +- code/__DEFINES/access.dm | 371 +++++ code/__DEFINES/id_cards.dm | 38 + code/__DEFINES/misc.dm | 24 +- code/__DEFINES/subsystems.dm | 1 + code/__HELPERS/game.dm | 4 +- code/__HELPERS/icons.dm | 9 +- code/__HELPERS/names.dm | 2 +- code/controllers/subsystem/id_access.dm | 457 ++++++ code/controllers/subsystem/job.dm | 87 +- code/controllers/subsystem/ticker.dm | 60 +- code/controllers/subsystem/traumas.dm | 4 +- code/datums/id_trim/_id_trim.dm | 13 + code/datums/id_trim/centcom.dm | 170 +++ code/datums/id_trim/families.dm | 8 + code/datums/id_trim/jobs.dm | 472 ++++++ code/datums/id_trim/misc.dm | 57 + code/datums/id_trim/ruins.dm | 97 ++ code/datums/id_trim/syndicate.dm | 50 + code/datums/mind.dm | 2 +- code/datums/outfit.dm | 7 + code/game/atoms.dm | 10 + code/game/gamemodes/clown_ops/clown_ops.dm | 7 +- code/game/gamemodes/nuclear/nuclear.dm | 6 +- code/game/gamemodes/sandbox/airlock_maker.dm | 5 +- code/game/gamemodes/sandbox/h_sandbox.dm | 14 +- code/game/machinery/PDApainter.dm | 354 ++++- .../game/machinery/computer/communications.dm | 6 +- .../machinery/computer/prisoner/_prisoner.dm | 8 +- code/game/machinery/computer/security.dm | 4 +- .../machinery/doors/airlock_electronics.dm | 28 +- code/game/machinery/gulag_item_reclaimer.dm | 4 +- .../machinery/porta_turret/portable_turret.dm | 2 +- code/game/machinery/recycler.dm | 4 +- code/game/objects/items/cards_ids.dm | 1278 +++++++++++------ code/game/objects/items/control_wand.dm | 21 +- code/game/objects/items/devices/PDA/PDA.dm | 16 +- code/game/objects/items/storage/boxes.dm | 18 +- code/game/objects/items/storage/secure.dm | 56 +- .../game/objects/items/storage/uplink_kits.dm | 8 +- code/game/objects/items/storage/wallets.dm | 57 +- .../structures/crates_lockers/closets.dm | 2 +- .../crates_lockers/closets/secure/security.dm | 1 - .../objects/structures/ghost_role_spawners.dm | 20 +- code/modules/admin/verbs/debug.dm | 42 +- .../antagonists/fugitive/fugitive_outfits.dm | 12 +- code/modules/antagonists/gang/outfits.dm | 7 +- .../antagonists/highlander/highlander.dm | 6 +- .../traitor/equipment/contractor.dm | 3 +- code/modules/awaymissions/capture_the_flag.dm | 5 +- code/modules/awaymissions/corpse.dm | 42 +- .../awaymissions/mission_code/Academy.dm | 2 +- .../awaymissions/mission_code/snowdin.dm | 4 +- code/modules/cargo/order.dm | 2 +- code/modules/cargo/packs.dm | 17 +- code/modules/clothing/chameleon.dm | 81 +- code/modules/clothing/outfits/ert.dm | 34 +- code/modules/clothing/outfits/standard.dm | 75 +- code/modules/clothing/outfits/vr.dm | 11 +- code/modules/clothing/outfits/vv_outfit.dm | 10 +- code/modules/economy/account.dm | 7 +- code/modules/jobs/access.dm | 311 +--- code/modules/jobs/job_types/_job.dm | 77 +- code/modules/jobs/job_types/assistant.dm | 10 +- .../jobs/job_types/atmospheric_technician.dm | 12 +- code/modules/jobs/job_types/bartender.dm | 3 +- code/modules/jobs/job_types/botanist.dm | 7 +- code/modules/jobs/job_types/captain.dm | 14 +- .../jobs/job_types/cargo_technician.dm | 7 +- code/modules/jobs/job_types/chaplain.dm | 4 +- code/modules/jobs/job_types/chemist.dm | 4 +- code/modules/jobs/job_types/chief_engineer.dm | 17 +- .../jobs/job_types/chief_medical_officer.dm | 19 +- code/modules/jobs/job_types/clown.dm | 5 +- code/modules/jobs/job_types/cook.dm | 4 +- code/modules/jobs/job_types/curator.dm | 4 +- code/modules/jobs/job_types/detective.dm | 4 +- code/modules/jobs/job_types/geneticist.dm | 4 +- .../jobs/job_types/head_of_personnel.dm | 68 +- .../jobs/job_types/head_of_security.dm | 21 +- code/modules/jobs/job_types/janitor.dm | 4 +- code/modules/jobs/job_types/lawyer.dm | 3 +- code/modules/jobs/job_types/medical_doctor.dm | 4 +- code/modules/jobs/job_types/mime.dm | 4 +- code/modules/jobs/job_types/paramedic.dm | 8 +- code/modules/jobs/job_types/prisoner.dm | 6 +- code/modules/jobs/job_types/psychologist.dm | 6 +- code/modules/jobs/job_types/quartermaster.dm | 23 +- .../jobs/job_types/research_director.dm | 25 +- code/modules/jobs/job_types/roboticist.dm | 6 +- code/modules/jobs/job_types/scientist.dm | 7 +- .../jobs/job_types/security_officer.dm | 31 +- code/modules/jobs/job_types/shaft_miner.dm | 8 +- .../jobs/job_types/station_engineer.dm | 15 +- code/modules/jobs/job_types/virologist.dm | 4 +- code/modules/jobs/job_types/warden.dm | 8 +- code/modules/mining/laborcamp/laborstacker.dm | 12 +- code/modules/mining/machine_vending.dm | 22 +- code/modules/mining/minebot.dm | 6 +- .../modules/mob/dead/new_player/new_player.dm | 11 +- .../mob/living/carbon/human/examine.dm | 8 +- code/modules/mob/living/carbon/human/human.dm | 4 - .../mob/living/carbon/human/human_helpers.dm | 2 - .../living/carbon/human/human_update_icons.dm | 14 +- .../mob/living/simple_animal/bot/bot.dm | 21 +- .../mob/living/simple_animal/bot/cleanbot.dm | 7 +- .../mob/living/simple_animal/bot/firebot.dm | 10 +- .../mob/living/simple_animal/bot/floorbot.dm | 11 +- .../mob/living/simple_animal/bot/honkbot.dm | 8 +- .../living/simple_animal/bot/hygienebot.dm | 10 +- .../mob/living/simple_animal/bot/medbot.dm | 10 +- .../mob/living/simple_animal/bot/mulebot.dm | 9 +- .../mob/living/simple_animal/bot/secbot.dm | 10 +- .../mob/living/simple_animal/corpse.dm | 30 +- .../simple_animal/friendly/drone/_drone.dm | 8 +- .../hostile/mining_mobs/hivelord.dm | 1 - code/modules/mob/mob.dm | 1 + .../computers/item/computer.dm | 54 +- .../file_system/programs/card.dm | 423 +++--- .../modular_computers/hardware/card_slot.dm | 7 + code/modules/paperwork/handlabeler.dm | 1 + code/modules/research/designs/misc_designs.dm | 4 +- .../research/xenobiology/xenobiology.dm | 4 +- code/modules/ruins/lavaland_ruin_code.dm | 3 +- .../ruins/spaceruin_code/hilbertshotel.dm | 9 +- code/modules/shuttle/arrivals.dm | 8 + code/modules/shuttle/special.dm | 12 +- code/modules/uplink/uplink_items.dm | 7 +- code/modules/vehicles/mecha/mecha_topic.dm | 4 +- icons/mob/clothing/belt.dmi | Bin 42862 -> 42964 bytes icons/mob/clothing/id.dmi | Bin 0 -> 371 bytes icons/mob/clothing/id_card.dmi | Bin 0 -> 551 bytes icons/mob/hud.dmi | Bin 18791 -> 18365 bytes icons/mob/hud_old.dmi | Bin 0 -> 18791 bytes icons/obj/card.dmi | Bin 13462 -> 12292 bytes icons/obj/card_old.dmi | Bin 0 -> 13462 bytes .../master_files/code/datums/id_trim/jobs.dm | 101 ++ .../code/modules/clothing/outfits/ert.dm | 4 +- .../assaultops/code/assaultops_outfits.dm | 6 +- .../code/modules/jobs/job_types/blueshield.dm | 13 +- modular_skyrat/modules/card/access.dm | 24 - modular_skyrat/modules/card/card.dm | 82 -- modular_skyrat/modules/card/card.dmi | Bin 23918 -> 0 bytes .../cloning/code/modules/jobs/access.dm | 19 - .../code/modules/jobs/job_types/chemist.dm | 3 - .../jobs/job_types/chief_medical_officer.dm | 4 - .../code/modules/jobs/job_types/geneticist.dm | 4 - .../modules/jobs/job_types/medical_doctor.dm | 4 - .../code/modules/jobs/job_types/paramedic.dm | 4 - .../code/modules/jobs/job_types/virologist.dm | 3 - .../modules/cryosleep/code/cryopod.dm | 2 +- .../objects/structures/ghost_role_spawners.dm | 19 +- modular_skyrat/modules/mapping/code/genpop.dm | 2 +- .../modules/jobs/job_types/shuttle_pilot.dm | 9 +- .../modules/sec_haul/code/misc/bulletbot.dm | 6 +- .../code/peacekeeper/armadyne_clothing.dm | 39 +- .../code/security_medic/security_medic.dm | 4 +- .../security_sergeant/security_sergeant.dm | 4 +- tgstation.dme | 19 +- .../tgui-panel/styles/goon/chat-dark.scss | 4 + .../tgui-panel/styles/goon/chat-light.scss | 5 + .../tgui/interfaces/AirlockElectronics.js | 137 +- .../packages/tgui/interfaces/ChameleonCard.js | 68 + tgui/packages/tgui/interfaces/NtosCard.js | 346 +++-- .../tgui/interfaces/PaintingMachine.js | 128 ++ .../tgui/interfaces/common/AccessList.js | 362 +++-- tgui/yarn.lock | 24 +- 181 files changed, 5042 insertions(+), 2274 deletions(-) create mode 100644 code/__DEFINES/id_cards.dm create mode 100644 code/controllers/subsystem/id_access.dm create mode 100644 code/datums/id_trim/_id_trim.dm create mode 100644 code/datums/id_trim/centcom.dm create mode 100644 code/datums/id_trim/families.dm create mode 100644 code/datums/id_trim/jobs.dm create mode 100644 code/datums/id_trim/misc.dm create mode 100644 code/datums/id_trim/ruins.dm create mode 100644 code/datums/id_trim/syndicate.dm mode change 100755 => 100644 code/modules/jobs/job_types/captain.dm create mode 100644 icons/mob/clothing/id.dmi create mode 100644 icons/mob/clothing/id_card.dmi create mode 100644 icons/mob/hud_old.dmi create mode 100644 icons/obj/card_old.dmi create mode 100644 modular_skyrat/master_files/code/datums/id_trim/jobs.dm delete mode 100644 modular_skyrat/modules/card/access.dm delete mode 100644 modular_skyrat/modules/card/card.dm delete mode 100644 modular_skyrat/modules/card/card.dmi delete mode 100644 modular_skyrat/modules/cloning/code/modules/jobs/access.dm delete mode 100644 modular_skyrat/modules/cloning/code/modules/jobs/job_types/chemist.dm delete mode 100644 modular_skyrat/modules/cloning/code/modules/jobs/job_types/chief_medical_officer.dm delete mode 100644 modular_skyrat/modules/cloning/code/modules/jobs/job_types/geneticist.dm delete mode 100644 modular_skyrat/modules/cloning/code/modules/jobs/job_types/medical_doctor.dm delete mode 100644 modular_skyrat/modules/cloning/code/modules/jobs/job_types/paramedic.dm delete mode 100644 modular_skyrat/modules/cloning/code/modules/jobs/job_types/virologist.dm create mode 100644 tgui/packages/tgui/interfaces/ChameleonCard.js create mode 100644 tgui/packages/tgui/interfaces/PaintingMachine.js 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 eb35469fa78..cfdc43336c6 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -37973,7 +37973,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, @@ -39559,9 +39559,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 @@ -41435,6 +41432,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" = ( @@ -52900,6 +52900,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" = ( @@ -54513,6 +54514,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/MetaStation/MetaStation_skyrat.dmm b/_maps/map_files/MetaStation/MetaStation_skyrat.dmm index 3dada88c4b3..056e9296694 100644 --- a/_maps/map_files/MetaStation/MetaStation_skyrat.dmm +++ b/_maps/map_files/MetaStation/MetaStation_skyrat.dmm @@ -37994,7 +37994,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, 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/_maps/map_files/generic/CentCom_skyrat_z2.dmm b/_maps/map_files/generic/CentCom_skyrat_z2.dmm index f63bccb52d9..e4829f5ee5b 100644 --- a/_maps/map_files/generic/CentCom_skyrat_z2.dmm +++ b/_maps/map_files/generic/CentCom_skyrat_z2.dmm @@ -4777,11 +4777,11 @@ dir = 1 }, /obj/structure/table/reinforced, -/obj/item/card/id/syndicate_command/crew_id, -/obj/item/card/id/syndicate_command/crew_id, -/obj/item/card/id/syndicate_command/crew_id, -/obj/item/card/id/syndicate_command/crew_id, -/obj/item/card/id/syndicate_command/crew_id, +/obj/item/card/id/advanced/chameleon, +/obj/item/card/id/advanced/chameleon, +/obj/item/card/id/advanced/chameleon, +/obj/item/card/id/advanced/chameleon, +/obj/item/card/id/advanced/chameleon, /turf/open/floor/mineral/plastitanium/red, /area/cruiser_dock/bridge) "mJ" = ( diff --git a/code/__DEFINES/access.dm b/code/__DEFINES/access.dm index 3145e0397b9..037a024883b 100644 --- a/code/__DEFINES/access.dm +++ b/code/__DEFINES/access.dm @@ -155,3 +155,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 7f3f48249b9..dbad4bf4c9e 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -13,16 +13,17 @@ #define REVERSE_DIR(dir) ( ((dir & 85) << 1) | ((dir & 170) >> 1) ) //Human Overlays Indexes///////// -#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 BANDAGE_LAYER 21 //Overlays related to wounds, bandages and splints too //SKYRAT EDIT ADDITION - MEDICAL -#define ID_LAYER 20 //lmao at the idiot who put both ids and hands on the same layer +#define MUTATIONS_LAYER 30 //mutations. Tk headglows, cold resistance glow, etc +#define BODY_BEHIND_LAYER 29 //certain mutantrace features (tail when looking south) that must appear behind the body parts +#define BODYPARTS_LAYER 28 //Initially "AUGMENTS", this was repurposed to be a catch-all bodyparts flag +#define BODY_ADJ_LAYER 27 //certain mutantrace features (snout, body markings) that must appear above the body parts +#define BODY_LAYER 26 //underwear, undershirts, socks, eyes, lips(makeup) +#define FRONT_MUTATIONS_LAYER 25 //mutations that should appear above body, body_adj and bodyparts layer (e.g. laser eyes) +#define DAMAGE_LAYER 24 //damage indicators (cuts and burns) +#define UNIFORM_LAYER 23 +#define BANDAGE_LAYER 22 //Overlays related to wounds, bandages and splints too //SKYRAT EDIT ADDITION - MEDICAL +#define ID_LAYER 21 +#define ID_CARD_LAYER 20 #define HANDS_PART_LAYER 19 #define GLOVES_LAYER 18 #define SHOES_LAYER 17 @@ -42,7 +43,8 @@ #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 29 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_; //SKYRAT EDIT CHANGE - 29 from 28. Added BANDAGE_LAYER +#define TOTAL_LAYERS 30 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_; //SKYRAT EDIT CHANGE - 30 from 29. Added BANDAGE_LAYER + //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 62782f09825..fd3e6ac3c21 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -1123,8 +1123,8 @@ 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) - /*if (!thing) - SKYRAT EDIT REMOVAL BEGIN - ICON2HTML +/proc/icon2html(thing, target, icon_state, dir = SOUTH, frame = 1, moving = FALSE, sourceonly = FALSE, extra_classes = null) + if (!thing) return var/key @@ -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,8 +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 ""*/ //- SKYRAT EDIT REMOVAL END - 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 14bab19b42e..1a0fffdfcca 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)) @@ -435,9 +464,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) @@ -492,7 +520,7 @@ SUBSYSTEM_DEF(job) if (M.client && job.no_dresscode && job.loadout) packed_items = M.client.prefs.equip_preference_loadout(living_mob,FALSE,job,blacklist=job.blacklist_dresscode_slots,initial=TRUE) //SKYRAT EDIT ADDITION END - 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) @@ -767,3 +795,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 0a2104aedd0..8a2f32ac736 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -385,23 +385,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..f0747287b6a --- /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 //SKYRAT EDIT: ID TRIM ACCESS OVERRIDEN IN MODULAR JOBS.DM + 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 //SKYRAT EDIT: ID TRIM ACCESS OVERRIDEN IN MODULAR JOBS.DM + 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 //SKYRAT EDIT: ID TRIM ACCESS OVERRIDEN IN MODULAR JOBS.DM + 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 //SKYRAT EDIT: ID TRIM ACCESS OVERRIDEN IN MODULAR JOBS.DM + 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 //SKYRAT EDIT: ID TRIM ACCESS OVERRIDEN IN MODULAR JOBS.DM + 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 d86d60d5812..15679626ebc 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -519,7 +519,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 f07727c8591..f816fc97eea 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 efa616ad46f..d479b97853f 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) @@ -303,7 +303,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) @@ -312,7 +312,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 df01c8465ff..5b36346a870 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -741,7 +741,7 @@ What a mess.*/ if((istype(active1, /datum/data/record) && L.Find(rank))) temp = "
Rank:
" temp += "" else @@ -753,7 +753,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 e3b2345c9a7..ee52abb8b6b 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,421 +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 - // SKYRAT EDIT: Start - Genpop IDs - access = list(ACCESS_ENTER_GENPOP) - var/sentence = 0 //When world.time is greater than this number, the card will have its ACCESS_ENTER_GENPOP access replaced with ACCESS_LEAVE_GENPOP the next time it's checked, unless this value is 0/null - var/crime= "\[REDACTED\]" - -/obj/item/card/id/prisoner/GetAccess() - if((sentence && world.time >= sentence) || (goal && points >= goal)) - access = list(ACCESS_LEAVE_GENPOP) - return ..() - -/obj/item/card/id/prisoner/examine(mob/user) - . = ..() - if(sentence && world.time < sentence) - to_chat(user, "You're currently serving a sentence for [crime]. [DisplayTimeText(sentence - world.time)] left.") - else if(goal) - to_chat(user, "You have accumulated [points] out of the [goal] points you need for freedom.") - else if(!sentence) - to_chat(user, "You are currently serving a permanent sentence for [crime].") - else - to_chat(user, "Your sentence is up! You're free!") -/* -/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/process() - if(!sentence) - STOP_PROCESSING(SSobj, src) - return - if(world.time >= sentence) - if(prob(90)) - playsound(loc, 'sound/machines/ping.ogg', 50, 1) - if(isliving(loc)) - to_chat(loc, "[src] buzzes: You have served your sentence! You may now exit prison through the turnstiles and collect your belongings.") - else - playsound(loc, 'modular_skyrat/modules/mapping/code/quest_succeeded.ogg', 50, 1) - if(isliving(loc)) - to_chat(loc, "[src]Quest Completed! Serve your prison sentence. You may now leave the prison through the turnstiles and return this ID to the locker to retrieve your belongings.") - STOP_PROCESSING(SSobj, src) - return - // SKYRAT EDIT: End - Genpop IDs - -/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" @@ -734,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 @@ -791,3 +674,506 @@ 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 + // SKYRAT EDIT: Start - Genpop IDs + access = list(ACCESS_ENTER_GENPOP) + var/sentence = 0 //When world.time is greater than this number, the card will have its ACCESS_ENTER_GENPOP access replaced with ACCESS_LEAVE_GENPOP the next time it's checked, unless this value is 0/null + var/crime= "\[REDACTED\]" + +/obj/item/card/id/advanced/prisoner/GetAccess() + if((sentence && world.time >= sentence) || (goal && points >= goal)) + access = list(ACCESS_LEAVE_GENPOP) + return ..() + +/obj/item/card/id/advanced/prisoner/examine(mob/user) + . = ..() + if(sentence && world.time < sentence) + to_chat(user, "You're currently serving a sentence for [crime]. [DisplayTimeText(sentence - world.time)] left.") + else if(goal) + to_chat(user, "You have accumulated [points] out of the [goal] points you need for freedom.") + else if(!sentence) + to_chat(user, "You are currently serving a permanent sentence for [crime].") + else + to_chat(user, "Your sentence is up! You're free!") + +/obj/item/card/id/advanced/prisoner/process() + if(!sentence) + STOP_PROCESSING(SSobj, src) + return + if(world.time >= sentence) + if(prob(90)) + playsound(loc, 'sound/machines/ping.ogg', 50, 1) + if(isliving(loc)) + to_chat(loc, "[src] buzzes: You have served your sentence! You may now exit prison through the turnstiles and collect your belongings.") + else + playsound(loc, 'modular_skyrat/modules/mapping/code/quest_succeeded.ogg', 50, 1) + if(isliving(loc)) + to_chat(loc, "[src]Quest Completed! Serve your prison sentence. You may now leave the prison through the turnstiles and return this ID to the locker to retrieve your belongings.") + STOP_PROCESSING(SSobj, src) + return + // SKYRAT EDIT: End - Genpop IDs + +/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 14952052c6b..13989fa2706 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//SKYRAT EDIT - ICON OVERRIDEN BY AESTHETICS - SEE MODULE - 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 96979eb9aaa..e19ca5b3038 100644 --- a/code/game/objects/items/storage/boxes.dm +++ b/code/game/objects/items/storage/boxes.dm @@ -543,7 +543,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 @@ -570,7 +570,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" @@ -580,13 +580,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 131a7fc2d03..7c414497870 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -17,7 +17,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 @@ -80,7 +79,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) @@ -88,7 +86,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 @@ -128,7 +126,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) @@ -138,14 +135,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) @@ -260,7 +255,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 22557345324..40f35e978ae 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 6f445df9c2a..0ed565f65ee 100755 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -32,7 +32,6 @@ new /obj/item/storage/belt/sabre(src) new /obj/item/storage/box/gunset/pdh_captain(src) //SKYRAT EDIT CHANGE - SEC_HAUL 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 9498c5053b8..b2c57b659c4 100644 --- a/code/game/objects/structures/ghost_role_spawners.dm +++ b/code/game/objects/structures/ghost_role_spawners.dm @@ -446,7 +446,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 @@ -488,7 +489,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" @@ -500,6 +500,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 @@ -724,7 +725,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 @@ -738,7 +739,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) @@ -759,7 +760,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 @@ -768,7 +769,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) . = ..() @@ -776,6 +777,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 @@ -786,7 +790,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" @@ -795,7 +798,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 8ffa1e7a9db..a8e2938a7f1 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -181,10 +181,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 @@ -373,20 +369,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 @@ -470,6 +455,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 @@ -478,6 +468,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 @@ -505,8 +498,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 @@ -516,8 +507,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 @@ -527,12 +516,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 @@ -546,12 +534,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 @@ -563,7 +550,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 a061caadb38..7555c737133 100644 --- a/code/modules/cargo/packs.dm +++ b/code/modules/cargo/packs.dm @@ -333,7 +333,6 @@ 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." @@ -1631,6 +1630,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 2452c0c0d1e..3d737fe339e 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 5cf4124e128..2015469bfd5 100644 --- a/code/modules/jobs/access.dm +++ b/code/modules/jobs/access.dm @@ -1,4 +1,3 @@ - //returns TRUE if this mob has sufficient access to use this object /obj/proc/allowed(mob/M) //check if it doesn't require any access at all @@ -102,305 +101,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, - //SKYRAT EDIT ADDITION - BLUESHIELD & GENPOP - ACCESS_BLUESHIELD, - ACCESS_ENTER_GENPOP, - ACCESS_LEAVE_GENPOP - //SKYRAT EDIT ADDITION END - ) - -/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, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP) // SKYRAT EDIT: Adds ACCESS_ENTER_GENPOP and ACCESS_LEAVE_GENPOP - 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" -// SKYRAT EDIT: Start - Modular Access - if(ACCESS_ENTER_GENPOP) - return "Prison Turnstile Entrance" - if(ACCESS_LEAVE_GENPOP) - return "Prison Turnstile Exit" -// SKYRAT EDIT: End - Modular Access - -/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" - -/*//SKYRAT EDIT - CARD - MOVED TO MODULAR - modules/card/access.dm -/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") -*///Skyrat Edit End - /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 8bbc3ef18ae..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,12 +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) - SKRYAT EDIT ORIGINAL - - minimal_access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_MAINT_TUNNELS, ACCESS_MECH_ENGINE, ACCESS_AUX_BASE, // Skyrat edit: additional engineering access - ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_MINERAL_STOREROOM) paycheck = PAYCHECK_MEDIUM paycheck_department = ACCOUNT_ENG @@ -28,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 @@ -43,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 f8abc409d49..cecf006b905 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") //ORIGINAL department_head = list("Quartermaster") //SKYRAT EDIT CHANGE @@ -11,8 +11,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 @@ -21,10 +19,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 fd56a479a27..22de3dcc970 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 ed272cf018a..7e337b3d712 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") @@ -18,38 +18,6 @@ outfit = /datum/outfit/job/hop departments = DEPARTMENT_COMMAND | DEPARTMENT_SERVICE - //SKYRAT EDIT CHANGE BEGIN - /* - 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) - */ - access = list(ACCESS_COURT, ACCESS_WEAPONS, - ACCESS_CHANGE_IDS, ACCESS_EVA, ACCESS_HEADS, - ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_MORGUE, - ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_HYDROPONICS, ACCESS_LAWYER, - ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_VAULT, - ACCESS_MECH_ENGINE, ACCESS_MECH_SCIENCE, ACCESS_MECH_SECURITY, ACCESS_MECH_MEDICAL, - ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, 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_HYDROPONICS, ACCESS_LAWYER, - ACCESS_MECH_ENGINE, ACCESS_MECH_SCIENCE, ACCESS_MECH_SECURITY, ACCESS_MECH_MEDICAL, - ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_VAULT, ACCESS_MINING_STATION, - ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM, ACCESS_AUX_BASE, ACCESS_TELEPORTER) - //SKYRAT EDIT CHANGE END paycheck = PAYCHECK_COMMAND paycheck_department = ACCOUNT_SRV bounty_types = CIV_JOB_RANDOM @@ -58,11 +26,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 @@ -73,8 +46,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 2e5c18e8fbb..ba71101fd23 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, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP) // SKYRAT EDIT: Adds ACCESS_ENTER_GENPOP and ACCESS_LEAVE_GENPOP - 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, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP) // SKYRAT EDIT: Adds ACCESS_ENTER_GENPOP and ACCESS_LEAVE_GENPOP 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/peacekeeper //SKYRAT EDIT CHANGE - SEC_HAUL - ORIGINAL: /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 5b8d73ee5f1..b44a91f6b4b 100644 --- a/code/modules/jobs/job_types/prisoner.dm +++ b/code/modules/jobs/job_types/prisoner.dm @@ -17,9 +17,12 @@ 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 + // SKYRAT EDIT: Start - Adds spawn text to prisoners. /datum/job/prisoner/after_spawn(mob/living/carbon/human/H, mob/M) . = ..() @@ -28,3 +31,4 @@ to_chat(M, "You are a prisoner being held in Space Station 13, awaiting transfer to a secure prison facility or to the courthouse to stand trial.") to_chat(M, "It's up to you to decide why you're in here. Chances are, the case against you might not be strong enough to convict you. Or is it?
") // SKYRAT EDIT : End - Adds spawn text to prisoners. + 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 aea4dd9efda..bec299433a0 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") //ORIGINAL department_head = list("Captain") //SKYRAT EDIT CHANGE @@ -12,16 +12,7 @@ outfit = /datum/outfit/job/quartermaster - //SKYRAT EDIT CHANGE BEGIN - /* - 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) - */ - 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, ACCESS_KEYCARD_AUTH, ACCESS_RC_ANNOUNCE, ACCESS_SEC_DOORS, ACCESS_HEADS) - 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, ACCESS_KEYCARD_AUTH, ACCESS_RC_ANNOUNCE, ACCESS_SEC_DOORS, ACCESS_HEADS) - //SKYRAT EDIT CHANGE END - //paycheck = PAYCHECK_MEDIUM //ORIGINAL - paycheck = PAYCHECK_COMMAND //SKYRAT EDIT CHANGE + paycheck = PAYCHECK_COMMAND //SKYRAT EDIT CHANGE - ORIGINAL: PAYCHECK_MEDIUM paycheck_department = ACCOUNT_CAR liver_traits = list(TRAIT_PRETENDER_ROYAL_METABOLISM) @@ -30,14 +21,19 @@ 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 //ORIGINAL ears = /obj/item/radio/headset/heads/qm //SKYRAT EDIT CHANGE - id = /obj/item/card/id/silver //SKYRAT EDIT ADDITION + id = /obj/item/card/id/advanced/silver //SKYRAT EDIT CHANGE uniform = /obj/item/clothing/under/rank/cargo/qm shoes = /obj/item/clothing/shoes/sneakers/brown glasses = /obj/item/clothing/glasses/sunglasses @@ -47,3 +43,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 54e9bfc6cda..fae4e6ce51c 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 8b553f9da2c..724d305f70e 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, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP) // SKYRAT EDIT: Adds ACCESS_ENTER_GENPOP and ACCESS_LEAVE_GENPOP - minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_MECH_SECURITY, ACCESS_MINERAL_STOREROOM, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP) // See /datum/job/officer/get_access() // SKYRAT EDIT: Adds ACCESS_ENTER_GENPOP and ACCESS_LEAVE_GENPOP 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 9bd9b8e6d24..71dae6cfebe 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 @@ -43,6 +41,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 6f5d32f8ae3..3db07a71e45 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 1db2fa0b862..6207066b991 100644 --- a/code/modules/mining/machine_vending.dm +++ b/code/modules/mining/machine_vending.dm @@ -216,7 +216,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), @@ -261,24 +261,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." @@ -292,7 +274,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 df3b0ae0d78..0ac83eb9f44 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -329,7 +329,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 e4069efa23c..224f88f3201 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -74,8 +74,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)) @@ -113,6 +111,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 39f932eede0..4e8ccaebfcc 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -615,10 +615,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 08864b8a9ab..48839a8429c 100644 --- a/code/modules/mob/living/carbon/human/human_update_icons.dm +++ b/code/modules/mob/living/carbon/human/human_update_icons.dm @@ -147,6 +147,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] @@ -161,13 +162,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 b93c0f31e99..38503aa48d8 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 d85b806fcee..24d387e0f4c 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1138,6 +1138,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 1ce9fd10ba0..31e156f4ca7 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 c87764de38d..c543b8bc323 100644 --- a/code/modules/ruins/spaceruin_code/hilbertshotel.dm +++ b/code/modules/ruins/spaceruin_code/hilbertshotel.dm @@ -497,15 +497,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 cd3867b42f0..a1e02a64ab1 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -1186,12 +1186,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 f12803c66b50fa66d950b0c3952c1ffd8921527c..4cdc732ac900075d0861aacff0fb716ae1c0b54c 100644 GIT binary patch literal 42964 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

T$x3@9xOe`I*XiE;#5z5|Bx1clka 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 literal 42862 zcmd422UJr**ESkN1w}*vEf5hE5D@7qO+k7I9jQS; zklqO$=_PbR3nb^B_`dJ|-MhZB*8TqfyX(4^%*mNEGyBY*{mh=dpA)L3p>*;5jq?x) zfp{myeeQC)DRJRd z&s5g++wjT|RCJH9}dL&+~+OoQcj6LLeKUm8` zIy}TSEzOVynikKMM{T{7m>`jy+={aXcfm_zXF-wv*y9dp!K2n0C+`85!ZT9!qsF)U z#V0Pp6T=D`!-X)YR|Abyi~CDzXEj_#og6QJ!(X^yMWh;PE;d`^SL=gW1P=3lpr{@> z4=?*{JG0%cXi}5ST*(|+DN7f}w(JM>T~~4@E=Pp2rY+xFqpnbwe(gB-!EC&ib3}a0 zL*Fm$z_5X?347|pllp5fn|I4!Il3!)8W={gEb3VXt22z6KT+JDpAVP1)jCwDFx^Jy z!$Hpz&^+?1(9zMBLGqh;jiL6$Pth?|-=A$X+(L)cE(zu-EQ*uxZx-*WqF*-jt9w(i zTvt>31`WKjHNyY8V5L7tt5WBC+fzfv#|b5o4fCgL)UJLTxyi8l^^&Zx;Hoe$gokgQ z+B&|!I>=st=Y6jWUd18Mr{JnlKhD$auuNZN2W@eRgTg zW_4-p9-kB;l`|}~$7Sh##&6Pvz#p#*G#c-|<#l`OJ-b{xZG8Jq(Iu~@y@g5)&_VU! zR)GIy;N{T_^fmI~a+Bu1%v9VP&KK-0UWwNL`m?Dxant)gH$pS;TI`ZzS>e5*n( z(|b+@PgAPVcq&miCwsdZUnqxo!M}_J|CY{Cit~S2raS!r8&?CLf6;0Y$>nqZYKc+K zHHHJ_m9}EDV6EPLm#6&uEiNmQqUU8A>)R@Nhcy`QDCe}w5K5rb>GW?GO>`56lb0*L z`i-8IwRZOwo4eT{_gaXjh2rcLM(&c`m)bu(OrG=1^krnf2-bQT&a~u`X`_@9K-}4$ zn(2F8&@`VTMCX2mXS_s4^3>kzh>JtTmL68Dx34JmD|EcgbV!|hsIyTJV^hNW=f_69 z7;8n%`XX-hvGw!wB2=w+)x@Z?O;Rx=+od6_kho~j9DjlQ8WS^(`e>70b_za%|HCQ^ zY%M?g%#RwOL4AhdDpGwmvU40@0B_|AL19Z^g^Wum{7ZhMDXvw2{oAw}E% zne}wY4@Pw!qwVyp(7XF15AeEb#t z();&k-mfufyuSCL4f`}F_MuEf+|6^Df4tW-{`|N)W_mw4pj~F&tAIm>RCi*R?JJEu zK|&``z9T0zg#y$N3fjvcKCwfTVG6q5No$jSNZp|-VB6;4)z@YS9i75?hS>Np!~Dv* zD2K@MH2sSS`Ujyd2l$_#(YhZI-Cg{j~=vOXollAPu$7Z)C|A zkl)u%-y@erZy;pwgv4KNAQuf+PF*LL1`i>Wg{@aaT zg_m!M!{5rjb7(|TuGrtF==3mIsIKK5X+(1J@9gdt__K`Vzpb9}poWy_aI=M3BOx90 zZO#|cL=Gxi=;D@`xy-wj0|M$@n>Fs0JfrcLt1i5faaiB^l*ZYOAy3#-(Xsw$sz)PY zW(1?`Pudm=*rxH=Y2)co=1IQoI9|bk_4u_bGg{%8=rCEWC+<9T$7|ZZ(!#E&%{yf& zWJ~?}HCe#i)dW6zBY4T@Lzk~M%t{l9&M2d(eREToc5&c}4bf~?h7`3>j)XSM#nwPgOQ z#s)Wxv3iBL+@e;Bnq}!%yytfJc*Dm=_mSyZHQYx>N8cg@{`FIsd$CcJ?Cc! zS>ANdL$GO@uD8N(*L4TKdhKkJn3EIa!V~a@=Ra>Gsu7>bL+l|vyEl7wlfW^Km!+>% z73MV(s;NsWo-7Zj_3;y2uJCnWUtZ`T97Lm8IR@MXLN6g(^Mt zRVlrH@RK10Z-dQqq3!1lDr5oPPFaZj?fk7cCrAAT2>Zp+`tvabJ<#)Rw2QO&zA15AadsEXONb{F_b-v(?xS$p zl}2g=k4(ycbL1yHj?dhd~|v~0k3b6{Ps+3BLkv-5%`DPy%xlaWoVRE-J%#mH<` z;g@Ss0}u*!da-kUkn9?uZr78G<7vHpR++<)Aq*Dav+2GBHY?d*^$K;{>F~>9CPu6Q zQ$95!Ch3`D9QbLWL;@`=5GZ@(FU28X4$*?Ew1z5gz|;GQ(C7@C`F8e;0+V)(Aw+mVVV`g zDPl8>P`lCG^4Lwu+m4zw1|@r6$DMzv3cW{ZsFiASGN6E-wW&Cg>K-X|v@UPkSKMx} zrK<-%Rbr))+dBGt@#Y(`X%Y?MJE68s{tr5F?J1sPpE4A6v%)S|TQrK4XrEV+kYmcT zXtj3`Af66%n3f0|)9{!}Y z9Lm+>;CNmg5Mhz~g+6^xwnwL8QCfRMK+YVi$sI1NXvKPMYBBdGX8R^H;vj@|5&&(s z{7wgoA|a<=>WpwcB(|&E4Q+CVn05DJ9`d%;K+?Qvjlp|U-v?{vFqMi<hi4xB738ucvFqo;Hd=JM0%hz*n1) zX#6Xme9+}fYaxv?3FsfnU92ZTy{&!M|N1ho?t@$UrztOkTMlhM?5RyCILW@t*Pm%c zQUfDdazf0mq0AvSf z5n7nqtsZKKciZbrOU+{_J5nZg3jzPp@5&>fV`W(%CjfsZ`zHUnU%f}~4VQz068miyj{b*V z+^2atI8eyi9k=PKeQe96PC4(aomZtRSw;&TUeGIaQS=&49Ma+_uFZ8jE)I@4wj>*3 zgI15KLr0ct_orWvap+HCS_@^;>afmkk4J0csH8g={4PH;YmepofU92poW`-jlh*Qq z#U77qzz~M&=Zc?}m?MyhZPhE+@2x-MS+%%&&g_iC(7s5(T-2{+)c%e!e8p(QLgBDm z?s%;z_@iFqr%l{DhgftwB34se_epm~!tXDb^_WE#!MMOW)!A>qV=Vhg{=wvl~WMW}8=-0u*VK_nlvtJRg=9J9&Riq{u5Y z{Kv7ra>Y(IoJ&38R@@~PsRtio+y3Ba922@#RA4vhgjwog{7vDcAC^t^hpX^G&0QK! z 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%0AM&7`@V?bi(KF3D%5<|vQ}{xFjT%zUqqOS;oN+k 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 M>FVdQ&MBb@0P!D+p#T5? literal 0 HcmV?d00001 diff --git a/icons/mob/clothing/id_card.dmi b/icons/mob/clothing/id_card.dmi new file mode 100644 index 0000000000000000000000000000000000000000..caced4584a96242916dfdc512a956ec40c730f28 GIT binary patch literal 551 zcmeAS@N?(olHy`uVBq!ia0vp^2Y`412Q!e&{WdutNF@aLgt#6!a6mSDHLroMhK7cN zgM+YVY2^HyzYj7eB_+wq$}U{E(An9!wzl^0;lqCq1oZ?e{(sS9Rb&VRhfDR|ym_;0 z*Dh&k=@;#K<)#cvJsEzlhyfaS{@l6t&YtDVm;XL%*2$Nx3ABr`B*-tA!Qt7BG$3bo zRY*jMOKNd)QD#9&W_}(6L&col3un0w83?dEDE=g}aBp{gxc((2+Z*8%W~@1zH0wm# z${*jhRSCE3J$t}>`pUB#ZAUzf)P8__SljW1wSSs9+?f4$h9Hh5FT2$qtP_*@|;{cQR3%vo7{7W<#r++TH8bl>a! zVX+BkEVsuzcyIXk44~`Pwd*-k*!7 moWG`;m9un8tUgfY&nfYRrmX+AT+a6bsq=L8b6Mw<&;$UXHuI|h literal 0 HcmV?d00001 diff --git a/icons/mob/hud.dmi b/icons/mob/hud.dmi index a3e57b06cf3517fd355a8808c91f19b16fdc8782..54bfaa9d48f29815ee50f95560707dc09e720adb 100644 GIT binary patch literal 18365 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 asu{(*CN(cTKS4_Zz#nMe&%gWZ-TweMhse7C literal 18791 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 R2VdO}JW|$Ds#LHD{a-1TTc`j4 literal 13462 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 diff --git a/icons/obj/card_old.dmi b/icons/obj/card_old.dmi new file mode 100644 index 0000000000000000000000000000000000000000..56c647aa63e26fd73514a09c06f39b66e7be7f57 GIT binary patch literal 13462 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 literal 0 HcmV?d00001 diff --git a/modular_skyrat/master_files/code/datums/id_trim/jobs.dm b/modular_skyrat/master_files/code/datums/id_trim/jobs.dm new file mode 100644 index 00000000000..04af1344325 --- /dev/null +++ b/modular_skyrat/master_files/code/datums/id_trim/jobs.dm @@ -0,0 +1,101 @@ +//MODULAR ID TRIM ACCESS OVERRIDES GO HERE!! + +/datum/id_trim/job/head_of_security/New() + . = ..() + + access |= list(ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP) + +/datum/id_trim/job/warden/New() + . = ..() + + access |= list(ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP) + +/datum/id_trim/job/security_officer/New() + . = ..() + + access |= list(ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP) + + +/datum/id_trim/job/atmospheric_technician + 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_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_MAINT_TUNNELS, ACCESS_MECH_ENGINE, ACCESS_AUX_BASE, + ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_MINERAL_STOREROOM) + +/datum/id_trim/job/head_of_personnel + access = list(ACCESS_COURT, ACCESS_WEAPONS, + ACCESS_CHANGE_IDS, ACCESS_EVA, ACCESS_HEADS, + ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_MORGUE, + ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_HYDROPONICS, ACCESS_LAWYER, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_VAULT, + ACCESS_MECH_ENGINE, ACCESS_MECH_SCIENCE, ACCESS_MECH_SECURITY, ACCESS_MECH_MEDICAL, + ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, 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_HYDROPONICS, ACCESS_LAWYER, + ACCESS_MECH_ENGINE, ACCESS_MECH_SCIENCE, ACCESS_MECH_SECURITY, ACCESS_MECH_MEDICAL, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_VAULT, ACCESS_MINING_STATION, + ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM, ACCESS_AUX_BASE, ACCESS_TELEPORTER) + +/datum/id_trim/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, ACCESS_KEYCARD_AUTH, ACCESS_RC_ANNOUNCE, ACCESS_SEC_DOORS, ACCESS_HEADS) + 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, ACCESS_KEYCARD_AUTH, ACCESS_RC_ANNOUNCE, ACCESS_SEC_DOORS, ACCESS_HEADS) + +/datum/id_trim/job/blueshield + assignment = "Blueshield" + trim_state = "trim_blueshield" + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, \ + ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_HEADS, \ + ACCESS_MAINT_TUNNELS, ACCESS_CONSTRUCTION, ACCESS_MORGUE, \ + ACCESS_RESEARCH, ACCESS_CARGO, \ + ACCESS_RC_ANNOUNCE, ACCESS_GATEWAY, ACCESS_WEAPONS, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP) + wildcard_access = list(ACCESS_BLUESHIELD) + minimal_access = list(ACCESS_FORENSICS_LOCKERS, ACCESS_SEC_DOORS, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_ENGINE, ACCESS_MAINT_TUNNELS, ACCESS_RESEARCH, + ACCESS_RC_ANNOUNCE, ACCESS_HEADS, ACCESS_WEAPONS, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP) + minimal_wildcard_access = list(ACCESS_BLUESHIELD) + config_job = "blueshield" + template_access = list(ACCESS_CAPTAIN, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/captain/shuttle_pilot + assignment = "Shuttle Pilot" + trim_state = "trim_shuttle_pilot" + config_job = "shuttle pilot" + +/datum/id_trim/job/security_medic + assignment = "Security Medic" + trim_state = "trim_security_medic" + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MECH_SECURITY, ACCESS_MORGUE, ACCESS_WEAPONS, + ACCESS_FORENSICS_LOCKERS, ACCESS_MINERAL_STOREROOM, ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_GENETICS, + ACCESS_VIROLOGY, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_MECH_SECURITY, ACCESS_MINERAL_STOREROOM, + ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP) + config_job = "security_medic" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOS, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/security_medic/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/security_sergeant + assignment = "Security Sergeant" + trim_state = "trim_security_sergeant" + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MECH_SECURITY, ACCESS_MAINT_TUNNELS, ACCESS_MORGUE, + ACCESS_WEAPONS, ACCESS_FORENSICS_LOCKERS, ACCESS_MINERAL_STOREROOM, ACCESS_ENTER_GENPOP) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_MECH_SECURITY, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM, + ACCESS_MAINT_TUNNELS, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP) + + config_job = "security_sergeant" + template_access = list(ACCESS_CAPTAIN, ACCESS_HOS, ACCESS_CHANGE_IDS) + +/datum/id_trim/job/security_sergeant/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/modular_skyrat/master_files/code/modules/clothing/outfits/ert.dm b/modular_skyrat/master_files/code/modules/clothing/outfits/ert.dm index d3fe2687555..e5c990b6fcd 100644 --- a/modular_skyrat/master_files/code/modules/clothing/outfits/ert.dm +++ b/modular_skyrat/master_files/code/modules/clothing/outfits/ert.dm @@ -17,7 +17,7 @@ suit_store = /obj/item/tank/internals/emergency_oxygen/double belt = /obj/item/storage/belt/security/full l_hand = /obj/item/gun/energy/pulse/carbine/loyalpin //if this is still bulky make it not bulky and storable on belt/back/bag/exosuit - id = /obj/item/card/id/ert + id = /obj/item/card/id/advanced/centcom/ert ears = /obj/item/radio/headset/headset_cent/alt skillchips = list(/obj/item/skillchip/disk_verifier) @@ -35,8 +35,6 @@ Radio.set_frequency(FREQ_CENTCOM) Radio.freqlock = TRUE var/obj/item/card/id/ID = person.wear_id - ID.access = get_all_accesses()//They get full station access. - ID.access += get_centcom_access("Death Commando")//Let's add their alloted CentCom access. ID.assignment = "Asset Protection" ID.registered_name = person.real_name ID.update_label() diff --git a/modular_skyrat/modules/assaultops/code/assaultops_outfits.dm b/modular_skyrat/modules/assaultops/code/assaultops_outfits.dm index 6062202fa6e..3a85e55cab9 100644 --- a/modular_skyrat/modules/assaultops/code/assaultops_outfits.dm +++ b/modular_skyrat/modules/assaultops/code/assaultops_outfits.dm @@ -11,17 +11,17 @@ back = /obj/item/storage/backpack/fireproof ears = /obj/item/radio/headset/syndicate/alt/assault l_pocket = /obj/item/modular_computer/tablet/nukeops - id = /obj/item/card/id/syndicate_command/assault_operative + id = /obj/item/card/id/advanced/chameleon suit = /obj/item/clothing/suit/space/hardsuit/syndi suit_store = /obj/item/gun/ballistic/automatic/pistol/aps r_pocket = /obj/item/ammo_box/magazine/m9mm_aps belt = /obj/item/storage/belt/utility/syndicate + id_trim = /datum/id_trim/chameleon/operative + var/command_radio = FALSE var/cqc = FALSE -/obj/item/card/id/syndicate_command/assault_operative - assignment = "Assault Operative" /datum/outfit/assaultops/cqb name = "Assault Operative - CQB" diff --git a/modular_skyrat/modules/blueshield/code/modules/jobs/job_types/blueshield.dm b/modular_skyrat/modules/blueshield/code/modules/jobs/job_types/blueshield.dm index 10b77bd9bfb..526c0ad80c0 100644 --- a/modular_skyrat/modules/blueshield/code/modules/jobs/job_types/blueshield.dm +++ b/modular_skyrat/modules/blueshield/code/modules/jobs/job_types/blueshield.dm @@ -13,13 +13,6 @@ outfit = /datum/outfit/job/blueshield - access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, \ - ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_HEADS, \ - ACCESS_MAINT_TUNNELS, ACCESS_CONSTRUCTION, ACCESS_MORGUE, \ - ACCESS_RESEARCH, ACCESS_CARGO, \ - ACCESS_RC_ANNOUNCE, ACCESS_GATEWAY, ACCESS_WEAPONS, ACCESS_BLUESHIELD, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP) - minimal_access = list(ACCESS_FORENSICS_LOCKERS, ACCESS_SEC_DOORS, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_ENGINE, ACCESS_MAINT_TUNNELS, ACCESS_RESEARCH, - ACCESS_RC_ANNOUNCE, ACCESS_HEADS, ACCESS_BLUESHIELD, ACCESS_WEAPONS, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP) display_order = JOB_DISPLAY_ORDER_BLUESHIELD outfit = /datum/outfit/job/blueshield @@ -27,9 +20,9 @@ name = "Blueshield" jobtype = /datum/job/blueshield uniform = /obj/item/clothing/under/rank/security/blueshield - id = /obj/item/card/id/silver suit = /obj/item/clothing/suit/armor/vest/blueshield gloves = /obj/item/clothing/gloves/tackler/combat/insulated + id = /obj/item/card/id/advanced/silver shoes = /obj/item/clothing/shoes/jackboots ears = /obj/item/radio/headset/heads/blueshield/alt glasses = /obj/item/clothing/glasses/hud/security/sunglasses @@ -42,8 +35,12 @@ box = /obj/item/storage/box/survival/security belt = /obj/item/pda/security + id_trim = /datum/id_trim/job/blueshield + /datum/outfit/plasmaman/blueshield name = "Blueshield Plasmaman" head = /obj/item/clothing/head/helmet/space/plasmaman/blueshield uniform = /obj/item/clothing/under/plasmaman/blueshield + + diff --git a/modular_skyrat/modules/card/access.dm b/modular_skyrat/modules/card/access.dm deleted file mode 100644 index ec0e44e2ae9..00000000000 --- a/modular_skyrat/modules/card/access.dm +++ /dev/null @@ -1,24 +0,0 @@ -//moved from code/modules/jobs/access.dm -/proc/get_all_jobs() //All on-station jobs go here. - 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", "Security Medic", "Security Sergeant", - "Prisoner", "Blueshield", "Shuttle Pilot") - -/proc/get_all_job_icons() //For all existing HUD icons, Things a HoP or someone with an agent card might realistically apply, but not a real job. Also ghost role / event stuffs. - 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", - "Operative", "Tradepost Officer", "CentCom Psychologist", "Bluespace Tech", "Syndicate Overlord", "Syndicate Operative", "Syndicate Ship Captain", - "Charlie Station Security Officer", "Charlie Station Scientist", "Charlie Station Engineer", "Charlie Station Assistant", "Charlie Station Botanist", "Charlie Station Curator", "Charlie Station Lawyer", "Charlie Station Chaplain", "Charlie Station Bartender", "Charlie Station Cook", - "Charlie Station Janitor", "Charlie Station Clown", "Charlie Station Mime", "Charlie Station Quartermaster", "Charlie Station Cargo Technician", "Charlie Station Shaft Miner", "Charlie Station Atmospheric Technician", "Charlie Station Virologist", "Charlie Station Chemist", - "Charlie Station Paramedic", "Charlie Station Psychologist", "Charlie Station Medical Doctor", "Charlie Station Geneticist", "Charlie Station Roboticist", "Charlie Station Warden", "Charlie Station Detective", "Charlie Station Prisoner", "Charlie Station Head Of Security", - "Charlie Station Head Of Personnel", "Charlie Station Chief Engineer", "Charlie Station Research Director", "Charlie Station Chief Medical Officer", "Charlie Station Captain","CentCom Assistant","CentCom Botanist","CentCom Curator","CentCom Lawyer","CentCom Chaplain", - "CentCom Bartender","CentCom Cook","CentCom Janitor","CentCom Clown","CentCom Mime","CentCom Quartermaster","CentCom Cargo Technician","CentCom Shaft Miner","CentCom Atmospheric Technician","CentCom Station Engineer","CentCom Virologist","CentCom Chemist","CentCom Paramedic", - "CentCom Medical Doctor","CentCom Medical Doctor","CentCom Geneticist","CentCom Scientist","CentCom Roboticist","CentCom Warden","CentCom Detective","CentCom Security Officer","CentCom Prisoner","Admiral", "Protective Custody", "VIP Guest", "Custodian","Medical Officer", - "Research Officer","Special Ops Officer","Private Security Force","CentCom Commander","Service Guest","Guest Comedian","Private Trader","Independent Technician","Long Term Patient","Research Subject","Independent Researcher","Security Contractor", "DonkCo Diplomat", - "Cybersun Diplomat","Interdyne Chemist","Syndicate","Syndicate Prisoner","Nuclear Operative","Assault Operative","Lizard","Moth","Kobold","Cutie","Station Admiral","Corporate Liaison","Chief Engineering Officer","Chief Research Officer","Chief Master At Arms", - "Investigative Officer","Brig Officer","Master At Arms","Corporate Lawyer","Station Medical Officer","Bioweapons Technician","Engine Technician","Genetics Researcher","Researcher","Robotics Technician","Chef","Counsel","Sanitation Technician","Deck Officer", - "Deck Crewman","Syndicate Staff","Comms Officer") - -/proc/get_all_centcom_jobs() //Admin-only jobs or jobs that nobody would care if it was a generic nanotrasen logo. - return list("Central Command","Thunderdome Overseer","CentCom Official") diff --git a/modular_skyrat/modules/card/card.dm b/modular_skyrat/modules/card/card.dm deleted file mode 100644 index c61c79615e8..00000000000 --- a/modular_skyrat/modules/card/card.dm +++ /dev/null @@ -1,82 +0,0 @@ -/obj/item/card - icon = 'modular_skyrat/modules/card/card.dmi' - -/obj/item/card/id/syndicate_command - uses_overlays = TRUE - -/obj/item/card/id/syndicate_command/crew_id - uses_overlays = TRUE - -/obj/item/card/id/syndicate_command/captain_id - uses_overlays = TRUE - -/obj/item/card/id/centcom - uses_overlays = TRUE - -/obj/item/card/id/ert - icon_state = "gold" - uses_overlays = TRUE - -/obj/item/card/id/ert/security - icon_state = "centcom" - uses_overlays = TRUE - -/obj/item/card/id/ert/engineer - icon_state = "centcom" - uses_overlays = TRUE - -/obj/item/card/id/ert/medical - icon_state = "centcom" - uses_overlays = TRUE - -/obj/item/card/id/ert/chaplain - icon_state = "centcom" - uses_overlays = TRUE - -/obj/item/card/id/ert/janitor - icon_state = "centcom" - uses_overlays = TRUE - -/obj/item/card/id/ert/clown - icon_state = "centcom" - uses_overlays = TRUE - -/obj/item/card/id/ert/deathsquad - icon_state = "syndie" - uses_overlays = TRUE - -/obj/item/card/id/debug - icon_state = "centcom" - uses_overlays = TRUE - -/obj/item/card/id/prisoner - icon_state = "orange" - uses_overlays = TRUE - -/obj/item/card/id/prisoner/one - uses_overlays = FALSE - -/obj/item/card/id/prisoner/two - uses_overlays = FALSE - -/obj/item/card/id/prisoner/three - uses_overlays = FALSE - -/obj/item/card/id/prisoner/four - uses_overlays = FALSE - -/obj/item/card/id/prisoner/five - uses_overlays = FALSE - -/obj/item/card/id/prisoner/six - uses_overlays = FALSE - -/obj/item/card/id/prisoner/seven - uses_overlays = FALSE - -/obj/item/card/id/away - uses_overlays = TRUE - -/obj/item/card/id/debug/bst - icon_state = "bluespacetech" - uses_overlays = FALSE diff --git a/modular_skyrat/modules/card/card.dmi b/modular_skyrat/modules/card/card.dmi deleted file mode 100644 index dee319598bd988f8354776014cf5653a24b1b10d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23918 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 diff --git a/modular_skyrat/modules/cloning/code/modules/jobs/access.dm b/modular_skyrat/modules/cloning/code/modules/jobs/access.dm deleted file mode 100644 index 3b8c038d462..00000000000 --- a/modular_skyrat/modules/cloning/code/modules/jobs/access.dm +++ /dev/null @@ -1,19 +0,0 @@ -/get_all_accesses() - . = ..() - . += ACCESS_CLONING - -/get_region_accesses(code) - . = ..() - if(code == 3) - . += ACCESS_CLONING - if(.) - return . - -/get_access_desc(A) - . = ..() - if(.) - return . - switch(A) - if(ACCESS_CLONING) - return "Cloning Room" - diff --git a/modular_skyrat/modules/cloning/code/modules/jobs/job_types/chemist.dm b/modular_skyrat/modules/cloning/code/modules/jobs/job_types/chemist.dm deleted file mode 100644 index 0b75a2ddfb3..00000000000 --- a/modular_skyrat/modules/cloning/code/modules/jobs/job_types/chemist.dm +++ /dev/null @@ -1,3 +0,0 @@ -/datum/job/chemist/New() - access += ACCESS_CLONING - . = ..() diff --git a/modular_skyrat/modules/cloning/code/modules/jobs/job_types/chief_medical_officer.dm b/modular_skyrat/modules/cloning/code/modules/jobs/job_types/chief_medical_officer.dm deleted file mode 100644 index 4179c93c4da..00000000000 --- a/modular_skyrat/modules/cloning/code/modules/jobs/job_types/chief_medical_officer.dm +++ /dev/null @@ -1,4 +0,0 @@ -/datum/job/cmo/New() - access += ACCESS_CLONING - minimal_access += ACCESS_CLONING - . = ..() diff --git a/modular_skyrat/modules/cloning/code/modules/jobs/job_types/geneticist.dm b/modular_skyrat/modules/cloning/code/modules/jobs/job_types/geneticist.dm deleted file mode 100644 index 29a5ed912cf..00000000000 --- a/modular_skyrat/modules/cloning/code/modules/jobs/job_types/geneticist.dm +++ /dev/null @@ -1,4 +0,0 @@ -/datum/job/geneticist/New() - access += ACCESS_CLONING - minimal_access += ACCESS_CLONING - . = ..() diff --git a/modular_skyrat/modules/cloning/code/modules/jobs/job_types/medical_doctor.dm b/modular_skyrat/modules/cloning/code/modules/jobs/job_types/medical_doctor.dm deleted file mode 100644 index a135766f864..00000000000 --- a/modular_skyrat/modules/cloning/code/modules/jobs/job_types/medical_doctor.dm +++ /dev/null @@ -1,4 +0,0 @@ -/datum/job/doctor/New() - access += ACCESS_CLONING - minimal_access += ACCESS_CLONING - . = ..() diff --git a/modular_skyrat/modules/cloning/code/modules/jobs/job_types/paramedic.dm b/modular_skyrat/modules/cloning/code/modules/jobs/job_types/paramedic.dm deleted file mode 100644 index 39d62047d94..00000000000 --- a/modular_skyrat/modules/cloning/code/modules/jobs/job_types/paramedic.dm +++ /dev/null @@ -1,4 +0,0 @@ -/datum/job/paramedic/New() - access += ACCESS_CLONING - minimal_access += ACCESS_CLONING - . = ..() diff --git a/modular_skyrat/modules/cloning/code/modules/jobs/job_types/virologist.dm b/modular_skyrat/modules/cloning/code/modules/jobs/job_types/virologist.dm deleted file mode 100644 index 114c14cc78d..00000000000 --- a/modular_skyrat/modules/cloning/code/modules/jobs/job_types/virologist.dm +++ /dev/null @@ -1,3 +0,0 @@ -/datum/job/virologist/New() - access += ACCESS_CLONING - . = ..() diff --git a/modular_skyrat/modules/cryosleep/code/cryopod.dm b/modular_skyrat/modules/cryosleep/code/cryopod.dm index 334b6db2241..0aab8fb8424 100644 --- a/modular_skyrat/modules/cryosleep/code/cryopod.dm +++ b/modular_skyrat/modules/cryosleep/code/cryopod.dm @@ -183,7 +183,7 @@ // These items are preserved when the process() despawn proc occurs. var/static/list/preserve_items = typecacheof(list( /obj/item/hand_tele, - /obj/item/card/id/captains_spare, + /obj/item/card/id/advanced/gold/captains_spare, /obj/item/aicard, /obj/item/mmi, /obj/item/paicard, diff --git a/modular_skyrat/modules/ghostcafe/code/game/objects/structures/ghost_role_spawners.dm b/modular_skyrat/modules/ghostcafe/code/game/objects/structures/ghost_role_spawners.dm index 45dda0896c1..9a8700349cd 100644 --- a/modular_skyrat/modules/ghostcafe/code/game/objects/structures/ghost_role_spawners.dm +++ b/modular_skyrat/modules/ghostcafe/code/game/objects/structures/ghost_role_spawners.dm @@ -75,7 +75,7 @@ uniform = /obj/item/clothing/under/color/random shoes = /obj/item/clothing/shoes/sneakers/black r_hand = /obj/item/storage/box/syndie_kit/chameleon/ghostcafe - id = /obj/item/card/id/ghost_cafe + id = /obj/item/card/id/advanced/ghost_cafe /datum/action/toggle_dead_chat_mob icon_icon = 'icons/mob/mob.dmi' @@ -108,16 +108,13 @@ new /obj/item/clothing/mask/chameleon(src) new /obj/item/storage/backpack/chameleon(src) -/obj/item/card/id/ghost_cafe +/obj/item/card/id/advanced/ghost_cafe name = "\improper Cafe ID" - id_type_name = "\improper Cafe ID" - desc = "An Cafe Visitor card." - icon_state = "silver_id" - registered_name = "Cafe Visitor" - assignment = "Cafe Visitor" - uses_overlays = FALSE + desc = "An ID straight from God." + icon_state = "card_centcom" + worn_icon_state = "card_centcom" + assigned_icon_state = "assigned_centcom" registered_age = null + trim = /datum/id_trim/admin + wildcard_slots = WILDCARD_LIMIT_ADMIN -/obj/item/card/id/ghost_cafe/Initialize() - access = get_all_accesses() - . = ..() diff --git a/modular_skyrat/modules/mapping/code/genpop.dm b/modular_skyrat/modules/mapping/code/genpop.dm index 70f9ccb4e06..e825c3c7968 100644 --- a/modular_skyrat/modules/mapping/code/genpop.dm +++ b/modular_skyrat/modules/mapping/code/genpop.dm @@ -4,7 +4,7 @@ name = "prisoner closet" var/default_name = "prisoner closet" req_access = list(ACCESS_BRIG) - var/obj/item/card/id/prisoner/registered_id = null + var/obj/item/card/id/advanced/prisoner/registered_id = null icon = 'modular_skyrat/modules/mapping/icons/turnstile.dmi' icon_state = "prisoner" locked = FALSE diff --git a/modular_skyrat/modules/mapping/code/modules/jobs/job_types/shuttle_pilot.dm b/modular_skyrat/modules/mapping/code/modules/jobs/job_types/shuttle_pilot.dm index 3d706ae29ef..d557b62ce83 100644 --- a/modular_skyrat/modules/mapping/code/modules/jobs/job_types/shuttle_pilot.dm +++ b/modular_skyrat/modules/mapping/code/modules/jobs/job_types/shuttle_pilot.dm @@ -15,16 +15,11 @@ outfit = /datum/outfit/job/shuttlepilot - access = list() //See get_access() - minimal_access = list() //See get_access() paycheck = PAYCHECK_COMMAND paycheck_department = ACCOUNT_SEC display_order = JOB_DISPLAY_ORDER_CAPTAIN -/datum/job/shuttlepilot/get_access() - return get_all_accesses() - /datum/job/shuttlepilot/announce(mob/living/carbon/human/H) ..() SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "Shuttle Pilot [H.real_name] on deck!")) @@ -33,7 +28,7 @@ name = "Shuttle Pilot" jobtype = /datum/job/shuttlepilot - id = /obj/item/card/id/silver + id = /obj/item/card/id/advanced/silver belt = /obj/item/pda/captain glasses = /obj/item/clothing/glasses/sunglasses ears = /obj/item/radio/headset/heads/captain/alt @@ -49,9 +44,11 @@ implants = list(/obj/item/implant/mindshield) + id_trim = /datum/id_trim/job/captain/shuttle_pilot /datum/outfit/plasmaman/shuttlepilot name = "Shuttle Pilot Plasmaman" head = /obj/item/clothing/head/helmet/space/plasmaman/captain uniform = /obj/item/clothing/under/plasmaman/captain gloves = /obj/item/clothing/gloves/color/captain/pilot + diff --git a/modular_skyrat/modules/sec_haul/code/misc/bulletbot.dm b/modular_skyrat/modules/sec_haul/code/misc/bulletbot.dm index f07a3ef88f2..851e188eabf 100644 --- a/modular_skyrat/modules/sec_haul/code/misc/bulletbot.dm +++ b/modular_skyrat/modules/sec_haul/code/misc/bulletbot.dm @@ -103,9 +103,9 @@ get_targets() icon_state = "bulletbot[on]" - var/datum/job/officer/O = new/datum/job/officer - access_card.access += O.get_access() - prev_access = access_card.access + var/datum/id_trim/job/sec_trim = SSid_access.trim_singletons_by_path[/datum/id_trim/job/security_officer] + access_card.add_access(sec_trim.access + sec_trim.wildcard_access) + prev_access = access_card.access.Copy() stolen_valor = list() prefixes = list(command, security, engineering) diff --git a/modular_skyrat/modules/sec_haul/code/peacekeeper/armadyne_clothing.dm b/modular_skyrat/modules/sec_haul/code/peacekeeper/armadyne_clothing.dm index dce43404248..94607dd8fbf 100644 --- a/modular_skyrat/modules/sec_haul/code/peacekeeper/armadyne_clothing.dm +++ b/modular_skyrat/modules/sec_haul/code/peacekeeper/armadyne_clothing.dm @@ -110,7 +110,7 @@ box = /obj/item/storage/box/survival/security l_pocket = /obj/item/megaphone/command implants = list(/obj/item/implant/mindshield) - id = /obj/item/card/id/armadyne/corpo + id = /obj/item/card/id/advanced/armadyne/agent /datum/outfit/armadyne_security @@ -132,21 +132,40 @@ box = /obj/item/storage/box/survival/security l_pocket = /obj/item/megaphone/command implants = list(/obj/item/implant/mindshield) - id = /obj/item/card/id/armadyne/corpo/security + id = /obj/item/card/id/advanced/armadyne/security -/obj/item/card/id/armadyne/corpo +/obj/item/card/id/advanced/armadyne name = "\improper Armadyne ID" - id_type_name = "\improper Armadyne ID" desc = "An Armadyne ID card." - icon_state = "centcom" - registered_name = "Armadyne Corpo" - assignment = "Armadyne Corporate Agent" - uses_overlays = FALSE + icon_state = "card_centcom" + worn_icon_state = "card_centcom" + assigned_icon_state = "assigned_centcom" registered_age = null + trim = /datum/id_trim/centcom/armadyne + wildcard_slots = WILDCARD_LIMIT_CENTCOM -/obj/item/card/id/armadyne/Initialize(mapload) +/datum/id_trim/centcom/armadyne + assignment = "Armadyne Corporate" + trim_state = "trim_ert_commander" + +/datum/id_trim/centcom/armadyne/New() . = ..() - access = get_all_accesses()+get_ert_access("commander")-ACCESS_CHANGE_IDS + access = SSid_access.get_region_access_list(list(REGION_CENTCOM, REGION_ALL_STATION)) + +/obj/item/card/id/advanced/armadyne/security + registered_name = "Armadyne Corpo" + trim = /datum/id_trim/centcom/armadyne/security + +/datum/id_trim/centcom/armadyne/security + assignment = "Armadyne Corporate Security Detail" + trim_state = "trim_ert_commander" + +/obj/item/card/id/advanced/armadyne/agent + trim = /datum/id_trim/centcom/armadyne/agent + +/datum/id_trim/centcom/armadyne/agent + assignment = "Armadyne Corporate Directorate" + trim_state = "trim_ert_commander" /datum/outfit/armadyne_rep/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) if(visualsOnly) diff --git a/modular_skyrat/modules/sec_haul/code/security_medic/security_medic.dm b/modular_skyrat/modules/sec_haul/code/security_medic/security_medic.dm index 55d842caeff..018f89c02e7 100644 --- a/modular_skyrat/modules/sec_haul/code/security_medic/security_medic.dm +++ b/modular_skyrat/modules/sec_haul/code/security_medic/security_medic.dm @@ -14,8 +14,6 @@ outfit = /datum/outfit/job/security_medic - 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, ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_VIROLOGY, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP) // SKYRAT EDIT: Adds ACCESS_ENTER_GENPOP and ACCESS_LEAVE_GENPOP - minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_MECH_SECURITY, ACCESS_MINERAL_STOREROOM, ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CLONING, ACCESS_MAINT_TUNNELS, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP) // SKYRAT EDIT: Adds ACCESS_ENTER_GENPOP and ACCESS_LEAVE_GENPOP paycheck = PAYCHECK_MEDIUM paycheck_department = ACCOUNT_SEC @@ -50,6 +48,8 @@ implants = list(/obj/item/implant/mindshield) + id_trim = /datum/id_trim/job/security_medic + /obj/effect/landmark/start/security_medic name = "Security Medic" icon_state = "Security Officer" diff --git a/modular_skyrat/modules/sec_haul/code/security_sergeant/security_sergeant.dm b/modular_skyrat/modules/sec_haul/code/security_sergeant/security_sergeant.dm index 6c7f6d90052..28b0574d20d 100644 --- a/modular_skyrat/modules/sec_haul/code/security_sergeant/security_sergeant.dm +++ b/modular_skyrat/modules/sec_haul/code/security_sergeant/security_sergeant.dm @@ -13,8 +13,6 @@ outfit = /datum/outfit/job/security_sergeant - access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MECH_SECURITY, ACCESS_MAINT_TUNNELS, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_FORENSICS_LOCKERS, ACCESS_MINERAL_STOREROOM, ACCESS_ENTER_GENPOP) // SKYRAT EDIT: Adds ACCESS_ENTER_GENPOP and ACCESS_LEAVE_GENPOP - minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_MECH_SECURITY, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM, ACCESS_MAINT_TUNNELS, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP) // SKYRAT EDIT: Adds ACCESS_ENTER_GENPOP and ACCESS_LEAVE_GENPOP paycheck = PAYCHECK_HARD paycheck_department = ACCOUNT_SEC @@ -50,6 +48,8 @@ implants = list(/obj/item/implant/mindshield) + id_trim = /datum/id_trim/job/security_sergeant + /obj/effect/landmark/start/security_sergeant name = "Security Sergeant" icon_state = "Security Officer" diff --git a/tgstation.dme b/tgstation.dme index 69bd3e66547..f37dbddfc2f 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" @@ -326,6 +327,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" @@ -691,6 +693,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" @@ -3452,6 +3461,7 @@ #include "modular_skyrat\master_files\code\_globalvars\~maint_loot.dm" #include "modular_skyrat\master_files\code\controllers\configuration\entries.dm" #include "modular_skyrat\master_files\code\datums\ert.dm" +#include "modular_skyrat\master_files\code\datums\id_trim\jobs.dm" #include "modular_skyrat\master_files\code\datums\martial\cqcplus.dm" #include "modular_skyrat\master_files\code\datums\traits\good.dm" #include "modular_skyrat\master_files\code\datums\traits\negative.dm" @@ -3607,8 +3617,6 @@ #include "modular_skyrat\modules\bluespace_locker\code\controllers\subsystem\bluespace_locker.dm" #include "modular_skyrat\modules\bluespace_locker\code\game\area\Space_Station_13_areas.dm" #include "modular_skyrat\modules\bluespace_locker\code\game\objects\structures\crates_lockers\bluespace_locker.dm" -#include "modular_skyrat\modules\card\access.dm" -#include "modular_skyrat\modules\card\card.dm" #include "modular_skyrat\modules\cargo\code\bloat.dm" #include "modular_skyrat\modules\cargo\code\goodies.dm" #include "modular_skyrat\modules\cargo\code\packs.dm" @@ -3633,13 +3641,6 @@ #include "modular_skyrat\modules\cloning\code\game\objects\items\circuitboards\computer_circuitboards.dm" #include "modular_skyrat\modules\cloning\code\game\objects\items\circuitboards\machine_circuitboards.dm" #include "modular_skyrat\modules\cloning\code\modules\atmospherics\gasmixtures\immutable_mixtures.dm" -#include "modular_skyrat\modules\cloning\code\modules\jobs\access.dm" -#include "modular_skyrat\modules\cloning\code\modules\jobs\job_types\chemist.dm" -#include "modular_skyrat\modules\cloning\code\modules\jobs\job_types\chief_medical_officer.dm" -#include "modular_skyrat\modules\cloning\code\modules\jobs\job_types\geneticist.dm" -#include "modular_skyrat\modules\cloning\code\modules\jobs\job_types\medical_doctor.dm" -#include "modular_skyrat\modules\cloning\code\modules\jobs\job_types\paramedic.dm" -#include "modular_skyrat\modules\cloning\code\modules\jobs\job_types\virologist.dm" #include "modular_skyrat\modules\cloning\code\modules\library\lib_items.dm" #include "modular_skyrat\modules\cloning\code\modules\mob\living\brain\brain_item.dm" #include "modular_skyrat\modules\cloning\code\modules\research\designs\comp_board_designs.dm" diff --git a/tgui/packages/tgui-panel/styles/goon/chat-dark.scss b/tgui/packages/tgui-panel/styles/goon/chat-dark.scss index d9266c6a6b6..03a8657c0b4 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 b6f8f6e764d..23b06379190 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 ( +