From 4b80bb178a21838ea5109161f2265cebb3f9c4a2 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Thu, 6 Jan 2022 14:15:41 +0100 Subject: [PATCH] [MIRROR] Circuit ID Components [MDB IGNORE] (#10472) * Circuit ID Components (#63817) This PR adds several circuit components used for scanning and checking ID cards: The Get ID component returns the ID the target is wearing or holding The Read ID Info component returns the name, rank, and age registered on the ID The Read ID Access component returns a list of all the accesses on the ID The Access Checker component does comparisons on lists of numbers, specifically tailored for checking ID access Due to the access checker using a similar UI element to the airlock electronics, that element has been moved to its own file in tgui/interfaces/common. This change is not player-facing. * Circuit ID Components Co-authored-by: Y0SH1M4S73R --- .../research/designs/wiremod_designs.dm | 19 +++ code/modules/research/techweb/all_nodes.dm | 4 + .../wiremod/components/id/access_checker.dm | 138 ++++++++++++++++++ .../wiremod/components/id/access_reader.dm | 32 ++++ code/modules/wiremod/components/id/getter.dm | 34 +++++ .../wiremod/components/id/info_reader.dm | 44 ++++++ tgstation.dme | 4 + .../tgui/interfaces/AirlockElectronics.js | 137 +---------------- .../tgui/interfaces/CircuitAccessChecker.js | 44 ++++++ .../tgui/interfaces/common/AccessConfig.js | 131 +++++++++++++++++ 10 files changed, 454 insertions(+), 133 deletions(-) create mode 100644 code/modules/wiremod/components/id/access_checker.dm create mode 100644 code/modules/wiremod/components/id/access_reader.dm create mode 100644 code/modules/wiremod/components/id/getter.dm create mode 100644 code/modules/wiremod/components/id/info_reader.dm create mode 100644 tgui/packages/tgui/interfaces/CircuitAccessChecker.js create mode 100644 tgui/packages/tgui/interfaces/common/AccessConfig.js diff --git a/code/modules/research/designs/wiremod_designs.dm b/code/modules/research/designs/wiremod_designs.dm index d06b2340f59..673767bef72 100644 --- a/code/modules/research/designs/wiremod_designs.dm +++ b/code/modules/research/designs/wiremod_designs.dm @@ -341,6 +341,25 @@ id = "comp_filter_list" build_path = /obj/item/circuit_component/filter_list +/datum/design/component/id_getter + name = "ID Getter Component" + id = "comp_id_getter" + build_path = /obj/item/circuit_component/id_getter + +/datum/design/component/id_info_reader + name = "ID Getter Component" + id = "comp_id_info_reader" + build_path = /obj/item/circuit_component/id_info_reader + +/datum/design/component/id_access_reader + name = "ID Access Reader Component" + id = "comp_id_access_reader" + build_path = /obj/item/circuit_component/id_access_reader + +/datum/design/component/access_checker + name = "Access Checker Component" + id = "comp_access_checker" + build_path = /obj/item/circuit_component/compare/access /datum/design/compact_remote_shell name = "Compact Remote Shell" diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index 4c1bfd024ee..45055f92723 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -236,6 +236,7 @@ description = "Research on how to fully exploit the power of integrated circuits" design_ids = list( "circuit_multitool", + "comp_access_checker", "comp_arithmetic", "comp_binary_convert", "comp_clock", @@ -251,6 +252,9 @@ "comp_gps", "comp_health", "comp_hear", + "comp_id_access_reader", + "comp_id_getter", + "comp_id_info_reader", "comp_index", "comp_index_assoc", "comp_index_table", diff --git a/code/modules/wiremod/components/id/access_checker.dm b/code/modules/wiremod/components/id/access_checker.dm new file mode 100644 index 00000000000..8f302e5f371 --- /dev/null +++ b/code/modules/wiremod/components/id/access_checker.dm @@ -0,0 +1,138 @@ +/obj/item/circuit_component/compare/access + display_name = "Access Checker" + desc = "Performs a basic comparison between two numerical lists, with additional functions that help in using it to check access on IDs." + category = "ID" + + input_port_amount = 0 //Uses custom ports for its comparisons + + /// A list of the accesses to check + var/datum/port/input/subject_accesses + + /// A list of the accesses required to return true + var/datum/port/input/required_accesses + + /// Whether to check for all or any of the required accesses + var/datum/port/input/check_any + + ui_buttons = list("id-card" = "access") + +/obj/item/circuit_component/compare/access/Initialize(mapload) + . = ..() + gen_access() + +/obj/item/circuit_component/compare/access/get_ui_notices() + . = ..() + . += create_ui_notice("When \"Check Any\" is true, returns true if \"Access To Check\" contains ANY value in \"Required Access\".", "orange", "info") + . += create_ui_notice("When \"Check Any\" is false, returns true only if \"Access To Check\" contains ALL values in \"Required Access\".", "orange", "info") + +/obj/item/circuit_component/compare/access/populate_custom_ports() + subject_accesses = add_input_port("Access To Check", PORT_TYPE_LIST(PORT_TYPE_NUMBER)) + required_accesses = add_input_port("Required Access", PORT_TYPE_LIST(PORT_TYPE_NUMBER)) + check_any = add_input_port("Check Any", PORT_TYPE_NUMBER) + +/obj/item/circuit_component/compare/access/save_data_to_list(list/component_data) + . = ..() + component_data["input_ports_stored_data"] = list(required_accesses.name = list("stored_data" = required_accesses.value)) + +/obj/item/circuit_component/compare/access/add_to(obj/item/integrated_circuit/added_to) + . = ..() + RegisterSignal(added_to, COMSIG_CIRCUIT_POST_LOAD, .proc/on_post_load) + +/obj/item/circuit_component/compare/access/removed_from(obj/item/integrated_circuit/removed_from) + UnregisterSignal(removed_from, COMSIG_CIRCUIT_POST_LOAD) + return ..() + +/obj/item/circuit_component/compare/access/proc/on_post_load(datum/source) + regenerate_access() + +/obj/item/circuit_component/compare/access/proc/regenerate_access() + var/check_any_value = check_any.value + var/list/required_accesses_list = required_accesses.value + if(!islist(required_accesses_list)) + return + if(check_any_value) + LAZYCLEARLIST(req_access) + req_one_access = required_accesses_list.Copy() + else + LAZYCLEARLIST(req_one_access) + req_access = required_accesses_list.Copy() + +/obj/item/circuit_component/compare/access/do_comparisons(list/ports) + return check_access_list(subject_accesses.value) + +/obj/item/circuit_component/compare/access/ui_perform_action(mob/user, action) + if(length(required_accesses.connected_ports)) + balloon_alert(user, "Disconnect port before manually configuring!") + return + interact(user) + +/obj/item/circuit_component/compare/access/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "CircuitAccessChecker", display_name) + ui.open() + +/obj/item/circuit_component/compare/access/ui_static_data(mob/user) + var/list/data = list() + + 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] + if(parent?.admin_only) + regions += tgui_region_data[REGION_CENTCOM] + regions += tgui_region_data[REGION_ALL_GLOBAL] + data["regions"] = regions + return data + +/obj/item/circuit_component/compare/access/ui_data(mob/user) + var/list/data = list() + data["accesses"] = required_accesses.value + data["oneAccess"] = check_any.value + return data + +/obj/item/circuit_component/compare/access/ui_act(action, params) + . = ..() + if(.) + return + + switch(action) + if("clear_all") + required_accesses.set_value(list()) + check_any.set_value(0) + . = TRUE + if("grant_all") + required_accesses.set_value(SSid_access.get_region_access_list(list(REGION_ALL_STATION))) + . = TRUE + if("one_access") + check_any.set_value(!check_any.value) + . = TRUE + if("set") + var/list/required_accesses_list = required_accesses.value + var/list/new_accesses_value = LAZYCOPY(required_accesses_list) + var/access = text2num(params["access"]) + if (!(access in new_accesses_value)) + new_accesses_value += access + else + new_accesses_value -= access + required_accesses.set_value(new_accesses_value) + . = TRUE + if("grant_region") + var/list/required_accesses_list = required_accesses.value + var/list/required_accesses_value = LAZYCOPY(required_accesses_list) + var/region = params["region"] + if(isnull(region)) + return + required_accesses.set_value(required_accesses_value | SSid_access.get_region_access_list(list(region))) + . = TRUE + if("deny_region") + var/list/required_accesses_list = required_accesses.value + var/list/required_accesses_value = LAZYCOPY(required_accesses_list) + var/region = params["region"] + if(isnull(region)) + return + required_accesses.set_value(required_accesses_value - SSid_access.get_region_access_list(list(region))) + . = TRUE + if(.) + regenerate_access() + SStgui.update_uis(parent) diff --git a/code/modules/wiremod/components/id/access_reader.dm b/code/modules/wiremod/components/id/access_reader.dm new file mode 100644 index 00000000000..cb576daa7e5 --- /dev/null +++ b/code/modules/wiremod/components/id/access_reader.dm @@ -0,0 +1,32 @@ +/obj/item/circuit_component/id_access_reader + display_name = "Read ID Access" + desc = "A component that reads the access on an ID." + category = "ID" + + /// The input port + var/datum/port/input/target + + /// A list of the accesses on the ID + var/datum/port/output/access_port + + circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL + + var/max_range = 1 + +/obj/item/circuit_component/id_access_reader/get_ui_notices() + . = ..() + . += create_ui_notice("Maximum Range: [max_range] tiles.", "orange", "info") + +/obj/item/circuit_component/id_access_reader/populate_ports() + target = add_input_port("Target", PORT_TYPE_ATOM) + access_port = add_output_port("Access", PORT_TYPE_LIST(PORT_TYPE_NUMBER)) + + +/obj/item/circuit_component/id_access_reader/input_received(datum/port/input/port) + var/obj/item/card/id/target_item = target.value + var/turf/current_turf = get_location() + var/turf/target_turf = get_turf(target_item) + if(!istype(target_item) || get_dist(current_turf, target_turf) > max_range || current_turf.z != target_turf.z) + access_port.set_output(null) + return + access_port.set_output(target_item.GetAccess()) diff --git a/code/modules/wiremod/components/id/getter.dm b/code/modules/wiremod/components/id/getter.dm new file mode 100644 index 00000000000..7143e5689d8 --- /dev/null +++ b/code/modules/wiremod/components/id/getter.dm @@ -0,0 +1,34 @@ +/obj/item/circuit_component/id_getter + display_name = "Get ID" + desc = "A component that returns the first available ID card on an organism." + category = "ID" + + /// The input port + var/datum/port/input/target + + /// Whether to prioritize the target's hands when checking for an ID + var/datum/port/input/prioritize_hands + + /// The reference to the ID + var/datum/port/output/id_port + + circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL + + var/max_range = 1 + +/obj/item/circuit_component/id_getter/get_ui_notices() + . = ..() + . += create_ui_notice("Maximum Range: [max_range] tiles.", "orange", "info") + +/obj/item/circuit_component/id_getter/populate_ports() + target = add_input_port("Target", PORT_TYPE_ATOM) + prioritize_hands = add_input_port("Prioritize Hands", PORT_TYPE_NUMBER) + id_port = add_output_port("ID", PORT_TYPE_ATOM) + +/obj/item/circuit_component/id_getter/input_received(datum/port/input/port) + var/mob/living/target_mob = target.value + var/turf/current_turf = get_location() + if(!istype(target_mob) || get_dist(current_turf, target_mob) > max_range || current_turf.z != target_mob.z) + id_port.set_output(null) + return + id_port.set_output(target_mob.get_idcard(prioritize_hands.value)) diff --git a/code/modules/wiremod/components/id/info_reader.dm b/code/modules/wiremod/components/id/info_reader.dm new file mode 100644 index 00000000000..ee81a87d4d7 --- /dev/null +++ b/code/modules/wiremod/components/id/info_reader.dm @@ -0,0 +1,44 @@ +/obj/item/circuit_component/id_info_reader + display_name = "Read ID Info" + desc = "A component that reads the name, job, and age on an ID." + category = "ID" + + /// The input port + var/datum/port/input/target + + /// The name registered on the ID + var/datum/port/output/name_port + + /// The rank registered on the ID + var/datum/port/output/rank_port + + /// The age registered on the ID + var/datum/port/output/age_port + + circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL + + var/max_range = 1 + +/obj/item/circuit_component/id_info_reader/get_ui_notices() + . = ..() + . += create_ui_notice("Maximum Range: [max_range] tiles.", "orange", "info") + +/obj/item/circuit_component/id_info_reader/populate_ports() + target = add_input_port("Target", PORT_TYPE_ATOM) + name_port = add_output_port("Name", PORT_TYPE_STRING) + rank_port = add_output_port("Rank", PORT_TYPE_STRING) + age_port = add_output_port("Age", PORT_TYPE_NUMBER) + + +/obj/item/circuit_component/id_info_reader/input_received(datum/port/input/port) + var/obj/item/card/id/target_item = target.value + var/turf/current_turf = get_location() + var/turf/target_turf = get_turf(target_item) + if(!istype(target_item) || get_dist(current_turf, target_turf) > max_range || current_turf.z != target_turf.z) + name_port.set_output(null) + rank_port.set_output(null) + age_port.set_output(null) + return + name_port.set_output(target_item.registered_name) + rank_port.set_output(target_item.assignment) + age_port.set_output(target_item.registered_age) diff --git a/tgstation.dme b/tgstation.dme index acaa4d006f5..0f8e4dd608a 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4103,6 +4103,10 @@ #include "code\modules\wiremod\components\bci\hud\counter_overlay.dm" #include "code\modules\wiremod\components\bci\hud\object_overlay.dm" #include "code\modules\wiremod\components\bci\hud\target_intercept.dm" +#include "code\modules\wiremod\components\id\access_checker.dm" +#include "code\modules\wiremod\components\id\access_reader.dm" +#include "code\modules\wiremod\components\id\getter.dm" +#include "code\modules\wiremod\components\id\info_reader.dm" #include "code\modules\wiremod\components\list\assoc_literal.dm" #include "code\modules\wiremod\components\list\concat.dm" #include "code\modules\wiremod\components\list\filter.dm" diff --git a/tgui/packages/tgui/interfaces/AirlockElectronics.js b/tgui/packages/tgui/interfaces/AirlockElectronics.js index 24d2cefa757..5a588825703 100644 --- a/tgui/packages/tgui/interfaces/AirlockElectronics.js +++ b/tgui/packages/tgui/interfaces/AirlockElectronics.js @@ -1,7 +1,7 @@ -import { useBackend, useLocalState } from '../backend'; -import { Button, Flex, Grid, Input, LabeledList, Section, Tabs } from '../components'; +import { useBackend } from '../backend'; +import { Button, Input, LabeledList, Section } from '../components'; import { Window } from '../layouts'; -import { sortBy } from 'common/collections'; +import { AccessConfig } from './common/AccessConfig'; export const AirlockElectronics = (props, context) => { const { act, data } = useBackend(context); @@ -78,7 +78,7 @@ export const AirlockElectronics = (props, context) => { - act('set', { @@ -96,132 +96,3 @@ 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/CircuitAccessChecker.js b/tgui/packages/tgui/interfaces/CircuitAccessChecker.js new file mode 100644 index 00000000000..820d3a943df --- /dev/null +++ b/tgui/packages/tgui/interfaces/CircuitAccessChecker.js @@ -0,0 +1,44 @@ +import { useBackend } from "../backend"; +import { Window } from "../layouts"; +import { LabeledList, Button } from "../components"; +import { AccessConfig } from "./common/AccessConfig"; + +export const CircuitAccessChecker = (props, context) => { + const { act, data } = useBackend(context); + const { + oneAccess, + } = data; + const regions = data.regions || []; + const accesses = data.accesses || []; + return ( + + + + +