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 ( + + + + +