From 8acef974bd313b8c56c4430d1a1de1dffd1328a7 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Sun, 20 Mar 2022 17:36:36 +0100 Subject: [PATCH] [MIRROR] Adds format (associative) list wiremod component [MDB IGNORE] (#12189) * Adds format (associative) list wiremod component (#65404) Adds a wiremod component called "Format List" and one called "Format Associative List" that you get at round-start. It accepts a format string and a list of parameters. For "Format List": The format string contains normal text, and codes of the form %n (eg. %1, %2, %3) that correlate to indexes in the param list. For "Format Associative List": The format string contains normal text, and codes of the form %key (eg. %name, %health) that correlate to keys in the associative param list. The param list can contain any types, which will be automatically converted to strings. Conversion of entities to strings still follows the range rule of To String. Important to keep in mind if you're formatting an NTNet transmission. For the associative version, the keys must be strings comprised of letters, numbers, or underscore. Simplest example that says "Bob McToolbox pressed the button.": * Adds format (associative) list wiremod component Co-authored-by: Tastyfish --- .../research/designs/wiremod_designs.dm | 10 ++ code/modules/research/techweb/all_nodes.dm | 2 + .../modules/wiremod/components/list/format.dm | 118 ++++++++++++++++++ tgstation.dme | 1 + 4 files changed, 131 insertions(+) create mode 100644 code/modules/wiremod/components/list/format.dm diff --git a/code/modules/research/designs/wiremod_designs.dm b/code/modules/research/designs/wiremod_designs.dm index 8c936b53563..d88053acb34 100644 --- a/code/modules/research/designs/wiremod_designs.dm +++ b/code/modules/research/designs/wiremod_designs.dm @@ -73,6 +73,16 @@ id = "comp_delay" build_path = /obj/item/circuit_component/delay +/datum/design/component/format + name = "Format List Component" + id = "comp_format" + build_path = /obj/item/circuit_component/format + +/datum/design/component/format_assoc + name = "Format Associative List Component" + id = "comp_format_assoc" + build_path = /obj/item/circuit_component/format/assoc + /datum/design/component/index name = "Index Component" id = "comp_index" diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index eab7a8330f7..1be56f7d75b 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -253,6 +253,8 @@ "comp_element_find", "comp_filter_list", "comp_foreach", + "comp_format", + "comp_format_assoc", "comp_get_column", "comp_gps", "comp_health", diff --git a/code/modules/wiremod/components/list/format.dm b/code/modules/wiremod/components/list/format.dm new file mode 100644 index 00000000000..2ff98535588 --- /dev/null +++ b/code/modules/wiremod/components/list/format.dm @@ -0,0 +1,118 @@ +/** + * # Format List Component + * + * Formats lists by replacing %n in format string with nth parameter. + * Alternative to the Concatenate component. + */ +/obj/item/circuit_component/format + display_name = "Format List" + desc = "A component that formats lists, replacing %n in the format string with corresponding nth list item." + category = "List" + + var/static/regex/format_component/list_param_regex = new(@"%([0-9]+)", "g") + + /// The regex used to find a parameter. + var/regex/format_component/param_regex + + var/datum/port/input/format_port + var/datum/port/input/param_list_port + + // Range for entity tostring to work, same mechanic as the To String component + var/max_range = 7 + + /// The result from the output + var/datum/port/output/output + circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL + +/obj/item/circuit_component/format/Initialize(mapload) + . = ..() + param_regex = list_param_regex + +/obj/item/circuit_component/format/proc/make_params_port() + param_list_port = add_input_port("Params", PORT_TYPE_LIST(PORT_TYPE_ANY)) + +/obj/item/circuit_component/format/populate_ports() + format_port = add_input_port("Format", PORT_TYPE_STRING) + make_params_port() + + output = add_output_port("Output", PORT_TYPE_STRING) + +/** + * Get an item from the list. + * Return null to indicate invalid index. + * Arguments: + * * param_list - The resolved list. + * * index_string - The raw list index, as a string. + */ +/obj/item/circuit_component/format/proc/get_list_item(list/param_list, index_string) + var/index = text2num(index_string) + if(index < 1 || index > length(param_list)) + return null + + return param_list[index] + +/obj/item/circuit_component/format/input_received(datum/port/input/port, list/return_values) + . = ..() + + // Inject the parameters. + param_regex.context = src + output.set_output(param_regex.Replace(format_port.value, /regex/format_component/proc/process_format_component_param)) + param_regex.context = null + +/** + * # Format Associative List Component + * + * Formats lists by replacing %n in format string with nth parameter. + * Alternative to the Concatenate component. + */ +/obj/item/circuit_component/format/assoc + display_name = "Format Associative List" + desc = "A component that formats associative lists, replacing %key in the format string with corresponding list\[key] item." + + var/static/regex/format_component/assoc_param_regex = new(@"%([a-zA-Z0-9_]+)", "g") + +/obj/item/circuit_component/format/assoc/Initialize(mapload) + . = ..() + param_regex = assoc_param_regex + +/obj/item/circuit_component/format/assoc/get_list_item(list/param_list, index_string) + return param_list[index_string] + +/obj/item/circuit_component/format/assoc/make_params_port() + param_list_port = add_input_port("Params", PORT_TYPE_ASSOC_LIST(PORT_TYPE_STRING, PORT_TYPE_ANY)) + +/** + * # Subtype of regex that holds context to /obj/item/circuit_component/format + */ +/regex/format_component + var/obj/item/circuit_component/format/context + +/** + * Replace %n with the actual param, as a string. + * Arguments: + * * match - The full %1 regex match. Unused. + * * index_string - Just the "1" of the %1 format, actually used. + */ +/regex/format_component/proc/process_format_component_param(match, index_string) + // The static regex_context var is what you'd expect src to be, but src is actually the regex instance. + var/list/param_list = context.param_list_port.value + if(!islist(param_list)) + return @"[NO LIST]" + + var/value = context.get_list_item(param_list, index_string) + if(value == null) + return @"[BAD INDEX]" + + // If this is a datum or atom, it's likely wrapped in a weakref. + if(isweakref(value)) + var/datum/weakref/weak_value = value + value = weak_value.resolve() + + // Working with entities is constrained by range, just as with To String. + if(isatom(value)) + var/turf/location = context.get_location() + var/turf/target_location = get_turf(value) + if(target_location.z != location.z || get_dist(location, target_location) > context.max_range) + return @"[OUT OF RANGE]" + + return "[value]" diff --git a/tgstation.dme b/tgstation.dme index 0cc2f11b4c0..6c14ef31184 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4306,6 +4306,7 @@ #include "code\modules\wiremod\components\list\concat.dm" #include "code\modules\wiremod\components\list\filter.dm" #include "code\modules\wiremod\components\list\foreach.dm" +#include "code\modules\wiremod\components\list\format.dm" #include "code\modules\wiremod\components\list\get_column.dm" #include "code\modules\wiremod\components\list\index.dm" #include "code\modules\wiremod\components\list\index_table.dm"