From 0432d97d7c6fc5dfa4cf64796405d66d26c98e39 Mon Sep 17 00:00:00 2001
From: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
Date: Mon, 2 Aug 2021 10:19:48 +0300
Subject: [PATCH] BCI overlays and click interception (#60503)
---
code/__DEFINES/cooldowns.dm | 1 +
.../research/designs/wiremod_designs.dm | 27 ++++
code/modules/research/techweb/all_nodes.dm | 18 ++-
code/modules/wiremod/component.dm | 8 ++
.../wiremod/components/hud/bar_overlay.dm | 60 ++++++++
.../wiremod/components/hud/counter_overlay.dm | 102 ++++++++++++++
.../wiremod/components/hud/object_overlay.dm | 132 ++++++++++++++++++
.../components/hud/target_intercept.dm | 64 +++++++++
.../wiremod/shell/brain_computer_interface.dm | 18 +--
icons/hud/screen_bci.dmi | Bin 0 -> 1665 bytes
tgstation.dme | 4 +
11 files changed, 423 insertions(+), 11 deletions(-)
create mode 100644 code/modules/wiremod/components/hud/bar_overlay.dm
create mode 100644 code/modules/wiremod/components/hud/counter_overlay.dm
create mode 100644 code/modules/wiremod/components/hud/object_overlay.dm
create mode 100644 code/modules/wiremod/components/hud/target_intercept.dm
create mode 100644 icons/hud/screen_bci.dmi
diff --git a/code/__DEFINES/cooldowns.dm b/code/__DEFINES/cooldowns.dm
index 5f87af8fd8e..fe72b872053 100644
--- a/code/__DEFINES/cooldowns.dm
+++ b/code/__DEFINES/cooldowns.dm
@@ -48,6 +48,7 @@
#define COOLDOWN_CIRCUIT_SPEECH "circuit_speech"
#define COOLDOWN_CIRCUIT_PATHFIND_SAME "circuit_pathfind_same"
#define COOLDOWN_CIRCUIT_PATHFIND_DIF "circuit_pathfind_different"
+#define COOLDOWN_CIRCUIT_TARGET_INTERCEPT "circuit_target_intercept"
//TIMER COOLDOWN MACROS
diff --git a/code/modules/research/designs/wiremod_designs.dm b/code/modules/research/designs/wiremod_designs.dm
index 3b5fb12c639..2d6ec2b75b6 100644
--- a/code/modules/research/designs/wiremod_designs.dm
+++ b/code/modules/research/designs/wiremod_designs.dm
@@ -238,6 +238,33 @@
id = "comp_module"
build_path = /obj/item/circuit_component/module
+/datum/design/component/bci
+ category = list("Circuitry", "BCI Components")
+
+/datum/design/component/bci/bci_action
+ name = "BCI Action Component"
+ id = "comp_bci_action"
+ build_path = /obj/item/circuit_component/bci_action
+
+/datum/design/component/bci/object_overlay
+ name = "Object Overlay Component"
+ id = "comp_object_overlay"
+ build_path = /obj/item/circuit_component/object_overlay
+
+/datum/design/component/bci/bar_overlay
+ name = "Bar Overlay Component"
+ id = "comp_bar_overlay"
+ build_path = /obj/item/circuit_component/object_overlay/bar
+
+/datum/design/component/bci/target_intercept
+ name = "BCI Target Interceptor"
+ id = "comp_target_intercept"
+ build_path = /obj/item/circuit_component/target_intercept
+
+/datum/design/component/bci/counter_overlay
+ name = "Counter Overlay Component"
+ id = "comp_counter_overlay"
+ build_path = /obj/item/circuit_component/counter_overlay
/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 f9241e18cd1..2dcdcee17bc 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -657,8 +657,6 @@
description = "Grants access to more complicated shell designs."
prereq_ids = list("basic_circuitry", "engineering")
design_ids = list(
- "bci_implanter",
- "bci_shell",
"bot_shell",
"door_shell",
"controller_shell",
@@ -667,6 +665,22 @@
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
+/datum/techweb_node/bci_shells
+ id = "bci_shells"
+ display_name = "Brain-Computer Interfaces"
+ description = "Grants access to biocompatable shell designs and components."
+ prereq_ids = list("adv_shells")
+ design_ids = list(
+ "bci_implanter",
+ "bci_shell",
+ "comp_bar_overlay",
+ "comp_bci_action",
+ "comp_target_intercept",
+ "comp_counter_overlay",
+ "comp_object_overlay",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 500)
+
/datum/techweb_node/movable_shells_tech
id = "movable_shells"
display_name = "Movable Shell Research"
diff --git a/code/modules/wiremod/component.dm b/code/modules/wiremod/component.dm
index 376a7efa004..4016f2e6fb4 100644
--- a/code/modules/wiremod/component.dm
+++ b/code/modules/wiremod/component.dm
@@ -50,6 +50,9 @@
// Whether the component is removable or not. Only affects user UI
var/removable = TRUE
+ // Defines which shells can accept this component. If set to null then all shells can use it.
+ var/required_shells = null
+
/obj/item/circuit_component/Initialize()
. = ..()
if(name == COMPONENT_DEFAULT_NAME)
@@ -200,6 +203,11 @@
/// Called when this component is about to be added to an integrated_circuit.
/obj/item/circuit_component/proc/add_to(obj/item/integrated_circuit/added_to)
+ if(required_shells && LAZYLEN(required_shells))
+ for(var/shell_type in required_shells)
+ if(istype(added_to, shell_type) || istype(added_to.loc, shell_type))
+ return TRUE
+ return FALSE
return TRUE
/// Called when this component is removed from an integrated_circuit.
diff --git a/code/modules/wiremod/components/hud/bar_overlay.dm b/code/modules/wiremod/components/hud/bar_overlay.dm
new file mode 100644
index 00000000000..004d7b38e34
--- /dev/null
+++ b/code/modules/wiremod/components/hud/bar_overlay.dm
@@ -0,0 +1,60 @@
+/**
+ * # Bar Overlay Component
+ *
+ * Basically an advanced verion of object overlay component that shows a horizontal/vertical bar.
+ * Requires a BCI shell.
+ */
+
+#define BAR_OVERLAY_LIMIT 10
+
+/obj/item/circuit_component/object_overlay/bar
+ display_name = "Bar Overlay"
+ desc = "Requires a BCI shell. A component that shows a bar overlay ontop of an object from a range of 0 to 100."
+
+ var/datum/port/input/bar_number
+
+/obj/item/circuit_component/object_overlay/bar/Initialize()
+ . = ..()
+ bar_number = add_input_port("Number", PORT_TYPE_NUMBER)
+
+/obj/item/circuit_component/object_overlay/bar/populate_options()
+ var/static/component_options_bar = list(
+ "Vertical",
+ "Horizontal"
+ )
+ options = component_options_bar
+
+ var/static/options_to_icons_bar = list(
+ "Vertical" = "barvert",
+ "Horizontal" = "barhoriz"
+ )
+ options_map = options_to_icons_bar
+
+/obj/item/circuit_component/object_overlay/bar/show_to_owner(atom/target_atom, mob/living/owner)
+ if(LAZYLEN(active_overlays) >= BAR_OVERLAY_LIMIT)
+ return
+
+ if(active_overlays[target_atom])
+ QDEL_NULL(active_overlays[target_atom])
+
+ var/number_clear = clamp(bar_number.input_value, 0, 100)
+ if(current_option == "Horizontal")
+ number_clear = round(number_clear / 6.25) * 6.25
+ else if(current_option == "Vertical")
+ number_clear = round(number_clear / 10) * 10
+ var/image/cool_overlay = image(icon = 'icons/hud/screen_bci.dmi', loc = target_atom, icon_state = "[options_map[current_option]][number_clear]", layer = RIPPLE_LAYER)
+
+ if(image_pixel_x.input_value)
+ cool_overlay.pixel_x = image_pixel_x.input_value
+
+ if(image_pixel_y.input_value)
+ cool_overlay.pixel_y = image_pixel_y.input_value
+
+ active_overlays[target_atom] = WEAKREF(target_atom.add_alt_appearance(
+ /datum/atom_hud/alternate_appearance/basic/one_person,
+ "bar_overlay_[REF(src)]",
+ cool_overlay,
+ owner,
+ ))
+
+#undef BAR_OVERLAY_LIMIT
diff --git a/code/modules/wiremod/components/hud/counter_overlay.dm b/code/modules/wiremod/components/hud/counter_overlay.dm
new file mode 100644
index 00000000000..c2158ec76a1
--- /dev/null
+++ b/code/modules/wiremod/components/hud/counter_overlay.dm
@@ -0,0 +1,102 @@
+/**
+ * # Counter Overlay Component
+ *
+ * Shows an counter overlay.
+ * Requires a BCI shell.
+ */
+
+/obj/item/circuit_component/counter_overlay
+ display_name = "Counter Overlay"
+ desc = "A component that shows an three digit counter. Requires a BCI shell."
+
+ required_shells = list(/obj/item/organ/cyberimp/bci)
+
+ var/datum/port/input/counter_number
+
+ var/datum/port/input/image_pixel_x
+ var/datum/port/input/image_pixel_y
+
+ var/datum/port/input/signal_update
+
+ var/obj/item/organ/cyberimp/bci/bci
+ var/list/numbers = list()
+ var/counter_appearance
+
+/obj/item/circuit_component/counter_overlay/Initialize()
+ . = ..()
+ counter_number = add_input_port("Displayed Number", PORT_TYPE_NUMBER)
+
+ signal_update = add_input_port("Update Overlay", PORT_TYPE_SIGNAL)
+
+ image_pixel_x = add_input_port("X-Axis Shift", PORT_TYPE_NUMBER)
+ image_pixel_y = add_input_port("Y-Axis Shift", PORT_TYPE_NUMBER)
+
+/obj/item/circuit_component/counter_overlay/register_shell(atom/movable/shell)
+ bci = shell
+ RegisterSignal(shell, COMSIG_ORGAN_REMOVED, .proc/on_organ_removed)
+
+/obj/item/circuit_component/counter_overlay/unregister_shell(atom/movable/shell)
+ bci = null
+ QDEL_NULL(counter_appearance)
+ for(var/number in numbers)
+ QDEL_NULL(number)
+ UnregisterSignal(shell, COMSIG_ORGAN_REMOVED)
+
+/obj/item/circuit_component/counter_overlay/input_received(datum/port/input/port)
+ . = ..()
+
+ if(. || !bci)
+ return
+
+ var/mob/living/owner = bci.owner
+
+ if(!owner || !istype(owner) || !owner.client)
+ return
+
+ for(var/number in numbers)
+ QDEL_NULL(number)
+ numbers = list()
+
+ QDEL_NULL(counter_appearance)
+ var/image/counter = image(icon = 'icons/hud/screen_bci.dmi', icon_state = "hud_numbers", loc = owner)
+ if(image_pixel_x.input_value)
+ counter.pixel_x = image_pixel_x.input_value
+ if(image_pixel_y.input_value)
+ counter.pixel_y = image_pixel_y.input_value
+
+ counter_appearance = WEAKREF(owner.add_alt_appearance(
+ /datum/atom_hud/alternate_appearance/basic/one_person,
+ "counter_overlay_[REF(src)]",
+ counter,
+ owner,
+ ))
+
+ var/cleared_number = clamp(round(counter_number.input_value), 0, 999)
+
+ for(var/i = 1 to 3)
+ var/cur_num = round(cleared_number / (10 ** (3 - i))) % 10
+ var/image/number = image(icon = 'icons/hud/screen_bci.dmi', icon_state = "hud_number_[cur_num]", loc = owner)
+
+ if(image_pixel_x.input_value)
+ number.pixel_x = image_pixel_x.input_value + (i - 1) * 9
+ if(image_pixel_y.input_value)
+ number.pixel_y = image_pixel_y.input_value
+
+ numbers.Add(WEAKREF(owner.add_alt_appearance(
+ /datum/atom_hud/alternate_appearance/basic/one_person,
+ "counter_overlay_[REF(src)]_[i]",
+ number,
+ owner,
+ )))
+
+/obj/item/circuit_component/counter_overlay/proc/on_organ_removed(datum/source, mob/living/carbon/owner)
+ SIGNAL_HANDLER
+ QDEL_NULL(counter_appearance)
+ for(var/number in numbers)
+ QDEL_NULL(number)
+
+/obj/item/circuit_component/counter_overlay/Destroy()
+ QDEL_NULL(counter_appearance)
+ for(var/number in numbers)
+ QDEL_NULL(number)
+ return ..()
diff --git a/code/modules/wiremod/components/hud/object_overlay.dm b/code/modules/wiremod/components/hud/object_overlay.dm
new file mode 100644
index 00000000000..dd502329f97
--- /dev/null
+++ b/code/modules/wiremod/components/hud/object_overlay.dm
@@ -0,0 +1,132 @@
+/**
+ * # Object Overlay Component
+ *
+ * Shows an overlay ontop of an object. Toggleable.
+ * Requires a BCI shell.
+ */
+
+#define OBJECT_OVERLAY_LIMIT 10
+
+/obj/item/circuit_component/object_overlay
+ display_name = "Object Overlay"
+ desc = "Requires a BCI shell. A component that shows an overlay on top of an object."
+
+ required_shells = list(/obj/item/organ/cyberimp/bci)
+
+ /// Target atom
+ var/datum/port/input/target
+
+ var/datum/port/input/image_pixel_x
+ var/datum/port/input/image_pixel_y
+
+ /// On/Off signals
+ var/datum/port/input/signal_on
+ var/datum/port/input/signal_off
+
+ var/obj/item/organ/cyberimp/bci/bci
+ var/list/active_overlays = list()
+ var/list/options_map
+
+/obj/item/circuit_component/object_overlay/Initialize()
+ . = ..()
+ target = add_input_port("Target", PORT_TYPE_ATOM)
+
+ signal_on = add_input_port("Create Overlay", PORT_TYPE_SIGNAL)
+ signal_off = add_input_port("Remove Overlay", PORT_TYPE_SIGNAL)
+
+ image_pixel_x = add_input_port("X-Axis Shift", PORT_TYPE_NUMBER)
+ image_pixel_y = add_input_port("Y-Axis Shift", PORT_TYPE_NUMBER)
+
+/obj/item/circuit_component/object_overlay/Destroy()
+ for(var/active_overlay in active_overlays)
+ QDEL_NULL(active_overlay)
+ return ..()
+
+/obj/item/circuit_component/object_overlay/populate_options()
+ var/static/component_options = list(
+ "Corners (Blue)",
+ "Corners (Red)",
+ "Circle (Blue)",
+ "Circle (Red)",
+ "Small Corners (Blue)",
+ "Small Corners (Red)",
+ "Triangle (Blue)",
+ "Triangle (Red)",
+ "HUD mark (Blue)",
+ "HUD mark (Red)"
+ )
+ options = component_options
+
+ var/static/options_to_icons = list(
+ "Corners (Blue)" = "hud_corners",
+ "Corners (Red)" = "hud_corners_red",
+ "Circle (Blue)" = "hud_circle",
+ "Circle (Red)" = "hud_circle_red",
+ "Small Corners (Blue)" = "hud_corners_small",
+ "Small Corners (Red)" = "hud_corners_small_red",
+ "Triangle (Blue)" = "hud_triangle",
+ "Triangle (Red)" = "hud_triangle_red",
+ "HUD mark (Blue)" = "hud_mark",
+ "HUD mark (Red)" = "hud_mark_red"
+ )
+ options_map = options_to_icons
+
+/obj/item/circuit_component/object_overlay/register_shell(atom/movable/shell)
+ bci = shell
+ RegisterSignal(shell, COMSIG_ORGAN_REMOVED, .proc/on_organ_removed)
+
+/obj/item/circuit_component/object_overlay/unregister_shell(atom/movable/shell)
+ bci = null
+ UnregisterSignal(shell, COMSIG_ORGAN_REMOVED)
+
+/obj/item/circuit_component/object_overlay/input_received(datum/port/input/port)
+ . = ..()
+
+ if(. || !bci)
+ return
+
+ var/mob/living/owner = bci.owner
+ var/atom/target_atom = target.input_value
+
+ if(!owner || !istype(owner) || !owner.client || !target_atom)
+ return
+
+ if(COMPONENT_TRIGGERED_BY(signal_on, port))
+ show_to_owner(target_atom, owner)
+
+ if(COMPONENT_TRIGGERED_BY(signal_off, port) && (target_atom in active_overlays))
+ QDEL_NULL(active_overlays[target_atom])
+ active_overlays.Remove(target_atom)
+
+/obj/item/circuit_component/object_overlay/proc/show_to_owner(atom/target_atom, mob/living/owner)
+ if(LAZYLEN(active_overlays) >= OBJECT_OVERLAY_LIMIT)
+ return
+
+ if(active_overlays[target_atom])
+ QDEL_NULL(active_overlays[target_atom])
+
+ var/image/cool_overlay = image(icon = 'icons/hud/screen_bci.dmi', loc = target_atom, icon_state = options_map[current_option], layer = RIPPLE_LAYER)
+
+ if(image_pixel_x.input_value)
+ cool_overlay.pixel_x = image_pixel_x.input_value
+
+ if(image_pixel_y.input_value)
+ cool_overlay.pixel_y = image_pixel_y.input_value
+
+ var/alt_appearance = WEAKREF(target_atom.add_alt_appearance(
+ /datum/atom_hud/alternate_appearance/basic/one_person,
+ "object_overlay_[REF(src)]",
+ cool_overlay,
+ owner,
+ ))
+
+ active_overlays[target_atom] = alt_appearance
+
+/obj/item/circuit_component/object_overlay/proc/on_organ_removed(datum/source, mob/living/carbon/owner)
+ SIGNAL_HANDLER
+
+ for(var/atom/target_atom in active_overlays)
+ QDEL_NULL(active_overlays[target_atom])
+ active_overlays.Remove(target_atom)
+
+#undef OBJECT_OVERLAY_LIMIT
diff --git a/code/modules/wiremod/components/hud/target_intercept.dm b/code/modules/wiremod/components/hud/target_intercept.dm
new file mode 100644
index 00000000000..5de5695da33
--- /dev/null
+++ b/code/modules/wiremod/components/hud/target_intercept.dm
@@ -0,0 +1,64 @@
+/**
+ * # Target Intercept Component
+ *
+ * When activated intercepts next click and outputs clicked atom.
+ * Requires a BCI shell.
+ */
+
+/obj/item/circuit_component/target_intercept
+ display_name = "Target Intercept"
+ desc = "Requires a BCI shell. When activated, this component will allow user to target an object using their brain and will output the reference to said object."
+
+ required_shells = list(/obj/item/organ/cyberimp/bci)
+
+ var/datum/port/output/clicked_atom
+
+ var/obj/item/organ/cyberimp/bci/bci
+ var/intercept_cooldown = 1 SECONDS
+
+/obj/item/circuit_component/target_intercept/Initialize()
+ . = ..()
+ trigger_input = add_input_port("Activate", PORT_TYPE_SIGNAL)
+ trigger_output = add_output_port("Triggered", PORT_TYPE_SIGNAL)
+ clicked_atom = add_output_port("Targeted Object", PORT_TYPE_ATOM)
+
+/obj/item/circuit_component/target_intercept/register_shell(atom/movable/shell)
+ bci = shell
+ RegisterSignal(shell, COMSIG_ORGAN_REMOVED, .proc/on_organ_removed)
+
+/obj/item/circuit_component/target_intercept/unregister_shell(atom/movable/shell)
+ bci = null
+ UnregisterSignal(shell, COMSIG_ORGAN_REMOVED)
+
+/obj/item/circuit_component/target_intercept/input_received(datum/port/input/port)
+ . = ..()
+
+ if(. || !bci)
+ return
+
+ var/mob/living/owner = bci.owner
+ if(!owner || !istype(owner) || !owner.client)
+ return
+
+ if(TIMER_COOLDOWN_CHECK(parent, COOLDOWN_CIRCUIT_TARGET_INTERCEPT))
+ return
+
+ to_chat(owner, "Left-click to trigger target interceptor!")
+ owner.client.click_intercept = src
+
+/obj/item/circuit_component/target_intercept/proc/on_organ_removed(datum/source, mob/living/carbon/owner)
+ SIGNAL_HANDLER
+
+ if(owner.client && owner.client.click_intercept == src)
+ owner.client.click_intercept = null
+
+/obj/item/circuit_component/target_intercept/proc/InterceptClickOn(mob/user, params, atom/object)
+ user.client.click_intercept = null
+ clicked_atom.set_output(object)
+ trigger_output.set_output(COMPONENT_SIGNAL)
+ TIMER_COOLDOWN_START(parent, COOLDOWN_CIRCUIT_TARGET_INTERCEPT, intercept_cooldown)
+
+/obj/item/circuit_component/target_intercept/get_ui_notices()
+ . = ..()
+ . += create_ui_notice("Target Interception Cooldown: [DisplayTimeText(intercept_cooldown)]", "orange", "stopwatch")
+ . += create_ui_notice("Only usable in BCI circuits", "orange", "info")
diff --git a/code/modules/wiremod/shell/brain_computer_interface.dm b/code/modules/wiremod/shell/brain_computer_interface.dm
index e05720a420f..d43c402efb7 100644
--- a/code/modules/wiremod/shell/brain_computer_interface.dm
+++ b/code/modules/wiremod/shell/brain_computer_interface.dm
@@ -11,9 +11,9 @@
AddComponent(/datum/component/shell, list(
new /obj/item/circuit_component/bci_core,
- new /obj/item/circuit_component/bci_action("One"),
- new /obj/item/circuit_component/bci_action("Two"),
- new /obj/item/circuit_component/bci_action("Three"),
+ new /obj/item/circuit_component/bci_action(null, "One"),
+ new /obj/item/circuit_component/bci_action(null, "Two"),
+ new /obj/item/circuit_component/bci_action(null, "Three"),
), SHELL_CAPACITY_SMALL)
/obj/item/organ/cyberimp/bci/Insert(mob/living/carbon/reciever, special, drop_if_replaced)
@@ -33,13 +33,10 @@
else
return ..()
-/obj/item/circuit_component/bci
- display_name = "Brain-Computer Interface"
- desc = "Used to receive inputs for the brain-computer interface. User is presented with three buttons."
-
/obj/item/circuit_component/bci_action
display_name = "BCI Action"
desc = "Represents an action the user can take when implanted with the brain-computer interface."
+ required_shells = list(/obj/item/organ/cyberimp/bci)
/// The name to use for the button
var/datum/port/input/button_name
@@ -394,12 +391,15 @@
if (. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
return .
+ if(!user.Adjacent(src))
+ return
+
if (locked)
balloon_alert(user, "it's locked!")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
var/obj/item/organ/cyberimp/bci/bci_to_implant_resolved = bci_to_implant?.resolve()
- if (isnull(bci_to_implant_resolved) && user.Adjacent(src))
+ if (isnull(bci_to_implant_resolved))
balloon_alert(user, "no bci inserted!")
else
user.put_in_hands(bci_to_implant_resolved)
@@ -524,7 +524,7 @@
if (!isnull(message))
if (COOLDOWN_FINISHED(src, message_cooldown))
COOLDOWN_START(src, message_cooldown, 5 SECONDS)
- balloon_alert(user, "it won't budge!")
+ balloon_alert(user, message)
return
diff --git a/icons/hud/screen_bci.dmi b/icons/hud/screen_bci.dmi
new file mode 100644
index 0000000000000000000000000000000000000000..2458e03f44ff268f5c409fed4bc97728cd29e71e
GIT binary patch
literal 1665
zcmV-{27dX8P)%0000UP)t-sz`(#;
zm%KrJqQ(&sp%M}R|NrkkB?~4#7DZ#gz`&Bi;WGdL00DGTPE!Ct=GbNc00BRGR9JLG
zWpiV4X>fFDZ*Bkpc$}ryOKyWO5C-5iJ_Sm)6|ynUMK@KVDvRa{$cdd`m4`Gqt=iMq
z&@QS9lo`6S(KlxJZ2{ZxY5#mUhP#&odowWDz6&42B;qW1x7yWyB4ZZR#$IdpJsF&G
z#t%33O;_h0I$yS7fwnOCtAYg=P^}rqzEGP+eTnm5^_owZl
zRZa8n1=e=qz^d-L^!#ETFW>A>L95^u4NM&hdezGT`0cnlPNR7__84}cB@Xy?Z_zbv~Q
z&Gz*U+J)I?SXJE)vF$VNG-ZEeLk%co+60fvnd^5k0ko>2z%th`U7)j-C~FxehU=B
zIdg0=#EvjSH}kKG3vD}AK!pb#Aht6FGz|cZqQWyglEEmtKkpwNc|dpWxsLt-oYL}m++
zKr6Tfcu0~f>LU7#@Q@I|PXKT|37vy7~cGWB#RNJOTLNtt@O6eJ=O#-tTMpmI6Ch{#PcMMWx?^NWhy
z1XI*ujZ3rZQ5HKa3^7`+5?Mztx5QnD?9B4AxEpy~+?B}AEH4iL7$6fsKF?nP$me<2
zM{@}{22GRU6}Kuu{O17b;qwZhX#ri(oMBOLcEPZHe!L5Y^GpDv#RpN10UxvpRz2Sa
zfKM7V27Hn%I0mi(WE{&eka4@%7&r&e^2U=q@!bGMCL%qR8>zcbwy9VxZFmEFzn*dCJg@Fj5A7Ex60vHc4F%SXR16TqP!1Mqkfe2uJ
zfF2+MSRO#L34j0yz&t@;OwJAPO(b7QO=SZOThEJ^g5A4pVgJJRPP1!l@01NY7*xSH
z2bfU=KmY_l00ck)1V8`;KmY_V`n4JX%ns1^TJ6UHIMUSX{n76S`05!ipLd#Fzqfaq
zU1NKv*)_7=1CU505{X12kw_#GiR4^k*YkgbCIjHBXRv&(vAxr5Zq~nl4uG9}_>V>|
zzLXNff3+%>@82)Czyrpwc+9WZ!A(PvCG00000
LNkvXXu0mjfC