From f1a1df01a7e75c68cc8928978b2c175d0039199e Mon Sep 17 00:00:00 2001 From: dwasint <82520990+dwasint@users.noreply.github.com> Date: Tue, 20 Feb 2024 13:14:01 -0500 Subject: [PATCH 01/81] base cybernetics --- code/__DEFINES/DNA.dm | 6 + code/__DEFINES/atom_hud.dm | 3 + code/__DEFINES/dcs/signals/signals_object.dm | 4 +- code/__DEFINES/hud.dm | 10 +- code/__DEFINES/~monkestation/cybernetics.dm | 39 ++ .../~monkestation/dcs/signals/signals_atom.dm | 3 + .../dcs/signals/signals_carbon.dm | 5 + code/__DEFINES/~monkestation/skills.dm | 1 + code/__DEFINES/~monkestation/traits.dm | 5 + code/_globalvars/traits.dm | 3 +- code/_onclick/hud/hud.dm | 4 + code/datums/elements/climbable.dm | 7 +- code/datums/hud.dm | 1 + code/datums/station_traits/positive_traits.dm | 6 +- code/game/data_huds.dm | 24 +- code/game/objects/items.dm | 4 + code/game/objects/items/choice_beacon.dm | 4 +- code/modules/assembly/flash.dm | 4 +- .../mob/living/carbon/carbon_defense.dm | 14 +- .../mob/living/carbon/carbon_defines.dm | 2 +- .../mob/living/carbon/human/human_defines.dm | 2 +- code/modules/power/cell.dm | 2 + code/modules/religion/rites.dm | 2 +- .../research/designs/medical_designs.dm | 4 +- code/modules/surgery/organ_manipulation.dm | 6 +- code/modules/surgery/organs/augments_arms.dm | 55 +-- .../surgery/organs/augments_internal.dm | 1 - code/modules/surgery/organs/autosurgeon.dm | 4 +- .../cybernetics/actions/base_action.dm | 3 + .../code/modules/cybernetics/assets.dm | 18 + .../cybernetics/augments/_base_changes.dm | 160 ++++++++ .../cybernetics/augments/arm_augments.dm | 345 ++++++++++++++++++ .../cybernetics/augments/leg_augments.dm | 215 +++++++++++ .../modules/cybernetics/designs/medical.dm | 25 ++ .../modules/cybernetics/hud/cybernetics.dm | 7 + .../code/modules/cybernetics/icons/96x96.dmi | Bin 0 -> 68749 bytes .../icons/UI_Icons/tgui/lines/0.png | Bin 0 -> 729 bytes .../icons/UI_Icons/tgui/lines/10.png | Bin 0 -> 764 bytes .../icons/UI_Icons/tgui/lines/10_f.png | Bin 0 -> 7802 bytes .../icons/UI_Icons/tgui/lines/12.png | Bin 0 -> 735 bytes .../icons/UI_Icons/tgui/lines/12_f.png | Bin 0 -> 7612 bytes .../icons/UI_Icons/tgui/lines/3.png | Bin 0 -> 579 bytes .../icons/UI_Icons/tgui/lines/3_f.png | Bin 0 -> 7235 bytes .../icons/UI_Icons/tgui/lines/5.png | Bin 0 -> 772 bytes .../icons/UI_Icons/tgui/lines/5_f.png | Bin 0 -> 7823 bytes .../icons/UI_Icons/tgui/lines/6.png | Bin 0 -> 766 bytes .../icons/UI_Icons/tgui/lines/6_f.png | Bin 0 -> 7842 bytes .../icons/UI_Icons/tgui/lines/9.png | Bin 0 -> 762 bytes .../icons/UI_Icons/tgui/lines/9_f.png | Bin 0 -> 7835 bytes .../icons/UI_Icons/tgui/lines/e.png | Bin 0 -> 11257 bytes .../icons/UI_Icons/tgui/lines/s.png | Bin 0 -> 5244 bytes .../code/modules/cybernetics/icons/cloaks.dmi | Bin 0 -> 30724 bytes .../cybernetics/icons/screen_cybernetics.dmi | Bin 0 -> 294 bytes .../modules/cybernetics/icons/surgery.dmi | Bin 0 -> 42552 bytes .../cybernetics/icons/swords_lefthand.dmi | Bin 0 -> 24854 bytes .../cybernetics/icons/swords_righthand.dmi | Bin 0 -> 28260 bytes .../cybernetics/implant_items/autosurgeons.dm | 124 +++++++ .../implant_items/cyberlink_connector.dm | 161 ++++++++ .../implant_items/weapons/mantis_blade.dm | 56 +++ .../cybernetics/minigame/hacking_minigame.dm | 276 ++++++++++++++ .../cybernetics/premade_storages/antags.dm | 8 + .../code/modules/cybernetics/readme.md | 35 ++ .../cybernetics/skills/implant_hacking.dm | 19 + .../code/modules/cybernetics/tech_nodes.dm | 17 + .../modules/surgery/organs/internal/butts.dm | 2 +- tgstation.dme | 15 + tgui/packages/tgui/interfaces/Hacking.js | 54 +++ 67 files changed, 1691 insertions(+), 74 deletions(-) create mode 100644 code/__DEFINES/~monkestation/cybernetics.dm create mode 100644 code/__DEFINES/~monkestation/skills.dm create mode 100644 monkestation/code/modules/cybernetics/actions/base_action.dm create mode 100644 monkestation/code/modules/cybernetics/assets.dm create mode 100644 monkestation/code/modules/cybernetics/augments/_base_changes.dm create mode 100644 monkestation/code/modules/cybernetics/augments/arm_augments.dm create mode 100644 monkestation/code/modules/cybernetics/augments/leg_augments.dm create mode 100644 monkestation/code/modules/cybernetics/designs/medical.dm create mode 100644 monkestation/code/modules/cybernetics/hud/cybernetics.dm create mode 100644 monkestation/code/modules/cybernetics/icons/96x96.dmi create mode 100644 monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/0.png create mode 100644 monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/10.png create mode 100644 monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/10_f.png create mode 100644 monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/12.png create mode 100644 monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/12_f.png create mode 100644 monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/3.png create mode 100644 monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/3_f.png create mode 100644 monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/5.png create mode 100644 monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/5_f.png create mode 100644 monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/6.png create mode 100644 monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/6_f.png create mode 100644 monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/9.png create mode 100644 monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/9_f.png create mode 100644 monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/e.png create mode 100644 monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/s.png create mode 100644 monkestation/code/modules/cybernetics/icons/cloaks.dmi create mode 100644 monkestation/code/modules/cybernetics/icons/screen_cybernetics.dmi create mode 100644 monkestation/code/modules/cybernetics/icons/surgery.dmi create mode 100644 monkestation/code/modules/cybernetics/icons/swords_lefthand.dmi create mode 100644 monkestation/code/modules/cybernetics/icons/swords_righthand.dmi create mode 100644 monkestation/code/modules/cybernetics/implant_items/autosurgeons.dm create mode 100644 monkestation/code/modules/cybernetics/implant_items/cyberlink_connector.dm create mode 100644 monkestation/code/modules/cybernetics/implant_items/weapons/mantis_blade.dm create mode 100644 monkestation/code/modules/cybernetics/minigame/hacking_minigame.dm create mode 100644 monkestation/code/modules/cybernetics/premade_storages/antags.dm create mode 100644 monkestation/code/modules/cybernetics/readme.md create mode 100644 monkestation/code/modules/cybernetics/skills/implant_hacking.dm create mode 100644 monkestation/code/modules/cybernetics/tech_nodes.dm create mode 100644 tgui/packages/tgui/interfaces/Hacking.js diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm index 759ff992d075..a593396cf802 100644 --- a/code/__DEFINES/DNA.dm +++ b/code/__DEFINES/DNA.dm @@ -126,6 +126,9 @@ #define ORGAN_SLOT_ZOMBIE "zombie_infection" #define ORGAN_SLOT_BUTT "butt" #define ORGAN_SLOT_BLADDER "bladder" +#define ORGAN_SLOT_LINK "cyber_link" +#define ORGAN_SLOT_RIGHT_LEG_AUG "r_leg_device" +#define ORGAN_SLOT_LEFT_LEG_AUG "l_leg_device" /// Organ slot external #define ORGAN_SLOT_EXTERNAL_TAIL "tail" @@ -170,9 +173,12 @@ /// Exists so Life()'s organ process order is consistent GLOBAL_LIST_INIT(organ_process_order, list( ORGAN_SLOT_BRAIN, + ORGAN_SLOT_LINK, ORGAN_SLOT_APPENDIX, ORGAN_SLOT_RIGHT_ARM_AUG, ORGAN_SLOT_LEFT_ARM_AUG, + ORGAN_SLOT_RIGHT_LEG_AUG, + ORGAN_SLOT_LEFT_LEG_AUG, ORGAN_SLOT_STOMACH, ORGAN_SLOT_STOMACH_AID, ORGAN_SLOT_BREATHING_TUBE, diff --git a/code/__DEFINES/atom_hud.dm b/code/__DEFINES/atom_hud.dm index d76512095766..9d591178c574 100644 --- a/code/__DEFINES/atom_hud.dm +++ b/code/__DEFINES/atom_hud.dm @@ -51,6 +51,8 @@ #define DIAG_NANITE_FULL_HUD "24" // for gun permit #define PERMIT_HUD "25" +// for implants to let you see sensor field +#define SENSOR_HUD "26" //monkestation edit end //by default everything in the hud_list of an atom is an image @@ -70,6 +72,7 @@ #define DATA_HUD_AI_DETECT 9 #define DATA_HUD_FAN 10 #define DATA_HUD_PERMIT 11 //monkestation edit +#define DATA_HUD_SENSORS 12 //monkestation edit // Notification action types #define NOTIFY_JUMP "jump" diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index 08d80b05ad6e..eabb62c9b1c4 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -321,10 +321,10 @@ //called in /obj/item/tank/jetpack/proc/turn_off() : () #define COMSIG_JETPACK_DEACTIVATED "jetpack_deactivated" -//called in /obj/item/organ/cyberimp/chest/thrusters/proc/toggle() : () +//called in /obj/item/organ/internal/cyberimp/chest/thrusters/proc/toggle() : () #define COMSIG_THRUSTER_ACTIVATED "jetmodule_activated" #define THRUSTER_ACTIVATION_FAILED (1<<0) -//called in /obj/item/organ/cyberimp/chest/thrusters/proc/toggle() : () +//called in /obj/item/organ/internal/cyberimp/chest/thrusters/proc/toggle() : () #define COMSIG_THRUSTER_DEACTIVATED "jetmodule_deactivated" // /obj/item/camera signals diff --git a/code/__DEFINES/hud.dm b/code/__DEFINES/hud.dm index 74ab51f6b384..73f2b7ee3c00 100644 --- a/code/__DEFINES/hud.dm +++ b/code/__DEFINES/hud.dm @@ -34,10 +34,12 @@ Therefore, the top right corner (except during admin shenanigans) is at "15,15" */ -/proc/ui_hand_position(i) //values based on old hand ui positions (CENTER:-/+16,SOUTH:5) - var/x_off = i % 2 ? 0 : -1 - var/y_off = round((i-1) / 2) - return"CENTER+[x_off]:16,SOUTH+[y_off]:5" +//Monkestation EDIT: START - CYBERNETICS +/proc/ui_hand_position(i,y_offset = 0,y_pixel_offset = 0) //values based on old hand ui positions (CENTER:-/+16,SOUTH:5) + var/x_off = -(!(i % 2)) + var/y_off = round((i-1) / 2) + y_offset + return"CENTER+[x_off]:16,SOUTH+[y_off]:[5 + y_pixel_offset]" +//Monkestation EDIT: END - CYBERNETICS /proc/ui_equip_position(mob/M) var/y_off = round((M.held_items.len-1) / 2) //values based on old equip ui position (CENTER: +/-16,SOUTH+1:5) diff --git a/code/__DEFINES/~monkestation/cybernetics.dm b/code/__DEFINES/~monkestation/cybernetics.dm new file mode 100644 index 000000000000..c681fdcd85d1 --- /dev/null +++ b/code/__DEFINES/~monkestation/cybernetics.dm @@ -0,0 +1,39 @@ +/// Cybernetics defines + +#define SECURITY_PROTOCOL "sec_protocol" +#define ENCODE_PROTOCOL "enc_protocol" +#define OPERATING_PROTOCOL "op_protocol" + +#define NO_PROTOCOL 0 + +/// Security protocols + +///Those names mean nothing don't try to decipher these. They are defines because some cybernetics will be incompatible with eachother. treat those defines like software names. + +#define SECURITY_NT1 "nt1" +#define SECURITY_NT2 "nt2" +#define SECURITY_NTX "ntx" +#define SECURITY_TMSP "tmsp" +#define SECURITY_TOSP "tosp" + + +/// Encode protocol + +#define ENCODE_ENC1 "enc1" +#define ENCODE_ENC2 "enc2" +#define ENCODE_TENN "tenn" +#define ENCODE_CSEP "csep" + + +/// Operating protocol + +#define OPERATING_NTOS "ntos" +#define OPERATING_TGMF "tgmf" +#define OPERATING_CSOF "csof" + +#define AUGMENT_NO_REQ list(SECURITY_PROTOCOL = NO_PROTOCOL, ENCODE_PROTOCOL = NO_PROTOCOL, OPERATING_PROTOCOL = NO_PROTOCOL) +#define AUGMENT_NT_LOWLEVEL list(SECURITY_PROTOCOL = list(SECURITY_NT1), ENCODE_PROTOCOL = list(ENCODE_ENC1), OPERATING_PROTOCOL = list(OPERATING_NTOS)) +#define AUGMENT_NT_HIGHLEVEL list(SECURITY_PROTOCOL = list(SECURITY_NT2 , SECURITY_NT1), ENCODE_PROTOCOL = list(ENCODE_ENC2), OPERATING_PROTOCOL = list(OPERATING_NTOS)) +#define AUGMENT_TG_LEVEL list(SECURITY_PROTOCOL = list(SECURITY_NTX , SECURITY_NT2 , SECURITY_TMSP), ENCODE_PROTOCOL = list(ENCODE_TENN), OPERATING_PROTOCOL = list(OPERATING_TGMF)) +#define AUGMENT_SYNDICATE_LEVEL list(SECURITY_PROTOCOL = list(SECURITY_TOSP), ENCODE_PROTOCOL = list(ENCODE_CSEP , ENCODE_TENN), OPERATING_PROTOCOL = list(OPERATING_CSOF)) +#define AUGMENT_ADMIN_LEVEL list(SECURITY_PROTOCOL = list(SECURITY_NTX , SECURITY_NT2 , SECURITY_NT1, SECURITY_TMSP, SECURITY_TOSP), ENCODE_PROTOCOL = list(ENCODE_ENC1, ENCODE_ENC2, ENCODE_CSEP, ENCODE_TENN), OPERATING_PROTOCOL = list(OPERATING_CSOF, OPERATING_TGMF, OPERATING_NTOS)) diff --git a/code/__DEFINES/~monkestation/dcs/signals/signals_atom.dm b/code/__DEFINES/~monkestation/dcs/signals/signals_atom.dm index 8dece5e3c63c..89b72655d4e6 100644 --- a/code/__DEFINES/~monkestation/dcs/signals/signals_atom.dm +++ b/code/__DEFINES/~monkestation/dcs/signals/signals_atom.dm @@ -9,3 +9,6 @@ /// from base of atom/eminence_act() : (mob/living/eminence/user) #define COMSIG_ATOM_EMINENCE_ACT "atom_eminence_act" + +///Called by either cell/proc/give or cell/proc/use +#define COMSIG_CELL_CHANGE_POWER "cell_change_power" diff --git a/code/__DEFINES/~monkestation/dcs/signals/signals_carbon.dm b/code/__DEFINES/~monkestation/dcs/signals/signals_carbon.dm index 191577527fa8..6b2854a6c830 100644 --- a/code/__DEFINES/~monkestation/dcs/signals/signals_carbon.dm +++ b/code/__DEFINES/~monkestation/dcs/signals/signals_carbon.dm @@ -1,2 +1,7 @@ #define COMSIG_CARBON_EQUIP_EARS "carbon_ears_equip" #define COMSIG_CARBON_UNEQUIP_EARS "carbon_ears_unequip" + +///Sent to carbon when they pickup something (/obj/item) +#define COMSIG_CARBON_ITEM_PICKED_UP "carbon_item_picked_up" +///Sent to carbon when they drop something (/obj/item) +#define COMSIG_CARBON_ITEM_DROPPED "carbon_item_dropped" diff --git a/code/__DEFINES/~monkestation/skills.dm b/code/__DEFINES/~monkestation/skills.dm new file mode 100644 index 000000000000..b9d91b51b5e7 --- /dev/null +++ b/code/__DEFINES/~monkestation/skills.dm @@ -0,0 +1 @@ +#define SKILL_TIME_MODIFIER "skill_time_modifier" //ideally added to timers and other do_afters() diff --git a/code/__DEFINES/~monkestation/traits.dm b/code/__DEFINES/~monkestation/traits.dm index 3818e1ae152c..ce8be0cd97e5 100644 --- a/code/__DEFINES/~monkestation/traits.dm +++ b/code/__DEFINES/~monkestation/traits.dm @@ -16,3 +16,8 @@ #define TRAIT_PERFECT_SURGEON "perfect_surgeon" /// Station trait for when the clown has bridge access *shudders* #define STATION_TRAIT_CLOWN_BRIDGE "clown_bridge" + +#define TRAIT_SENSOR_HUD "sensor_hud" +#define TRAIT_SHOVE_RESIST "shove_resist" //Used by implants +#define TRAIT_FAST_CLIMBER "fast_climber" //Used by implants +#define ANTI_DROP_IMPLANT_TRAIT "antidrop_implant" diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index ff32b66828d0..82cf68a44923 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -199,7 +199,8 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_OVERWATCH_IMMUNE" = TRAIT_OVERWATCH_IMMUNE, "TRAIT_UNDENSE" = TRAIT_UNDENSE, "TRAIT_EXPANDED_FOV" = TRAIT_EXPANDED_FOV, - "TRAIT_ANALGESIA" = TRAIT_ANALGESIA + "TRAIT_ANALGESIA" = TRAIT_ANALGESIA, + "TRAIT_FAST_CLIMBER" = TRAIT_FAST_CLIMBER ), /obj/item/bodypart = list( "TRAIT_PARALYSIS" = TRAIT_PARALYSIS, diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index 708678b35826..44cf0f0496f7 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -94,6 +94,9 @@ GLOBAL_LIST_INIT(available_ui_styles, list( var/atom/movable/screen/stamina var/atom/movable/screen/healthdoll var/atom/movable/screen/spacesuit + + var/list/atom/movable/screen/cybernetics/ammo_counter/cybernetics_ammo = list() //monkestation edit - CYBERNETICS + // subtypes can override this to force a specific UI style var/ui_style @@ -227,6 +230,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list( stamina = null healthdoll = null spacesuit = null + cybernetics_ammo = null //monkestation edit - CYBERNETICS blobpwrdisplay = null alien_plasma_display = null alien_queen_finder = null diff --git a/code/datums/elements/climbable.dm b/code/datums/elements/climbable.dm index 52a62fdb8fcb..b8e221619811 100644 --- a/code/datums/elements/climbable.dm +++ b/code/datums/elements/climbable.dm @@ -67,7 +67,10 @@ adjusted_climb_time *= 0.25 //aliens are terrifyingly fast if(HAS_TRAIT(user, TRAIT_FREERUNNING)) //do you have any idea how fast I am??? adjusted_climb_time *= 0.8 - adjusted_climb_stun *= 0.8 + //monkestation edit - CYBERNETICS + if(HAS_TRAIT(user,TRAIT_FAST_CLIMBER)) //How it feels to chew 5 gum + adjusted_climb_time *= 0.3 + //monkestation edit - CYBERNETICS LAZYADDASSOCLIST(current_climbers, climbed_thing, user) if(do_after(user, adjusted_climb_time, climbed_thing)) if(QDELETED(climbed_thing)) //Checking if structure has been destroyed @@ -147,7 +150,7 @@ if(bumpee.force_moving?.allow_climbing) do_climb(source, bumpee) if(bumpee.m_intent == MOVE_INTENT_SPRINT) - INVOKE_ASYNC(src, PROC_REF(attempt_sprint_climb), source, bumpee) + INVOKE_ASYNC(src, PROC_REF(climb_structure), source, bumpee) ///Tries to climb onto the target if the forced movement of the mob allows it /datum/element/climbable/proc/attempt_sprint_climb(datum/source, mob/bumpee) diff --git a/code/datums/hud.dm b/code/datums/hud.dm index 656bd57193f5..26ca77d8208b 100644 --- a/code/datums/hud.dm +++ b/code/datums/hud.dm @@ -19,6 +19,7 @@ GLOBAL_LIST_INIT(huds, list( DATA_HUD_AI_DETECT = new/datum/atom_hud/ai_detector(), DATA_HUD_FAN = new/datum/atom_hud/data/human/fan_hud(), DATA_HUD_PERMIT = new/datum/atom_hud/data/human/permit(), //monkestation edit + DATA_HUD_SENSORS = new/datum/atom_hud/data/human/medical/basic/sensors(), //monkestation edit - CYBERNETICS )) /datum/atom_hud diff --git a/code/datums/station_traits/positive_traits.dm b/code/datums/station_traits/positive_traits.dm index 966c20e12f37..7b5b7834ae9f 100644 --- a/code/datums/station_traits/positive_traits.dm +++ b/code/datums/station_traits/positive_traits.dm @@ -295,7 +295,7 @@ /datum/job/cook = /obj/item/organ/internal/cyberimp/chest/nutriment/plus, /datum/job/curator = /obj/item/organ/internal/eyes/robotic/glow, /datum/job/detective = /obj/item/organ/internal/lungs/cybernetic/tier3, - /datum/job/doctor = /obj/item/organ/internal/cyberimp/arm/surgery, + /datum/job/doctor = /obj/item/organ/internal/cyberimp/arm/item_set/surgery, /datum/job/geneticist = /obj/item/organ/internal/fly, //we don't care about implants, we have cancer. /datum/job/head_of_personnel = /obj/item/organ/internal/eyes/robotic, /datum/job/head_of_security = /obj/item/organ/internal/eyes/robotic/thermals, @@ -309,9 +309,9 @@ /datum/job/research_director = /obj/item/organ/internal/cyberimp/bci, /datum/job/roboticist = /obj/item/organ/internal/cyberimp/eyes/hud/diagnostic, /datum/job/scientist = /obj/item/organ/internal/ears/cybernetic, - /datum/job/security_officer = /obj/item/organ/internal/cyberimp/arm/flash, + /datum/job/security_officer = /obj/item/organ/internal/cyberimp/arm/item_set/flash, /datum/job/shaft_miner = /obj/item/organ/internal/monster_core/rush_gland, - /datum/job/station_engineer = /obj/item/organ/internal/cyberimp/arm/toolset, + /datum/job/station_engineer = /obj/item/organ/internal/cyberimp/arm/item_set/toolset, /datum/job/virologist = /obj/item/organ/internal/lungs/cybernetic/tier2, /datum/job/warden = /obj/item/organ/internal/cyberimp/eyes/hud/security, ) diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm index bec2556aa55a..28c37150c33d 100644 --- a/code/game/data_huds.dm +++ b/code/game/data_huds.dm @@ -20,7 +20,12 @@ /datum/atom_hud/data/human/medical hud_icons = list(STATUS_HUD, HEALTH_HUD, NANITE_HUD) -/datum/atom_hud/data/human/medical/basic +/datum/atom_hud/data/human/medical/basic/add_atom_to_single_mob_hud(mob/M, mob/living/carbon/H) + if(check_sensors(H)) + ..() + +/datum/atom_hud/data/human/medical/basic/proc/update_suit_sensors(mob/living/carbon/H) + check_sensors(H) ? add_atom_to_hud(H) : remove_atom_from_hud(H) /datum/atom_hud/data/human/medical/basic/proc/check_sensors(mob/living/carbon/human/H) if(!istype(H)) @@ -32,11 +37,12 @@ return FALSE return TRUE -/datum/atom_hud/data/human/medical/basic/add_atom_to_single_mob_hud(mob/M, mob/living/carbon/H) - if(check_sensors(H)) - ..() +/datum/atom_hud/data/human/medical/advanced -/datum/atom_hud/data/human/medical/basic/proc/update_suit_sensors(mob/living/carbon/H) +/datum/atom_hud/data/human/medical/basic/sensors + hud_icons = list(SENSOR_HUD) + +/datum/atom_hud/data/human/medical/basic/sensors/update_suit_sensors(mob/living/carbon/H) check_sensors(H) ? add_atom_to_hud(H) : remove_atom_from_hud(H) /datum/atom_hud/data/human/medical/advanced @@ -167,7 +173,9 @@ Medical HUD! Basic mode needs suit sensors on. //called when a human changes suit sensors /mob/living/carbon/proc/update_suit_sensors() var/datum/atom_hud/data/human/medical/basic/B = GLOB.huds[DATA_HUD_MEDICAL_BASIC] + var/datum/atom_hud/data/human/medical/basic/sensors/S = GLOB.huds[DATA_HUD_SENSORS] B.update_suit_sensors(src) + S.update_suit_sensors(src) //called when a living mob changes health /mob/living/proc/med_hud_set_health() @@ -186,14 +194,20 @@ Medical HUD! Basic mode needs suit sensors on. //called when a carbon changes stat, virus or XENO_HOST /mob/living/proc/med_hud_set_status() var/image/holder = hud_list?[STATUS_HUD] + var/image/sensors = hud_list?[SENSOR_HUD] if (isnull(holder)) return var/icon/I = icon(icon, icon_state, dir) holder.pixel_y = I.Height() - world.icon_size if(stat == DEAD || (HAS_TRAIT(src, TRAIT_FAKEDEATH))) + if(sensors) + SET_PLANE_EXPLICIT(sensors, ABOVE_LIGHTING_PLANE, src) + sensors.icon_state = "sensors" + holder.icon_state = "huddead" else + sensors?.icon_state = null holder.icon_state = "hudhealthy" /mob/living/carbon/med_hud_set_status() diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 02588d175c03..1840dacbd00d 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -687,6 +687,8 @@ qdel(src) item_flags &= ~IN_INVENTORY SEND_SIGNAL(src, COMSIG_ITEM_DROPPED, user) + if(user && iscarbon(user)) + SEND_SIGNAL(user, COMSIG_CARBON_ITEM_DROPPED, src) if(!silent) playsound(src, drop_sound, DROP_SOUND_VOLUME, ignore_walls = FALSE) user?.update_equipment_speed_mods() @@ -696,6 +698,8 @@ SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(src, COMSIG_ITEM_PICKUP, user) SEND_SIGNAL(user, COMSIG_LIVING_PICKED_UP_ITEM, src) + if(iscarbon(user)) + SEND_SIGNAL(user, COMSIG_CARBON_ITEM_PICKED_UP, src) item_flags |= IN_INVENTORY /// called when "found" in pockets and storage items. Returns 1 if the search should end. diff --git a/code/game/objects/items/choice_beacon.dm b/code/game/objects/items/choice_beacon.dm index b4bc8154e5c7..cf57ada536bc 100644 --- a/code/game/objects/items/choice_beacon.dm +++ b/code/game/objects/items/choice_beacon.dm @@ -111,8 +111,8 @@ // cyberimplants range from a nice bonus to fucking broken bullshit so no subtypesof var/list/selectable_types = list( /obj/item/organ/internal/cyberimp/brain/anti_drop, - /obj/item/organ/internal/cyberimp/arm/toolset, - /obj/item/organ/internal/cyberimp/arm/surgery, + /obj/item/organ/internal/cyberimp/arm/item_set/toolset, + /obj/item/organ/internal/cyberimp/arm/item_set/surgery, /obj/item/organ/internal/cyberimp/chest/thrusters, /obj/item/organ/internal/lungs/cybernetic/tier3, /obj/item/organ/internal/liver/cybernetic/tier3, diff --git a/code/modules/assembly/flash.dm b/code/modules/assembly/flash.dm index 758f95804e20..5707db9e05f0 100644 --- a/code/modules/assembly/flash.dm +++ b/code/modules/assembly/flash.dm @@ -311,7 +311,7 @@ var/datum/weakref/arm /obj/item/assembly/flash/armimplant/burn_out() - var/obj/item/organ/internal/cyberimp/arm/flash/real_arm = arm.resolve() + var/obj/item/organ/internal/cyberimp/arm/item_set/flash/real_arm = arm.resolve() if(real_arm?.owner) to_chat(real_arm.owner, span_warning("Your photon projector implant overheats and deactivates!")) real_arm.Retract() @@ -320,7 +320,7 @@ /obj/item/assembly/flash/armimplant/try_use_flash(mob/user = null) if(overheat) - var/obj/item/organ/internal/cyberimp/arm/flash/real_arm = arm.resolve() + var/obj/item/organ/internal/cyberimp/arm/item_set/flash/real_arm = arm.resolve() if(real_arm?.owner) to_chat(real_arm.owner, span_warning("Your photon projector is running too hot to be used again so quickly!")) return FALSE diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 523c269e4346..2c9768fc6263 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -179,7 +179,7 @@ if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_HAND, user, modifiers) & COMPONENT_CANCEL_ATTACK_CHAIN) . = TRUE - + if(length(diseases) && isliving(user)) var/mob/living/living = user var/block = living.check_contact_sterility(BODY_ZONE_EVERYTHING) @@ -196,7 +196,7 @@ if(length(contact) && !block) for(var/datum/disease/advanced/V as anything in contact) infect_disease(V, notes="(Skin Contact - (Bump), coming from [living])") - + for(var/datum/surgery/operations as anything in surgeries) if((user.istate & ISTATE_HARM)) @@ -226,7 +226,7 @@ var/datum/disease/D = thing if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN) ContactContractDisease(D) - */ + */ if(!(user.istate & ISTATE_HARM)) help_shake_act(user) return FALSE @@ -299,7 +299,15 @@ var/shove_dir = get_dir(loc, target.loc) var/turf/target_shove_turf = get_step(target.loc, shove_dir) var/shove_blocked = FALSE //Used to check if a shove is blocked so that if it is knockdown logic can be applied + var/turf/target_old_turf = target.loc + if(HAS_TRAIT(target,TRAIT_SHOVE_RESIST)) + log_combat(src, target, "shoved") + target.stamina.adjust(-7) + target.visible_message("[name] tries to shove [target.name]", + "You're nearly knocked down by [name]!", "You hear aggressive shuffling!", COMBAT_MESSAGE_RANGE, src) + return + //Are we hitting anything? or if(SEND_SIGNAL(target_shove_turf, COMSIG_CARBON_DISARM_PRESHOVE) & COMSIG_CARBON_ACT_SOLID) diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm index 84dc1029808b..006df1f042f6 100644 --- a/code/modules/mob/living/carbon/carbon_defines.dm +++ b/code/modules/mob/living/carbon/carbon_defines.dm @@ -2,7 +2,7 @@ blood_volume = BLOOD_VOLUME_NORMAL gender = MALE pressure_resistance = 15 - hud_possible = list(HEALTH_HUD,STATUS_HUD,ANTAG_HUD,GLAND_HUD,NANITE_HUD,DIAG_NANITE_FULL_HUD) + hud_possible = list(HEALTH_HUD,STATUS_HUD,ANTAG_HUD,GLAND_HUD,NANITE_HUD,DIAG_NANITE_FULL_HUD,SENSOR_HUD) has_limbs = TRUE held_items = list(null, null) num_legs = 0 //Populated on init through list/bodyparts diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 80fd5ade9649..fcaa78e87b74 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -5,7 +5,7 @@ icon = 'icons/mob/species/human/human.dmi' icon_state = "human_basic" appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE|LONG_GLIDE - hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPLOYAL_HUD,IMPCHEM_HUD,IMPTRACK_HUD,ANTAG_HUD,GLAND_HUD,SENTIENT_DISEASE_HUD,FAN_HUD,NANITE_HUD,DIAG_NANITE_FULL_HUD,PERMIT_HUD) + hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPLOYAL_HUD,IMPCHEM_HUD,IMPTRACK_HUD,ANTAG_HUD,GLAND_HUD,SENTIENT_DISEASE_HUD,FAN_HUD,NANITE_HUD,DIAG_NANITE_FULL_HUD,PERMIT_HUD,SENSOR_HUD) hud_type = /datum/hud/human pressure_resistance = 25 can_buckle = TRUE diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index 66d6366cd4c0..4d08b6cb8cf2 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -145,6 +145,7 @@ if(!force && charge < amount) return FALSE charge = max(charge - amount, 0) + SEND_SIGNAL(src,COMSIG_CELL_CHANGE_POWER) if(!istype(loc, /obj/machinery/power/apc)) SSblackbox.record_feedback("tally", "cell_used", 1, type) return TRUE @@ -158,6 +159,7 @@ amount = maxcharge var/power_used = min(maxcharge-charge,amount) charge += power_used + SEND_SIGNAL(src,COMSIG_CELL_CHANGE_POWER) return power_used /obj/item/stock_parts/cell/examine(mob/user) diff --git a/code/modules/religion/rites.dm b/code/modules/religion/rites.dm index 05f3314df6fc..009182a23249 100644 --- a/code/modules/religion/rites.dm +++ b/code/modules/religion/rites.dm @@ -129,7 +129,7 @@ ..() var/altar_turf = get_turf(religious_tool) var/blessing = pick( - /obj/item/organ/internal/cyberimp/arm/surgery, + /obj/item/organ/internal/cyberimp/arm/item_set/surgery, /obj/item/organ/internal/cyberimp/eyes/hud/diagnostic, /obj/item/organ/internal/cyberimp/eyes/hud/medical, /obj/item/organ/internal/cyberimp/mouth/breathing_tube, diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index 5290c8603602..de0ff047415f 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -398,7 +398,7 @@ build_type = PROTOLATHE | AWAY_LATHE | MECHFAB materials = list (/datum/material/iron = 2500, /datum/material/glass =HALF_SHEET_MATERIAL_AMOUNT * 1.5, /datum/material/silver =HALF_SHEET_MATERIAL_AMOUNT * 1.5) construction_time =SMALL_MATERIAL_AMOUNT * 2 - build_path = /obj/item/organ/internal/cyberimp/arm/surgery + build_path = /obj/item/organ/internal/cyberimp/arm/item_set/surgery category = list( RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_IMPLANTS_UTILITY ) @@ -411,7 +411,7 @@ build_type = PROTOLATHE | AWAY_LATHE | MECHFAB materials = list (/datum/material/iron = 2500, /datum/material/glass =HALF_SHEET_MATERIAL_AMOUNT * 1.5, /datum/material/silver =HALF_SHEET_MATERIAL_AMOUNT * 1.5) construction_time =SMALL_MATERIAL_AMOUNT * 2 - build_path = /obj/item/organ/internal/cyberimp/arm/toolset + build_path = /obj/item/organ/internal/cyberimp/arm/item_set/toolset category = list( RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_IMPLANTS_UTILITY ) diff --git a/code/modules/surgery/organ_manipulation.dm b/code/modules/surgery/organ_manipulation.dm index 28635805030c..e1058e99865b 100644 --- a/code/modules/surgery/organ_manipulation.dm +++ b/code/modules/surgery/organ_manipulation.dm @@ -13,7 +13,7 @@ ) /datum/surgery/organ_manipulation/soft - possible_locs = list(BODY_ZONE_PRECISE_GROIN, BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM) + possible_locs = list(BODY_ZONE_PRECISE_GROIN, BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) steps = list( /datum/surgery_step/incise, /datum/surgery_step/retract_skin, @@ -25,7 +25,7 @@ /datum/surgery/organ_manipulation/alien name = "Alien organ manipulation" - possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_PRECISE_GROIN, BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM) + possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_PRECISE_GROIN, BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) target_mobtypes = list(/mob/living/carbon/alien/adult) steps = list( /datum/surgery_step/saw, @@ -82,6 +82,8 @@ BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, + BODY_ZONE_L_LEG, + BODY_ZONE_R_LEG, ) steps = list( /datum/surgery_step/mechanic_open, diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm index 05dd6818e3e5..d797bc0e4e2d 100644 --- a/code/modules/surgery/organs/augments_arms.dm +++ b/code/modules/surgery/organs/augments_arms.dm @@ -20,14 +20,6 @@ /obj/item/organ/internal/cyberimp/arm/Initialize(mapload) . = ..() - if(ispath(active_item)) - active_item = new active_item(src) - items_list += WEAKREF(active_item) - - for(var/typepath in items_to_create) - var/atom/new_item = new typepath(src) - items_list += WEAKREF(new_item) - update_appearance() SetSlotFromZone() @@ -217,40 +209,25 @@ Retract() -/obj/item/organ/internal/cyberimp/arm/gun/emp_act(severity) - . = ..() - if(. & EMP_PROTECT_SELF) - return - if(prob(30/severity) && owner && !(organ_flags & ORGAN_FAILING)) - Retract() - owner.visible_message(span_danger("A loud bang comes from [owner]\'s [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm!")) - playsound(get_turf(owner), 'sound/weapons/flashbang.ogg', 100, TRUE) - to_chat(owner, span_userdanger("You feel an explosion erupt inside your [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm as your implant breaks!")) - owner.adjust_fire_stacks(20) - owner.ignite_mob() - owner.adjustFireLoss(25) - organ_flags |= ORGAN_FAILING - - -/obj/item/organ/internal/cyberimp/arm/gun/laser +/obj/item/organ/internal/cyberimp/arm/item_set/gun/laser name = "arm-mounted laser implant" desc = "A variant of the arm cannon implant that fires lethal laser beams. The cannon emerges from the subject's arm and remains inside when not in use." icon_state = "arm_laser" items_to_create = list(/obj/item/gun/energy/laser/mounted/augment) -/obj/item/organ/internal/cyberimp/arm/gun/laser/l +/obj/item/organ/internal/cyberimp/arm/item_set/gun/laser/l zone = BODY_ZONE_L_ARM -/obj/item/organ/internal/cyberimp/arm/gun/taser +/obj/item/organ/internal/cyberimp/arm/item_set/gun/taser name = "arm-mounted taser implant" desc = "A variant of the arm cannon implant that fires electrodes and disabler shots. The cannon emerges from the subject's arm and remains inside when not in use." icon_state = "arm_taser" items_to_create = list(/obj/item/gun/energy/e_gun/advtaser/mounted) -/obj/item/organ/internal/cyberimp/arm/gun/taser/l +/obj/item/organ/internal/cyberimp/arm/item_set/gun/taser/l zone = BODY_ZONE_L_ARM -/obj/item/organ/internal/cyberimp/arm/toolset +/obj/item/organ/internal/cyberimp/arm/item_set/toolset name = "integrated toolset implant" desc = "A stripped-down version of the engineering cyborg toolset, designed to be installed on subject's arm. Contain advanced versions of every tool." actions_types = list(/datum/action/item_action/organ_action/toggle/toolkit) @@ -263,10 +240,10 @@ /obj/item/multitool/cyborg, ) -/obj/item/organ/internal/cyberimp/arm/toolset/l +/obj/item/organ/internal/cyberimp/arm/item_set/toolset/l zone = BODY_ZONE_L_ARM -/obj/item/organ/internal/cyberimp/arm/toolset/emag_act(mob/user, obj/item/card/emag/emag_card) +/obj/item/organ/internal/cyberimp/arm/item_set/toolset/emag_act(mob/user, obj/item/card/emag/emag_card) for(var/datum/weakref/created_item in items_list) var/obj/potential_knife = created_item.resolve() if(istype(/obj/item/knife/combat/cyborg, potential_knife)) @@ -287,12 +264,12 @@ items_to_create = list(/obj/item/gun/medbeam) -/obj/item/organ/internal/cyberimp/arm/flash +/obj/item/organ/internal/cyberimp/arm/item_set/flash name = "integrated high-intensity photon projector" //Why not desc = "An integrated projector mounted onto a user's arm that is able to be used as a powerful flash." items_to_create = list(/obj/item/assembly/flash/armimplant) -/obj/item/organ/internal/cyberimp/arm/flash/Initialize(mapload) +/obj/item/organ/internal/cyberimp/arm/item_set/flash/Initialize(mapload) . = ..() for(var/datum/weakref/created_item in items_list) var/obj/potential_flash = created_item.resolve() @@ -301,22 +278,22 @@ var/obj/item/assembly/flash/armimplant/flash = potential_flash flash.arm = WEAKREF(src) // Todo: wipe single letter vars out of assembly code -/obj/item/organ/internal/cyberimp/arm/flash/Extend() +/obj/item/organ/internal/cyberimp/arm/item_set/flash/Extend() . = ..() active_item.set_light_range(7) active_item.set_light_on(TRUE) -/obj/item/organ/internal/cyberimp/arm/flash/Retract() +/obj/item/organ/internal/cyberimp/arm/item_set/flash/Retract() if(active_item) active_item.set_light_on(FALSE) return ..() -/obj/item/organ/internal/cyberimp/arm/baton +/obj/item/organ/internal/cyberimp/arm/item_set/baton name = "arm electrification implant" desc = "An illegal combat implant that allows the user to administer disabling shocks from their arm." items_to_create = list(/obj/item/borg/stun) -/obj/item/organ/internal/cyberimp/arm/combat +/obj/item/organ/internal/cyberimp/arm/item_set/combat name = "combat cybernetics implant" desc = "A powerful cybernetic implant that contains combat modules built into the user's arm." items_to_create = list( @@ -326,7 +303,7 @@ /obj/item/assembly/flash/armimplant, ) -/obj/item/organ/internal/cyberimp/arm/combat/Initialize(mapload) +/obj/item/organ/internal/cyberimp/arm/item_set/combat/Initialize(mapload) . = ..() for(var/datum/weakref/created_item in items_list) var/obj/potential_flash = created_item.resolve() @@ -335,7 +312,7 @@ var/obj/item/assembly/flash/armimplant/flash = potential_flash flash.arm = WEAKREF(src) // Todo: wipe single letter vars out of assembly code -/obj/item/organ/internal/cyberimp/arm/surgery +/obj/item/organ/internal/cyberimp/arm/item_set/surgery name = "surgical toolset implant" desc = "A set of surgical tools hidden behind a concealed panel on the user's arm." actions_types = list(/datum/action/item_action/organ_action/toggle/toolkit) @@ -349,7 +326,7 @@ /obj/item/surgical_drapes, ) -/obj/item/organ/internal/cyberimp/arm/surgery/emagged +/obj/item/organ/internal/cyberimp/arm/item_set/surgery/emagged name = "hacked surgical toolset implant" desc = "A set of surgical tools hidden behind a concealed panel on the user's arm. This one seems to have been tampered with." items_to_create = list( diff --git a/code/modules/surgery/organs/augments_internal.dm b/code/modules/surgery/organs/augments_internal.dm index 0ae0cbc962a0..b5dd5e3c77e3 100644 --- a/code/modules/surgery/organs/augments_internal.dm +++ b/code/modules/surgery/organs/augments_internal.dm @@ -1,4 +1,3 @@ - /obj/item/organ/internal/cyberimp name = "cybernetic implant" desc = "A state-of-the-art implant that improves a baseline's functionality." diff --git a/code/modules/surgery/organs/autosurgeon.dm b/code/modules/surgery/organs/autosurgeon.dm index 0987df92bd94..293135db3cf8 100644 --- a/code/modules/surgery/organs/autosurgeon.dm +++ b/code/modules/surgery/organs/autosurgeon.dm @@ -146,7 +146,7 @@ /obj/item/autosurgeon/syndicate/laser_arm desc = "A single use autosurgeon that contains a combat arms-up laser augment. A screwdriver can be used to remove it, but implants can't be placed back in." uses = 1 - starting_organ = /obj/item/organ/internal/cyberimp/arm/gun/laser + starting_organ = /obj/item/organ/internal/cyberimp/arm/item_set/gun/laser /obj/item/autosurgeon/syndicate/thermal_eyes starting_organ = /obj/item/organ/internal/eyes/robotic/thermals @@ -170,4 +170,4 @@ organ_whitelist += /obj/item/organ/internal/tongue /obj/item/autosurgeon/syndicate/emaggedsurgerytoolset - starting_organ = /obj/item/organ/internal/cyberimp/arm/surgery/emagged + starting_organ = /obj/item/organ/internal/cyberimp/arm/item_set/surgery/emagged diff --git a/monkestation/code/modules/cybernetics/actions/base_action.dm b/monkestation/code/modules/cybernetics/actions/base_action.dm new file mode 100644 index 000000000000..98254b99e777 --- /dev/null +++ b/monkestation/code/modules/cybernetics/actions/base_action.dm @@ -0,0 +1,3 @@ +/datum/action/item_action/organ_action/cyberlink + name = "Extend personal connection cable" + desc = "Extends the connection cable, allowing you to connect to some machinery." diff --git a/monkestation/code/modules/cybernetics/assets.dm b/monkestation/code/modules/cybernetics/assets.dm new file mode 100644 index 000000000000..de510a2a9ca3 --- /dev/null +++ b/monkestation/code/modules/cybernetics/assets.dm @@ -0,0 +1,18 @@ +/datum/asset/simple/hacking + assets = list( + "0.png" = 'monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/0.png', + "3.png" = 'monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/3.png', + "5.png" = 'monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/5.png', + "6.png" = 'monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/6.png', + "9.png" = 'monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/9.png', + "10.png" = 'monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/10.png', + "12.png" = 'monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/12.png', + "3_f.png" = 'monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/3_f.png', + "5_f.png" = 'monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/5_f.png', + "6_f.png" = 'monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/6_f.png', + "9_f.png" = 'monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/9_f.png', + "10_f.png" = 'monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/10_f.png', + "12_f.png" = 'monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/12_f.png', + "e.png" = 'monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/e.png', + "s.png" = 'monkestation/code/modules/cybernetics/icons/UI_Icons/tgui/lines/s.png', + ) diff --git a/monkestation/code/modules/cybernetics/augments/_base_changes.dm b/monkestation/code/modules/cybernetics/augments/_base_changes.dm new file mode 100644 index 000000000000..14e66cfa74c6 --- /dev/null +++ b/monkestation/code/modules/cybernetics/augments/_base_changes.dm @@ -0,0 +1,160 @@ +/obj/item/organ/internal/cyberimp + var/hacked = FALSE + var/syndicate_implant = FALSE //Makes the implant invisible to health analyzers and medical HUDs. + + var/list/encode_info = AUGMENT_NO_REQ + +/obj/item/organ/internal/cyberimp/examine(mob/user) + . = ..() + if(hacked) + . += "It seems to have been tinkered with." + if(HAS_TRAIT(user,TRAIT_DIAGNOSTIC_HUD)) + var/display = "" + var/list/check_list = encode_info[SECURITY_PROTOCOL] + if(check_list.len) + for(var/security in check_list) + display += "[uppertext(security)], " + . += "It's security protocols are [display] for the implant to function it requires at least one of them to be shared with the cyberlink." + check_list = encode_info[ENCODE_PROTOCOL] + if(check_list.len) + display = "" + for(var/encode in check_list) + display += "[uppertext(encode)], " + . += "It's encoding protocols are [display] for the implant to function it requires at least one of them to be shared with the cyberlink." + check_list = encode_info[OPERATING_PROTOCOL] + if(check_list.len) + display = "" + for(var/operating in check_list) + display += "[uppertext(operating)], " + . += "It's operating protocols are [display]for the implant to function it requires at least one of them to be shared with the cyberlink." + +/obj/item/organ/internal/cyberimp/emp_act(severity) + . = ..() + if(severity == EMP_HEAVY && prob(5)) + to_chat(owner," cyberlink beeps: ERR03 HEAVY ELECTROMAGNETIC MALFUNCTION DETECTED IN [uppertext(name)].DAMAGE DETECTED, INTERNAL MEMORY DAMAGED. ") + random_encode() + else + to_chat(owner," cyberlink beeps: ERR02 ELECTROMAGNETIC MALFUNCTION DETECTED IN [uppertext(name)] ") + + +/obj/item/organ/internal/cyberimp/New(mob/M = null) + if(iscarbon(M)) + src.Insert(M) + if(implant_overlay) + var/mutable_appearance/overlay = mutable_appearance(icon, implant_overlay) + overlay.color = implant_color + add_overlay(overlay) + return ..() + +/** + * Updates implants + * + * Used when an implant is already installed and a new cyberlink is inserted, in this situation this proc fires, to update the compatibility of an implant. + */ +/obj/item/organ/internal/cyberimp/proc/update_implants() + return + +/** + * Randomly scrambles encode_info of an implant + * + * Every implant contains it's own encode_info, this info stores the data on what security, encoding and operating protocols it uses. + * Implant is compatible if for every protocol catergory it shares at least 1 protocol in common with the link. + * If it fails to meet that criteria, than it is incompatible and this proc returns FALSE. If it is compatibile returns TRUE + */ +/obj/item/organ/internal/cyberimp/proc/random_encode() + hacked = TRUE + encode_info = list( SECURITY_PROTOCOL = list(pick(SECURITY_NT1,SECURITY_NT2,SECURITY_NTX,SECURITY_TMSP,SECURITY_TOSP)), \ + ENCODE_PROTOCOL = list(pick(ENCODE_ENC1,ENCODE_ENC2,ENCODE_TENN,ENCODE_CSEP)), \ + OPERATING_PROTOCOL = list(pick(OPERATING_NTOS,OPERATING_TGMF,OPERATING_CSOF))) +/** + * Checks compatibility of implant against the cyberlink + * + * Every implant contains it's own encode_info, this info stores the data on what security, encoding and operating protocols it uses. + * Implant is compatible if for every protocol catergory it shares at least 1 protocol in common with the link. + * If it fails to meet that criteria, than it is incompatible and this proc returns FALSE. If it is compatibile returns TRUE + */ +/obj/item/organ/internal/cyberimp/proc/check_compatibility() + var/obj/item/organ/internal/cyberimp/cyberlink/link = owner.get_organ_slot(ORGAN_SLOT_LINK) + + for(var/info in encode_info) + // We check if encode_info for this protocol categoru is NO_PROTOCOL meaning it is compatible with anything. + if(encode_info[info] == NO_PROTOCOL) + . = TRUE + continue + + var/list/encrypted_information = encode_info[info] + + . = FALSE + + //We check for link here because implants that contain NO_PROTOCOL for every category should work even without an implant. + if(!link) + return + + //We check if our protocol category shares at least 1 protocol with the cyberlink + for(var/protocol in encrypted_information) + if(protocol in link.encode_info[info]) + . = TRUE + + //If it doesn't return FALSE + if(!.) + return + +/obj/item/organ/internal/cyberimp/cyberlink + name = "cybernetic brain link" + desc = "Allows for smart communication between implants." + icon_state = "brain_implant" + implant_overlay = "brain_implant_overlay" + slot = ORGAN_SLOT_LINK + zone = BODY_ZONE_HEAD + w_class = WEIGHT_CLASS_TINY + var/obj/item/cyberlink_connector/connector + var/extended = FALSE + +/obj/item/organ/internal/cyberimp/cyberlink/Insert(mob/living/carbon/M, special, drop_if_replaced) + for(var/X in M.organs) + var/obj/item/organ/O = X + if(!istype(O,/obj/item/organ/internal/cyberimp)) + continue + var/obj/item/organ/internal/cyberimp/cyber = O + cyber.update_implants() + return ..() + +/obj/item/organ/internal/cyberimp/cyberlink/nt_low + name = "NT Cyberlink 1.0" + encode_info = AUGMENT_NT_LOWLEVEL + +/obj/item/organ/internal/cyberimp/cyberlink/nt_high + name = "NT Cyberlink 2.0" + encode_info = AUGMENT_NT_HIGHLEVEL + +/obj/item/organ/internal/cyberimp/cyberlink/terragov + name = "Terran Cyberware System" + encode_info = AUGMENT_TG_LEVEL + +/obj/item/organ/internal/cyberimp/cyberlink/syndicate + name = "Cybersun Cybernetics Access System" + encode_info = AUGMENT_SYNDICATE_LEVEL + +/obj/item/organ/internal/cyberimp/cyberlink/admin + name = "G.O.D. Cybernetics System" + encode_info = AUGMENT_ADMIN_LEVEL + +/obj/item/autosurgeon/organ/cyberlink_nt_low + starting_organ = /obj/item/organ/internal/cyberimp/cyberlink/nt_low + uses = 1 + +/obj/item/autosurgeon/organ/cyberlink_nt_high + starting_organ = /obj/item/organ/internal/cyberimp/cyberlink/nt_high + uses = 1 + +/obj/item/autosurgeon/organ/cyberlink_terragov + starting_organ = /obj/item/organ/internal/cyberimp/cyberlink/terragov + uses = 1 + +/obj/item/autosurgeon/organ/cyberlink_syndicate + starting_organ = /obj/item/organ/internal/cyberimp/cyberlink/syndicate + uses = 1 + +/obj/item/autosurgeon/organ/cyberlink_admin + starting_organ = /obj/item/organ/internal/cyberimp/cyberlink/admin + uses = 1 diff --git a/monkestation/code/modules/cybernetics/augments/arm_augments.dm b/monkestation/code/modules/cybernetics/augments/arm_augments.dm new file mode 100644 index 000000000000..33e49d98af19 --- /dev/null +++ b/monkestation/code/modules/cybernetics/augments/arm_augments.dm @@ -0,0 +1,345 @@ +/obj/item/organ/internal/cyberimp/arm + encode_info = AUGMENT_NT_LOWLEVEL + +/obj/item/organ/internal/cyberimp/arm/item_set/Initialize() + . = ..() + if(ispath(active_item)) + active_item = new active_item(src) + + for(var/typepath in items_to_create) + var/atom/new_item = new typepath(src) + items_list += WEAKREF(new_item) + +/obj/item/organ/internal/cyberimp/arm/item_set/update_implants() + if(!check_compatibility()) + Retract() + + owner.visible_message("[owner] retracts [active_item] back into [owner.p_their()] [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm.", + "[active_item] snaps back into your [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm.", + "You hear a short mechanical noise.") + + owner.transferItemToLoc(active_item, src, TRUE) + active_item = null + playsound(get_turf(owner), 'sound/mecha/mechmove03.ogg', 50, TRUE) + +/obj/item/organ/internal/cyberimp/arm/item_set/ui_action_click() + if((organ_flags & ORGAN_FAILING) || (!active_item && !contents.len)) + to_chat(owner, span_warning("The implant doesn't respond. It seems to be broken...")) + return + + if(!active_item || (active_item in src)) + active_item = null + if(contents.len == 1) + Extend(contents[1]) + else + var/list/choice_list = list() + for(var/datum/weakref/augment_ref in items_list) + var/obj/item/augment_item = augment_ref.resolve() + if(!augment_item) + items_list -= augment_ref + continue + choice_list[augment_item] = image(augment_item) + var/obj/item/choice = show_radial_menu(owner, owner, choice_list) + if(owner && owner == usr && owner.stat != DEAD && (src in owner.organs) && !active_item && (choice in contents)) + // This monster sanity check is a nice example of how bad input is. + Extend(choice) + else + Retract() + + +/obj/item/organ/internal/cyberimp/arm/item_set/gun/emp_act(severity) + . = ..() + if(. & EMP_PROTECT_SELF) + return + if(prob(30/severity) && owner && !(organ_flags & ORGAN_FAILING)) + Retract() + owner.visible_message(span_danger("A loud bang comes from [owner]\'s [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm!")) + playsound(get_turf(owner), 'sound/weapons/flashbang.ogg', 100, TRUE) + to_chat(owner, span_userdanger("You feel an explosion erupt inside your [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm as your implant breaks!")) + owner.adjust_fire_stacks(20) + owner.ignite_mob() + owner.adjustFireLoss(25) + organ_flags |= ORGAN_FAILING + + +/obj/item/organ/internal/cyberimp/arm/item_set/gun/laser + encode_info = AUGMENT_TG_LEVEL + +/obj/item/organ/internal/cyberimp/arm/item_set/gun/laser/l + zone = BODY_ZONE_L_ARM + +/obj/item/organ/internal/cyberimp/arm/item_set/gun/laser/Initialize() + . = ..() + var/obj/item/organ/internal/cyberimp/arm/item_set/gun/laser/laserphasergun = locate(/obj/item/gun/energy/laser/mounted) in contents + laserphasergun.icon = icon //No invisible laser guns kthx + laserphasergun.icon_state = icon_state + +/obj/item/organ/internal/cyberimp/arm/item_set/gun/taser + encode_info = AUGMENT_TG_LEVEL + +/obj/item/organ/internal/cyberimp/arm/item_set/gun/taser/l + zone = BODY_ZONE_L_ARM + +/obj/item/organ/internal/cyberimp/arm/item_set/toolset + encode_info = AUGMENT_NT_HIGHLEVEL + +/obj/item/organ/internal/cyberimp/arm/item_set/toolset/l + zone = BODY_ZONE_L_ARM + +/obj/item/organ/internal/cyberimp/arm/item_set/esword + encode_info = AUGMENT_SYNDICATE_LEVEL + +/obj/item/organ/internal/cyberimp/arm/item_set/medibeam + encode_info = AUGMENT_TG_LEVEL + + +/obj/item/organ/internal/cyberimp/arm/item_set/flash + encode_info = AUGMENT_NT_HIGHLEVEL + +/obj/item/organ/internal/cyberimp/arm/item_set/baton + encode_info = AUGMENT_TG_LEVEL + +/obj/item/organ/internal/cyberimp/arm/item_set/combat + encode_info = AUGMENT_TG_LEVEL + +/obj/item/organ/internal/cyberimp/arm/item_set/surgery + name = "surgical toolset implant" + desc = "A set of surgical tools hidden behind a concealed panel on the user's arm." + items_to_create = list(/obj/item/retractor/augment, /obj/item/hemostat/augment, /obj/item/cautery/augment, /obj/item/surgicaldrill/augment, /obj/item/scalpel/augment, /obj/item/circular_saw/augment, /obj/item/surgical_drapes) + encode_info = AUGMENT_NT_HIGHLEVEL + +/obj/item/organ/internal/cyberimp/arm/item_set/cook + name = "kitchenware toolset implant" + desc = "A set of kitchen tools hidden behind a concealed panel on the user's arm." + items_to_create = list( + /obj/item/kitchen/rollingpin, + /obj/item/knife/kitchen, + /obj/item/reagent_containers/cup/beaker + ) + encode_info = AUGMENT_NT_LOWLEVEL + +/obj/item/organ/internal/cyberimp/arm/item_set/janitor + name = "janitorial toolset implant" + desc = "A set of janitorial tools hidden behind a concealed panel on the user's arm." + items_to_create = list( + /obj/item/mop/advanced, + /obj/item/reagent_containers/cup/bucket, + /obj/item/soap, + /obj/item/reagent_containers/spray/cleaner + ) + encode_info = AUGMENT_NT_LOWLEVEL + +/obj/item/organ/internal/cyberimp/arm/item_set/detective + name = "detective's toolset implant" + desc = "A set of detective tools hidden behind a concealed panel on the user's arm." + items_to_create = list( + /obj/item/evidencebag, + /obj/item/evidencebag, + /obj/item/evidencebag, + /obj/item/detective_scanner, + /obj/item/lighter + ) + encode_info = AUGMENT_NT_HIGHLEVEL + +/obj/item/organ/internal/cyberimp/arm/item_set/detective/Destroy() + on_destruction() + return ..() + +/obj/item/organ/internal/cyberimp/arm/item_set/detective/proc/on_destruction() + //We need to drop whatever is in the evidence bags + for(var/obj/item/evidencebag/baggie in contents) + var/obj/item/located = locate() in baggie + if(located) + located.forceMove(drop_location()) + +/obj/item/organ/internal/cyberimp/arm/item_set/chemical + name = "chemical toolset implant" + desc = "A set of chemical tools hidden behind a concealed panel on the user's arm." + items_to_create = list( + /obj/item/reagent_containers/cup/beaker, + /obj/item/reagent_containers/cup/beaker, + /obj/item/reagent_containers/cup/beaker, + /obj/item/reagent_containers/dropper + ) + encode_info = AUGMENT_NT_HIGHLEVEL + +/obj/item/organ/internal/cyberimp/arm/item_set/atmospherics + name = "atmospherics toolset implant" + desc = "A set of atmospheric tools hidden behind a concealed panel on the user's arm." + items_to_create = list( + /obj/item/extinguisher, + /obj/item/analyzer, + /obj/item/crowbar, + /obj/item/holosign_creator/atmos + ) + encode_info = AUGMENT_NT_HIGHLEVEL + +/obj/item/organ/internal/cyberimp/arm/item_set/connector + name = "universal connection implant" + desc = "Special inhand implant that allows you to connect your brain directly into the protocl sphere of implants, which allows for you to hack them and make the compatible." + icon_state = "hand_implant" + implant_overlay = "hand_implant_overlay" + implant_color = "#39992d" + encode_info = AUGMENT_NO_REQ + items_to_create = list(/obj/item/cyberlink_connector) + +/obj/item/organ/internal/cyberimp/arm/item_set/mantis + name = "C.H.R.O.M.A.T.A. mantis blade implants" + desc = "High tech mantis blade implants, easily portable weapon, that has a high wound potential." + items_to_create = list(/obj/item/mantis_blade) + encode_info = AUGMENT_TG_LEVEL + +/obj/item/organ/internal/cyberimp/arm/item_set/syndie_mantis + name = "A.R.A.S.A.K.A. mantis blade implants" + desc = "Modernized mantis blade designed coined by Tiger operatives, much sharper blade with energy actuators makes it a much deadlier weapon." + items_to_create = list(/obj/item/mantis_blade/syndicate) + encode_info = AUGMENT_SYNDICATE_LEVEL + +/obj/item/organ/internal/cyberimp/arm/item_set/syndie_mantis/l + zone = BODY_ZONE_L_ARM + +/obj/item/organ/internal/cyberimp/arm/ammo_counter + name = "S.M.A.R.T. ammo logistics system" + desc = "Special inhand implant that allows transmits the current ammo and energy data straight to the user's visual cortex." + icon_state = "hand_implant" + implant_overlay = "hand_implant_overlay" + implant_color = "#750137" + encode_info = AUGMENT_NT_HIGHLEVEL + + var/atom/movable/screen/cybernetics/ammo_counter/counter_ref + var/obj/item/gun/our_gun + +/obj/item/organ/internal/cyberimp/arm/ammo_counter/Insert(mob/living/carbon/M, special, drop_if_replaced) + . = ..() + RegisterSignal(M,COMSIG_CARBON_ITEM_PICKED_UP, PROC_REF(add_to_hand)) + RegisterSignal(M,COMSIG_CARBON_ITEM_DROPPED, PROC_REF(remove_from_hand)) + +/obj/item/organ/internal/cyberimp/arm/ammo_counter/Remove(mob/living/carbon/M, special) + . = ..() + UnregisterSignal(M,COMSIG_CARBON_ITEM_PICKED_UP) + UnregisterSignal(M,COMSIG_CARBON_ITEM_DROPPED) + our_gun = null + update_hud_elements() + +/obj/item/organ/internal/cyberimp/arm/ammo_counter/update_implants() + update_hud_elements() + +/obj/item/organ/internal/cyberimp/arm/ammo_counter/proc/update_hud_elements() + SIGNAL_HANDLER + if(!owner || !owner?.stat || !owner?.hud_used) + return + + if(!check_compatibility()) + return + + var/datum/hud/H = owner.hud_used + + if(!our_gun) + if(!H.cybernetics_ammo[zone]) + return + H.cybernetics_ammo[zone] = null + + counter_ref.hud = null + H.infodisplay -= counter_ref + H.mymob.client.screen -= counter_ref + QDEL_NULL(counter_ref) + return + + if(!H.cybernetics_ammo[zone]) + counter_ref = new() + counter_ref.screen_loc = zone == BODY_ZONE_L_ARM ? ui_hand_position(1,1,9) : ui_hand_position(2,1,9) + H.cybernetics_ammo[zone] = counter_ref + counter_ref.hud = H + H.infodisplay += counter_ref + H.mymob.client.screen += counter_ref + + var/display + if(istype(our_gun,/obj/item/gun/ballistic)) + var/obj/item/gun/ballistic/balgun = our_gun + display = balgun.magazine.ammo_count() + else + var/obj/item/gun/energy/egun = our_gun + var/obj/item/ammo_casing/energy/shot = egun.ammo_type[egun.select] + display = FLOOR(egun.cell.charge / shot.e_cost,1) + counter_ref.maptext = MAPTEXT("
b@N zy$1sDMQLrKD*FIvNPtQv>GsLT*LV%|zUhj;G(EwH7o=U!HVW!*+;35NlFJL+(>ot6 zQUPdVR*jd-Ww{cT!$*&<(62Hx$TGARVld5m+VAck!b47}r8zud@#0d4;pRR!<<0CX zCRJ}=4kP{BPzF%2xyH)SjyO9kKqXY|Sz%)3=$g#Ti9nnZ+ND^{M8~k1xnQzM_Zq|^ z7^ii`zYc+wKYFD_PkuG=Cb0?Yy8zp|N4!7pHgCMF(Ej(0yEyQhR2-^nViIoz`Z;5@ zmsCS*`s?{Zwy4h2FcTdFwxabqAfT!I9od&UY}7uQk^E|pv+jtit_rEEG4Aly(b7Zb|Y&3-4}Nhe7?>*Gqht&SPM zHS6o^mEY`PKJeC``$8$Hsow+tzyVSF5k`HdSgqKiD?XPH5-GE~F%;l&vM&~qham+W zhB07=NO<;4d&XU cEIV$U}wn_e #WeP|tcb_zG23Rnedc{zAJz z12lf_3C4}k(sB-)R#~WlFA9w@AZi8gUb}Id>B+~-lkdO79fNCS04#QT=Q}k+dFih; zXL_sy%Ok$oD+C|n+LAmnNDSx#n2Bc^xN*i+UwCv{6EPrFXNg7=4L%!em}_VykTP?P zVNmGMe)+N(JL>>&)PE#I?>T4%Ww^e7L(5dx)LVFD97ySBEM}GVs_`fEzNUa+6+cuf z3czvT=#E*3%TpN@I+p&et5ZOT(OWfZB0S(87!?bHN Rq! e@P?WFSnl (wb5RlKhpw8j|XhsGt#hb`5y4m5^%r~L8U3T@S8 zXV2=%^~+N8-$SsT-U1aZWeyDtdqAe^O=rhXO}w(SwtZ1tT)eQqUps%qbZNT+%EBeZ zFy?y;d^NoQme2&LPsoE#9?$^CU2s6=L|_d-W3iZkglAWr*8fTVBr _S%;te}{69SA~7gt^w3G)ztW?i78JU>yN^zxZ3rlzJLD|bZe}DTsaLCix9(l zh26L2t}*DFTod+oUW+kjl{I8WIkz4us@hWk9Z=%vN7o9V8HwF&ZO*T1tE!BU^3qMV z$%qGU+GCAL>us4AVtz6%q*@r>SjE4@f67L$?Dhymcg(tpAPDcn$o?fa+?aD%;=S6u zAqx)z%4QG}?Cq#llpc$Lon(-xU ?mD#rgQ-*{n%7Alwm^hZe9zopv1oQ#ireduD ziB>5T6nvOR9r6CSr0o8*ewlJS3}nUO;o%p2@4iQ3fRnILr7}opMA&j!m7!$^ CIJ4m(yE{Mzz zAHMe(u!K_qMZKeBv=?-t$)V3pO=?bRgC~ImYX`c)#>8S`_}szu_dAM p3?x{)nirF0$}|GLEmA-`#vp`AEh3UgN=E9OMxC) zD+pp8pj;s_v0`Ao$V<#+#qq7Ht0d-Tvr(}UQqTa7<8X@|=CI^60S`j9&pF(H0N{F& zlWnN8d4~M)%Y0E3=1+0eTfwtBLxbj@2*vU*rdzuwKt3tW=rtagn#y?Vp!Y5=V12r& zacN&y8(2L4dw5-lG0FoT{JE~q3CF%^#(C|MVUrAw8QV3Y(qn>R%XTrIW-(>m|OseQg17( zK|u3Vnwo)pWs@orqbU>WzB|Sns7 k( bwMSE2erL1MJ$Yw|G0p z+zjn<2e qbW&i)Hj(CXg%5*S8C d?O gfYp)iWPNqo5O+Wd@pk#rAe=pyXQVTR|_d=hEm@0Ir-sOeuEkJf-B4alS+ z0MpT{{h?gRs-NIm6&Wg@`g?k+*WD%HY1*J?A?)Jnsw0#(Y#fwWQzN(vOpXe;T0r|g zsIWOSR^b4lu*BE0sZcR|;fCx_9+DxE%KgkX5(O@ex_8r*0h(2$%R?pi&7}$3jQ~@x zW(Y431y_My0I+FY@Q!v=2l3eN-+YZt+_!0YJx_4OU`o5=OF#<;B<)KhM$19d#_;X+ zG3i^UX0rQg~c{|C0T{LV=PlD6~Z%E2k6yjKE#KJ~^)R(SB! z^71s`1K#b41e9^+-}wJGdx?2CHBYSWhu$OxZp3y1mgq$Sk;mhBJb)7%Q}#-8r0N#Y zOImPh!+z#nY3Xj5*Zws>X}SQGc0WEcLm6T_=J#YpWNp%!owE*Xv=IZqv3uovY_K$h zgn;+D!!3jDnX=c#`SvWpYLUW#xTP+-e+I?5(~!5nx{?AxGHy+Apc}Hb<{UQ1(@^=f z(iqroG3sFL=TsIWq#W}t91sVuv5^iBqlr$ih6M6*q8W3*kr#_1-9nH6GdO7V*x+jy z78fG_*#G#ILQc!3-ep>)Elrl1nJGMVx>w!{2oIp%0j*|T#%~fMCsuhgZpO}uSbKX< zik^%a9RtQ2#n(NtCbZn>-GE1>7*{xfU>9=mBf4=}_%zgX)YTaYu;m=1N<9APWk| z_npoNm~Ut!M8U=>P}2%Eh7eejRH1BsV}2bV1etUgs^iDT3NnEN9cJ?qIJ$ 9loHT4pE{!~KsZjJ{>wy=Xcq$xg zczf|SR(jewCDIsFC3A7wKzt28LL~FK-E`SKAO@EX07K}qfTin%DxA0gur!^deqbDu zK#*(G1^1f^^eoX|C+ujOcx3H$*TQf5K@PnU*wF?y&kTtFEItzNB$Gi;{4$k{&`Zxq z89}Mb0PGv2tp!7X4JE|LAmHoY1-53R5>VZL8PR|U%TBBSQ;fJu;3oYM@I>G=1+WM$ z+ZV_J18vx`sR@5$j6Zl>VBBzE0&FqJjq7R?)7!#j t)|Vx?}JYcf_$c-$Qi6MWpkdr&Dm<1BQfbfm)Q-V6J(9_cK>5 zXIIVV+1ye{*V$%QJ-;6D>tWV1Ye{TrYT=2N@;qajIzN8b`@Pck1ieiCJj?(KIfzd7 z4B-GC(07CS8080R4MI8Jg`3vk;lW55j-bw*t@Itz1gp?&SeG0j8yo2ZpullwLs8Bd z5_aDdHHXDs3E^ Qu-pInI z=C5C0{f8u$SFheOCkGx$pc2zZVLv;}DNNSdd`wJjL%yw$yebVx!eoF{gHjG=32pYr zp3?=(k|kmWYi-89IOIq$!~i-0=Z%w^8=#|+ACQ3=0HddemE$vgod+*KBX_U9zMiiF z_AF6gVscy%jOKDsmFj7!qS3O+$;o$XZTP|Fypkyf-<+v(f?<#t5}J+{(*B(u?d-WU zs)01S3LHv%aLCrqjuFI@QDTKX6%E>`$H5wG$3an1N{i>oDui0NH0XXuv(0UVf)ZYX zPkJCJsc-v|whO47D;RmhsGi!CwJ)s>2=ZfZuzyZfrhrR%rk?w$3PBc)$YYw_xrV3k zAXmb;IT637WavEb)sJ{$b|eil7o5Og($39;kKSz*p#TniV4%P!$uBG};=m44b~(x@ z?Cnl8Te@vB+%PqfBBht^5lW7Rm_Q`D7i|PKmBIocI+GIM3aP8Bi-JRjEj4(XH|M8E z9{)xg&(2<2v|&B|yy1NNX1wGo!Vk?J%Y_3?XexQiOFeUK&tu{Dsh6{hi&m9Q8YfxN zE(sA|PVbDDL`Y@5S9=(KHFeT9rlyCs%ardu#uU)(`I(tk>wg=o^{*Z`dY{xvFva*U zx-W=OL}G7WMM{-V3*W+<@^19xuSdpM3{H>n>ITI>z6*ARV51K`VaUH@TXVyI?4@w? zD$bssjm^g$!^5~_?&0otD((gbSr{}8E)P>UOG02NbY01$9y2*TDJYTwU^rwaAcNs6 z{E|VEu?V~+!OQCJ|JcI-OwKi?dexWHB^LnR5-iZK_dd5)q>b$TccvAsJ0;`R4C#*> zn*jnE`Y*?4;ZMkOE9N0fu!843Ci}tWExy9cnWm OiFtpQu1P)4gLf8bp=mZ(fR|_Wqx>&w3X5*^HCZWn%?>a= z!~>7NLoRX*!N7$DL@?7Gzplc9#t@9s?RJ4co*{)C%Kv1airW##qom8HNq~HJ;Qjd) zNqg7@RvIc)DkA|Z-z%YOWveoLDNr;Kka1`-=2Ro^Xr3wkpBF$dYP^TM4UWK@=KbPg zh*AKS=%v{B`FWM2{gE~2(H(PqB@9dOEH(g8skT3{y*)o?fGcj*Ni+zKbDHHEsz?dQ zE(Q7ewN4WK;OEYZ%=I9(lapsQZCJZ9)T7Wj5m4=bWqa^|2-X(MMorr8fH1D4x26lu zGJKWSh8%D9#!JC03TX_{Z=Zr)S`gyRtq?$m2t*RQjNvNCL=|K({8z2rw9t~hl15~4 zumN3)^;Mv?b# @P??x`xLmY4*roPDKye;J0V>e<&x`*wh;4B*fdF{#h$$h})u zbixe(&9#S7537p)=X?A;^W1_xUHXa75e^8~H#q@^8c(6W$Dyp0WGwQ=xPD^QMeV`c z&yxG!9IRodyqnojVIp~#F`?mk9t+HGQACNII##8tHG+`%oDPA#>iaM7gUpxFwN%Au zuIDDo!MUg`a5=S^D*}=&Pu;h1`Vz^?W>2d>349Q7$eu74ATKfS0;AG}6K}psc0iD1 zqo)QIfa%Jcit6X!b(qn==xQE&M0*a)noC-bIsX`=I~T=iGqV&QM;0!*d @YdZ9JKVBN5d_H z#O|;EoCpg{oY?<*&H${^;tFUQ>dhe2v|bA^NW%{)4JRC08Qnp1jr^+YTcVHP)GDqB zLWs(Z=^i)tYvp)#p|lRr^M1!}m6m?@-Okb1q+S#CLJiKBYeEFvMZ9{=Uh+@a+5Vn< zOJ{g5yge2v6o_9FiZkYzpn4KFcp+Lck{?YJ9{Y(5c&SW&S;)tm{H^Csw@%Pzje}qW zU@J2%F)|V>fziBSH#sqMLtfg^kuC9YzMwR)?{I29%EM^FiIwsPCw{*o X`wY6CR z`|h{K6s6%Z5}Bhc#7ZbCpm#Xx-V`6ri|10bibVy*27pp?r}j|IG?G_x)BhI|G#}Y5 zbII}|Z$w-XXNc0a@F{!KFJ92s(=+o*C`5PkN{N9gj8`!O=N6|3Ry=z`JZRwr1%>|A zP$8PhW@+DP-I0mx^GBy4h$Jc3ubuKj_AL3B)L1yBjFK`tn~ewpqxW~
?(a1@`fbcK-ONAi{Yb3-FMTaH${l!6c(Va=i&W?V~C1fkW4>4-=)=)hyJ^dSP zg_4ARKUc9@dr$!^0O~N5R?dqJ5LDX|zb->Tlkoo2oCgr%fQAcpqwy9V0Wuwg6e+tl z?!-{JEC4>hgW I$i JQ*VD2is=8(d7VnSBQ?Hknu+L4dr;&ziZ~itpuPIny~l) zF7L0CBp=Q6vJj)9lQUr4_NRZ>n>n@j)0DN4^Ynp%2qHB~AwgYbe8A`~_|FbP(PWW} z4*(ivms4j192?|DOgxS7ulw0$t9+eHi !W8~AuNhI*W z2p%wYT617Dmi9d5;!!N@NIIi?afwfPD71k;RlHA--e`z9D#ZxTE(P|VJg(K10LZ42 zE07BZ@(r;+vkjJ*5|XjE#_uW`b ;=4oF+3kE-Su zeA>WjEtp(~%ZH(IjQ$@@R{<5}+I5FeLWDt)l9mohk!}$XBvhmkkdhMVP61&E1(EI$ zlrCwIR7yk~Lb^dpy8koxTmM~L7YxHYZ#;3%K6~$zXa(nj&3pQ@(f3+Y$~h5IPU-Vs z $FhxZ3Y9K;8il&Gx;u6`RRBSHuYUTK1t8Bza zosJ3nuE)bnbkUu^4txN?{Z&oHlBXQ|jrk>`##NgnZWe1Ku8KfnFcP6SiH|EA=6aWu zmwY!=ewiec;lVWoMF0|V?6-FVk%)KSm~mw@y0zZD&>8xIv=T_fHBEw}*}*Xnd?=$k zA3up094iecA=b+ovQI9LQwZ1u`h!2_v~Xn+Y!6dDh78(cgQh>kxf|Hl7PhlD#kOxm z#F-N?UZ8NS#-xT3LK6g6+K>ilbJ+i{v~KX8S9;BF zF(xeEYhlYr$>$%VT>e?9{4*jL^OJ?3#W)dvXYazT`#?H~ y-_49($Ppta!G~eSDD`M4nO(ZTd+L)Pz00_5`9e=tO8v}9QcNo390M}26 zUo>uB4Z+#(e|^Q~ 6SnN*fcCjOD;7S+kKabovL z{6M~jq1CHjk+30Gugmc52T)|aahD})DWm$furmgKRyQXGftt8gm8$gC==pitHs s+kCMu@@GgNs0`DNL?;C7)Y;4^If;#S5C48)|i|PsQ zV6so-d8f*}`Ro~#?*^Pk69q}0v_T+_^~;p>ZgQVzGum>(9xY)dv_>fRan$O!*BRZ~ z!AZLdZFPP9{Zoc#h!$X4E8ZF)!h%|o8mKFtGZDa0-o$I!!3KuVG6@GzWdi7B{FE5q zDj^20Kn<|FoamdvHQ*}3AIKxjh5kW02FQ{xvUFlXa<+~bj-P|xEL(hThZkwlIKG~A zW|V#>xvx%-+Y}!Q9;%=vU7=@M5AIC1qKv>>1)hp~a2Zhr&sbmDC2VPBdL0Q@t2bT| zj>IEuBY6FBvkhzleveF9<`)jXDj7Ei+Rm+D!1A>&BObGpLounM<>2kQ2&8&2wy+V) z&SEJ3POWm3hwQUJhKz^xb_j$ZQFcFNHeinP|2!&-A+kU6_%$+h*F)AHU?&k`sk?{t z{$4dRwaPQ WMs450GyG>IblC_gq^uIS<_YxlEpO*O2K-h> zny~0gO%ANrn1rCXSMd02@mvg vyvSTne{44&We*NYMKJFik`= z(q#ZPqYj-p4*+5%(vrDAhAImT(PIPGdgeqWCJVtl@?n84r ns;N4X^4yE>2jH+0V&i8a&aHZZ%J#AueJi(C)cfqX zN{=%5Syhpk3gdjFgm!|)jAvRGYdta>z4i`n6jS?i5Ee{Kcyhj%yZLsRc~w 2Y;MIh_dllYH#mk1(I(C%A9RevX1j3uQTx_^GD4k=y3;{P_n+H;UbT4 z|7nUFg#+>0+i (^&fcaCChP+ewQinkVPZ^xQby*3egf8ntb z+(Dv;3M%7{`0+-y7qWTA`@2XLU?k+zi!`c)ncu$6qs>k$@Y(d2mC)+KtJ;Rz+FW{l z-2%w=)@WYJaNOw##-AGu%N3+bLEaPbm{kycuM&{Ot|wOH&mJ~?Vm9qTY=8!wl9i-| zQb9J@Sq}@+*~grh*wj _C7oA1Kboakk zxfAxuO0r?e&;1DHl;R(SEqPEzc5=T)YQ+hC)fpl@l28l}yvHA)ILFUL{>0ADui5DF z6DU)>p+D4=9d)Es$< %FWSkHs+dVq3z^a`6hr z&I?|;r^fJpJXi6(tvnKk1vrd(c|4Jn&~Ny^Jrvg+qV&Op2qBb8>2`gd(C)m^wO< zofWA?X3)LF- r*Kvt*8qJGNUwE{K;HkrY z!O0)criuFRi%5i~ <#8M;n9_=I NA4^@)peyk>>RvuUcL^0KDlM=H7n zj|(e%;|mHYPnt;BUz1VgB7yN0{E}~ D)uT9s|1^dd#OJ}65 z1tci7EH3tWR$hJ9ZX%rP+#OgpfRsx>$~}*hgQI|g_z6F385Iymv*+5H`51Ny>gA3~ z@PSKBWT$VcfTCenCTzlj`aE;pZ*M&ra2c&>zW$ASf@Dg~Qf7#x`%_7iN)|sY@fQMo z03BrTIf}G *IA$aXgVSsP)#xi;{=>U|q-^HL^=1V|8}0 z$Y3-xo=k~9E(ZmSN{BG*>^OCkzyk;cohINzF!Xv~P($qc+M5oH)G{6uzI(&`T)-N6 z2@9!(V7#Ot3meZ>*nu=SFM+lUhAh0dB8j{&isc%VIle^6pE39!9f|&eLkG3Cs4`z4 z+gn5L+1}2#vzriB&_rCd8H4#uOcPpc(DYfIEY##=-_9^Ok>im{f%Ua_-z;7NKOL@0 z0iR>2Dcnkrc7OF#$>g_?ycIQw8mxEqsXD@%8_(yZ&%6Fjo%c=sx;P!UIQ`|;d@=z< zfA8%$yYbJ!5$RCC#)!zB?%MzP!L{&eVd7x&QxQ$1UI35W^7p~_g_4O8%HITRA$Jj^ z1SM+Q;_vwF_POX1>b*M0+4eW=@ZJ_aBFXnEnIBkHsHV;~+|YA(yy-;!5mMi|FG* u1Wcx>X`dE zq+1CJjvV;>0&+`=O6G6b3ZSlT`x6IT5ycm6NrY7hK}i%EcO@X$0H2EGYNOGI-ro4{ z*I(m;UIB)LKr>&o!(@Xw%HV@eD)A#N4iOO%zDM{R*@IzGUAGiD%+SS#pY~3Cz_UdS z+& Oe*+cOU$Q~gvf#Ds2ilK3Zlr|u4F-Q2ne*HRp ztriq4GSipHNH#zjxf(+}3aI ~`t(loTXiUC B>(L1iUQ3-Cknovl$o2CZ1z zakYM0|FRYCQx(qsGx_JIdYXjhhO1W|m|A^XER9nwe|uN!nrYxGgb?Aw%oUH@KNxdK z*9&lN4P;y2(+r9GiIls}nDb+%C8sUm4HA3@`xtYDirBw*?|ifq2b=`np0 +1TUkbc!xZW~1wpkf%c2Z<16Iw3OoJjlqAI*PX z-g2(&s)xy=nS3+<`|_!lw_cp;CC*JG`R_|w_QXXbQ?ALMP2OEeIsZMu1mY3`LNyh9 zgtJIa)S3Xk{Iaw7yxXNEry8HN@u{yDb9)!t4|Ec~q@s{=PEJlYEXFH`tYaS&GOC1r z)_Q{kuEi{`_so?~Cy^nH%F4Mq?6a= 6(%bM`V_Fe9u$+KN*5t&*Ffgw<`+`_G$iqc&pUF agl{nLM1nQkZ;!1 zf=CSIgFKm^S{+(hf7%q&1^N=et*>bo73!6Y-vJ{OuK&3I0ILG4_@`NnC1_ Ed zXMLFJHtDezVQe7D7S2@qZA5)NT}tOBQK(x>7AAGA=84$CCP8BV-Q2thf*hL~9w>Ey zx}gmAqw$sVifz;Y5p7pV8@TIXI1;@02orn=rD!6%_^6{ZAuL3Ofb+%{zvJ*Gvfpwn z?YQ&N^2RjI)*wzIunhb3?_$at&qZHzM dT *?>GiIuK9i&trLkpinB{Qo2{D=al0-Y&iGzu20n;?IxK@EQHj1*~jN zs!5$j4BVGetH7HDPA7`>aHzW+|Lp=h5_rjvqAfq?kq$VLlby^PuMJqHd0dtRD>Jvl z^f^~AxZxe9q&NzFG4IW|hQk!p6{FV5)0+<_^`IPEY}BX)v8Bj|9_tUcjS2y}Nb19R zweoXe4D!CGe&F-g^hCG#v5Hgf>O-IV0~UfJsSn%6#%&y|lR|DAd?|IE?|!8#tCaZc zIoW_?-?lpuGI%d&5sRbn-n_@{o`oGHvU}`LzjyB`YOv2|YIFy!MP?Xq+|ndt{4j89 zz)4q=Ckx&k!0 01^&THA4pBRRURj%AD|tu&bKHq+AlD5)@hef@X{Vr=p1zH8r@zgq}c)m zC(_9wh?koZ_iH+@58OFYTi*~(W+0j_qHpRj^4{)v#52U45Bv8sk>~GU)v9twKvoDK ztISX-D?Ss%a_y96K-tQ-pQx*9DlK*AB$C(R seB}D)Y4;>#hXaPi!IV0X{W&j!I=)AA;+xp5N+Cb;h)lh0X6LcTR5f z1ge{90~V3TUC4Wzhvx}CqlKRGt*L{|@pVi-A8t1uH!6(TxYHUv_g<{G;?WzZvBp|C zIX&v4e0qGAR$E{H^deUJD3-#()^^$0Z#6oNuua3!5M7ygHEro#>V#wk6iw<=iM 4sH=Vmz~ZuPhyTl0v=AZ z5(7n0VY*0FfIEGCA3k8dCh{7|IlmLe-w98-GM<+#2JXzy*}!;J6_M9k49f37uY9(x zFYUS3{w;R6`E0z|$J)^`Go^1)NG`0byj*KfBD&+?v=gw`aMpl|K`Tm#sILSfzWaJEWJJRSBKdvm34p~FHob{+ndGzl| zu#;cWqg2#=&mR66z4r6$Oz|uLkM3iX;w>_Q9V};YL#VWmRN0_+kB(|z-Tv?tqM&~g z(T+4S^%MNL&EX*y$fz85%BR4=c5!1V>+)Q29kXEWjdw9k>kMfl!B4g!&)mC&Wv2 zwe6F2wob}qVu=prh~^3V*jKd%jI8{m;UYC4_S6-v(|S@C4pD9{%z>eS$p(){#s&tT zN1||?`Gh<)H6DVC*4X4^vEPp0$)a0b>TNEvD;U)>xfHYA^vDslMvBH6N>fkBn0p zm$7A|zyEmTpcQhx+*h?2N|)5(G(}+?zwlGenbw!xoo4h 6(4JB#Zvi=9Ot6CpPDZJtzY;ua@Kc%r9spyGb7lH1~9NJ)&hSg^YC1V+iw zMyWSrwC~o%*>u&y!ook7vtv)b{epMOHXOPkAa0sF1PJTyr2EeBTvzlH9@DpdMQV+( ze xL#)GzfKoe;_Va!;KqwR>5C@Eycd=5dYZ$7{^ml4laL z-crRASJ)n-M67#(Z}|PVGsNg|#27pwXQ(|2scGFs9lx3D;-cU+`b=t?2xnT}R8~w8 zeyOjF_C->jE<2e G5~WFFhyt0MZix zqdD79^6*P`K4yjxN#2m--T}MXs_VdlTMf~?tA ViRJM)GO_*YQ}MMk$T3)Xb!W_=G9^ABY(jdSKLyZxZ<<0C?rU*YCZrT zJxz!uuA5>P=Bt- G&n!db;^*~@2G%B-;%<`8fasiDRUFIWpv z<9_0t9QmTD4?eFnG&Gcq2;i%VrACP^A1IfMvka0CF-7~Vls!>?_p$_i=c#tolgRb; zJy_WpF16-He0TxkxkFB<3;gqVEa~HUhmkb{lz<>30s6==SXXLSeHJdGwVb`44Au5b zR2fjxU|$VC)8Qs}ggu3oAAlXVoGR%Zsvq#c!KKxAH=F)UYv#_wu8Za9b9``s-1TTb z@;0cac=%BV)$|sMbJZ2~W*_g}xGvZI?GYQ`hC>n_tB>!b_hhC{7zUPw!)3|iXC7ga zxjO$RHwJinZR(0|a)@@g2i(sdOaLInTo=@~uPz~`_t)*@pFCj*j!}?P2%zU1FVn2j zEBd>+p4?s@#;jc0Tw}@Csl%Eq8?0k%?MMnj;c9?JJ}jz$)k8=Wf2a01(}RcyfJ)k2 zmPJ67^bbkOK%;G480~|b9bI_~b z=%UFcpLz_Lq<`TRH7#*p+2$>^A+k2U-qnIf?+4w9U=`%LAC>k`@m5}6@8h$ %_q=vi)a#d`lFIT+T|mKrH||?pW&W2l+Ry0@pIfqAcb<_>Pcr z*k_zI`rL~BW(BknS>&^$?pL) U6|sXzUV$SAiYEFN5vpj8 zeDMhWR2A=2L3+RnUJR>mo@^F-{PFp_^UJi^*!AwiW$^)Dysnr-KGeufi%Ke6UGf5M z|D+M)|K|emJ_WE#Nt2J3_#0Tx_S}!>($WGJj|OGP_gA@rU?6KkBkkMpYH#(45758^ zk#bOtzpt%b*k~V$g_!?$$=S>45oV%-cix{rLUi6+t*d>>cO$Q}J$zh!HRvVHeY?(t zwOZSg{c4OBjU~17e^x=&H7-w{IQH1SbuZc;oAato7fBz%1flHd19SEV*~DrFBT;@3 zaE^ffgoOpnf=I(fKXvfM+~T_bsjanTw}CW8)XBnS-h-QiMH7nTV2=w?fHv7B@L%-W z%EWv6Hbw484l5MNS+p1+Rl{ct*)w0~^c!jSS%V_&PpJ|fg>_X`);}0o7GS~BFInrk zA6={^Cd8twWnpnk&k*6Cm`DZB{$qXdgBvVGb`EyK 7CisY0il(#gKjpl6bL|-T z8>{sfha1##_>VtpQ+u5pt6Eoo$GQ)jzDZPHu~DragkoV@ged;<%1Y<&I|hrjbPt$g zm%)1!2<%uZs*3fERD&~zvWOtxsogUrp_V-Za2gpb*|COQH+_YNvgLkj=-d}$Lb0+R zqZ5Iaj#~t6TY~D`uF~rem&gvaF6mF5lV3(0CU2gp(ITez&_=hgc@d1kpJ~LkC9Q-b z!80}~GK3UYlV1=@06^1oGJJ+ggn1Vq$Na;^(5(^Q*?n$Njg~{n<6>#Eh}4uSSf}$u z+Rx!Chf=NMP98C0+LLa`#ZX=4+OHQ?)MmZA1{5M`L6lka96rJ*I685Xn%Tlg8Q7+P z{;H&z+~nSDl^^7mb=NiKh?aei)Z)!|&o1`Q#1MaG{KM(VKquoZfsc(;AdPd@`$q9k z&yQ(^47(+y$;gR@0$?o=dHw7N4g+X=4P14h77P<%@6*$g)$ptgi)`!ea+K%9?JGes z-`QXD8rPlfyGvl-FmpZ|s{*TgU9hqif|`vU@^UHQB&bfaWtaNPBs3_+TVwY3e}w(C zC8!y<6QGV{C!qmTI5Q!-HcV9|7)v#M?YPS3&JB1adiq~(k3$dp>o$fQs3CnTM V~0C@afV_l>Nqu7K~* zBKWNCLtI=u3 SBh#DJCf)@roh>0C|I)@$C!aN4Ns z`_bCWx?d~Ch&J}UY- tE>lNYfReAOA*Eq?;VN3+^f@7^qy?w%wEir8Zc&OjAP!zGzs0K4qjgq@3S?C z$p0-oxC0gTNX0ggW&GsnVIq;-{jmz>UQ-uaEBWcZ`%uvP3>E>?U_Iz&X(^fd?ODLm z%=clgs`uGk!w+k0{yp-1#{Y2HzWsQ@_o5d7nf?D#Bu{WVQGyK>aATV2u9s_zJdz)J zOnR7@--lN}A(4X5@Y-xVsAdTN2#Ge&kPrv$&of|%nNTW<6=gt5*I@Yg3HY3=NhZ17 z`39oUy{y7n%*&|{xw`p?%={cm9 0cTAA^tmy zF!tX4liTy*1q_aG$0La0s%Boc@M&)!^r!o+96>5L1LFDSCg=UXqfd0-#hB{?bF0u= zaO~oEJKET5{%ytZ2jlb2lK%ew*|RgrNAH-YFJUQYK8Ic$$AA9Nz#IuLHsSf%nw;7Z z{!Xz;+5)zcGvBGd2Pb)PaWR|GiCH H(pvxTIv0QE(Ot$mY$A!%1PW%a6*b(E0?y8|FLS#m6enkVILDHu zUt&m*!%skLiZAbeR)tnV73moo8V iUCESVxdrR zfI!fIr4Oc*{ierf3QvV2GLsa767M5mejUt&(*G`moEAWB)85}T%{#Qc_n#ZPg|{^K zUH-aD?f^A9njLlUSM0S=jc3|ODS~H{XnB4pzS(%PfiWTqxl{DVR(K7_DLyk-j6a?a zTpR^Gz1Z uI5?S-z5pZ0ol$4upwJ-3xl44f61hF+BJUb`&AwslZ# $?2eQ~}hJYh^I{!m}7G1htJgpAvR3O^#39;t+ieyQL;8ADIdVY*Ztzw$*&i z{^IMx1)IOF#z8RzrsKVL@04T-naE8zL@)Vme*g>A>kuIyZ`5xVO9E;zwQ}{CdkaU& z@~71ZeGL8SG+xR1=EaP3! la74LCZyuAbsG z7uoE!zC8H;Ma>X@YFH#$N6vFV9H9y *R #>kMmbmXM>jv6`oyGDCQ z A-RjIY-J(JlLnt-S(fI* mpo< zKbWz#nF8j#p!5yQvyScWi|I_#cMM`Q8fP0^KUVX(aUmW*|3<&NY#01BHQ31<^M_c$ z*+lp3Xuz+@b$_Pf6JY32ys<5KymH_fETEEp8pPK8&&}_^t>k<({cJRXmH*r9S5si< zS?eb!LshKTk!EatZzPCB>WdybStY; S0V214;JLUlX>Zs@eg3KW_|u2;!|02{QMdE$)S4SP>>z`t z7fMz{;(|)pb^B+>*oUUwTqg_;+aWb`>$fKP32=dD{gktgUS8Q3w}&{Rb}6lGh(t^V z_g}<(GKXE2UYp`CKp%+g%>S zptW-$P#uCsC13=?nj4olWNq$Q%w;s>G$0vj?0aPX595rdE{>D5h!g&L4}mcKtMXYH zB=RpbJD!#p6}|uPeSzy~@h`AIeAj1Q1q#WtOhJ;OEHW*yKG`erS+`M#-X6Kt2D-O_ zl(?oi@3HWOxaQTa)xBwt?!|I&k~Hx#^YQUGQd)p&WF0*&pSWPQOTFZ~9*eSdkzbB9 zTR!QL646xGZ-PPr7InPW9Xm1L27J3A)78#6fU!TmzJ wlHO*G#`); zPTT4RDqO1#b}=0jQ7b#4BfGSMCEc_3QDI4j4_aU}vJB^UH!LF{^?>oPdhCE|jY4 zG7CA+r-MjdK;ex#H;%MNfQ_@Lx7x0g(hOqxp}HAxwqW(zAGO@ZeAFz%uC*Kg+*(J# zcI4g3FWE8IC^Fbmd48_AhOK?Eb#N#tF66A{?xESmThyvGfQ}))$8As&T4~Q;Rk(p` zhQd(cA;6XbvGJY#n~JQ-9Zj2p!a09e-+#Kp@*0C!_5S6qrZR&RLr*hi%>zZ8U6fd8 z`e)5W?Ve@~xY486k_yAi)*haB?=gY7jC$7wojKuq*>0@15K-<}kuJy$3$=;8JYX zY|`7?YXu+IR9~;(MVSp_1Rx;O@oFk_)O!_{)1ma!-OvZ%()oiPTmYfMG>c(suYLJE zv>r3Ui_l~Ux4b>)fBj^4<6sv?y4UjosS|EY PhA5rUp z5161WPfz VArWzsAbB-PG}cg6U|Fu~e0J;tX#(iLhGm-vw4;h1d!1p$#wvGH5Gtk9 z+$WTSv=%x|5#9sqrYhgn`EQS)aYX@~zBc$aK^P_BJL|qT8%kkQK=6CzJ7h61of}=( zlcYeDNL#6AkwZlLwokqNipHrqzvR3RukCQ-sW+v+uexpB{&h
c-dDiMqf zztcGPHXfodYIudUHxBv830uJq>TgKZ`D%__?#YK8Q{I$jz0gHwzKUPNxvt-- 5Xn5r_WH&5o2Dxh#6a+{Jusqx%1Rw#c?5v$@F?s{x}u6c zKRyn^x|L_0hm_;c)dq0;9ScEc6Uj iGUiScnr>QJLusRob!!*#pu8rZV>g zEzG7x{8LjWFj_5-d}tPQVEWFAuD=|ArZ#N#(8$Si=m&xS$81Xa`ti_EtmPkw>Fi(b z<*5w(u4bOZR~Tl^HY&%NIh4Coy-Rc6e6{oDJ|91<+OC~->gtkO_c~RxqsAk<*f46v zV@Rrs5O?F}D+3)ErX05c)u66u`jTVc$Je3R$KTb+NTs^NvMxIPvy}ngYS-QFx37Tj zfa2fpxKA%x)7WVD{HHa`hn`IBPv)%G2r#umWJpHN4T*mezpaEY6&|qV$3b*2?O5?q zme$s4vAk7f63Yo*64{c72E;Z2tWF?=fU=Y`=xP47N}mluE{ut&i^gl#E;c?HOCFq` zY@*q7|F(a-J`4rE-$I{FWRcV0n1OMl2h6{x$~)6v6!DTR(V;gHcWYp3pdROFLL_5^ z*hVJwGGY*qz2AE_lvA5N^>%+J)*>du yzR#NsDG%0pbcrNRVZS9@fnZaKf0-E zvy-4>9{}!B)3UIEbZKMtTrFUml#Y>G*I@Vz#!aslRMxk<=%o*4I!J6<9R0QMC+a*N z3|LgKr+&*o8+ok{->3I+-n%b-Jg6^AiEV~*L=JuuE0%v_o|wfh2lo!x;n(C;11P|H z=_OnFcQ@{zC#Dj-Ex7CU#G(JWda;MgaQ8SHgc1fKXcVB264A_m2Lt2r 5j9cJY(`GBe-(&6 z)wB~Aw=C&(eGVqv&`sxiCDMBp>1VcfF7O}hBS3f}%tdPRH@R<{f)e#9P^>+Up@$Ab zI6EDki)}Kfx3t(_6DQ%#gOnm4W(;6eXeX*%6w5UNTm&C=WCAq1Vqp=Yj+Q0E{Q@4y z$F|nw ==Jv<;5pxFPlPTG6Q9nn;pJLObTuzJ z{4X-RGHc_LX1O=}_2_74X$>)W)}flF(}8JZRE2Sr$At2(tG^1s>FKUW8lLETIpGRj zB6~1(L#wBXJ*gTamo11BSUw=gj>{dgR>0@yF(pOuZ#av!yD&LkQlV-q3 N!CbZ)s{yRR@jdk^* zz9w>rPo!6J|0@uDLCE_+4nIIs@yU~6Im9&Ey(y^G2rve>FIC$mo1nHgHteB`04Vy1 z5!^ijZv)AQMPSOiAp(GX=#7U<$oTh{8;T8FZ~lNilkb7v9H_8x<+^G0=1vwW+7P>5 z_Ku|qodiBwc3X#kwyUJy*hoFu4_#8T1L6 z79|d8V{A(bIXKuyEl=r~#SLcZ{o?*)XNtOmFE0Zi#v5)DUi=nz^A<-LeEug@AFGP0 z-;m`X1;6_*Qjl}D3n{}00#sWL-{){9^v;tf*&C^O*4xz34{1S!n49YwLFxu-a^T=E zW^=dS4z(6L{L@$EEd3X `L8o@2p2Kb>hpuZGI6st#++ zHk-$l!@ez~)+WAJ7l71)qA5y%5CIk%s>&*yz=v_3MvM(U<8q=2t>TG5;JpO;%ZHqt zpYVGGLXj$R_wU>CM 6@188~;0nU)#sbSpMmqcYJS>JFxI~x~=MMv*XC&!x~@&K|1*V zdx9@1ff=c_xEYF6E)3Eku>Jq`=&d_K=2s-D-Ui~NhxPJ6%UQ3!$DZ38Wn=?uWir1a zhdI0eFN4cMyRNpu-^+-ErjR^qJ%gb)Xn{t>E=Qd|Rj7)b55~TU&rsWEwhT1C{fXCq zNad+<*&q}~*bx3udHUDi|4=foFD$xC Hd>jgV9U~$l7L01#miD$q5m{r8xy^?K)E<%HYLpeO!`Qag zFiFAqc6*Z`UM@*&H%> trlm2X&ZGJzYX3-3u=uQx|;fQh?X?l0oPS=#grm7yg2tx zD75C&S<1NKrJTIIM0+^{Br%|J991`%3iqq#j!>nKE^suffN#R;i4cb@de{B^nhYcL zr}<4ToIhP`>>o~)L=H68*W0K@ceIn2@Z1*Z>e_U1IQYciL;1g()xozHdS3mc)RaHTVWGL0!w zwa3Wip0DN1Yv$kd^~VOP7WJGa>21`kbw`5xm8pHLa {B*R60J` z^pd}}ZO8dtRrU7eU=;S(e`iU5hv{CY9S$m<4({AJRETiT9&KMpyl1#tE}%zb
~DSY>$LN%p{TzBj8K7oyUX zYoD#!W+hnYu-&wb ^v-_PhNIZ;Q*gr6&{z`a3iidX;0KSO=G;2dU|%WzVG_-`EpgIGDohI`f647 z2Scu7w Jia+zvWa=YMpwJofWvRO!iwQ9T+&O!yUnL{7o*eHtCn*N z&m{>Js9(n8$o&$MS!oVN2FlCNT<~=Q4O-(1k0&v~df{5|eIEO01Iuka^cS~ZSp$Tn zxH)LAsWl(tGWDi2P5DxrfFFDNHY6-!M?DMwG#nf@qofxF?-kY5xZSTcp!+$s*SJ@4 zD)sW{OAFiT8N$G8mIseUawfaWD(1~bG rs$;y?F2f- zNa69+@Bi~sMDP(xAg~S b>eHu+&10 z2V;m3&oca;#q-B!##v s*vbe_wZB?OHB0P?%qb8+=xcUMSas|CkPy7%O$ zl>bP69dJg);|~&Iqw!wYgKwx(zi3872{P-X1UnEv2! zx7~{|Pnt}kk^;QY-nKf6O>|J| D9IsP-x z7snmohLN`HLV=}p$w-SVhSE%yJvW~ArXDoj9N7J}8{r?XN2svrvo+n?1%F@ARdtZv z$mEYu0y;a|z+}T+sG)u}wZ8tQGwVjQ3H@cM!M|_Q;(FLDcfH8sw#wfWvk7B@0DLnP zPayskRR?r;00ARP@F9o!;DuoSmnN%S8jwSx_^+xCu2UOiwNOgU-ASjP4q`NS=8m`g ztu3=0>p)N7U;IAt=WlraNR|(HFq$#{i!M?5Ok$$p`8t<1I)bNDr~U`8mVabgLZ=>A z9K`dd=9`xg?C(|D;>{PJQZeL#`JG^5uj_cV6X+-&;6&MtijJ2%QOCfge6Sm@Rb{O> z`;jR>urXSZKUDtNpx`lelu v|~5*He6`LoR>#251ITDZ{L9xuN64c zP5thwFBW&u{mmo-G&ouZd;3Y+3HqxQ!AOrzoe=)`H=f+v?h;FN8x7}^(BH<(-hSiD zxxMc1CnwPG_{eVkOd;L+G(!0Qxd56$-K{}*^!miws4~>P=Pu=`Nw&L>KAjfAo9~y- z#*Jm=2l$L4Teo%G8T;<=hCoyDc9FT6=WN_B{o-Xpka7f>aWNgpNT+_?c6Y0u8lKlW ztw+J!0oSjjFof{424DLwGN-Ao6htN0I@TPg6(avb7dOXz(#W}Ccc0{D*Nv0;kvp%$ z#D0ZY 29@@CVTiIh_q*oN8@A~(HnM}qgy tu{m@Fdl^Zu`b6xJ^=1J3JC{j-A z5(A8D9}d#aFr;b@FAFP3 g2wbYRO1?r<4%xP(#;VHyWm-E{r{|gN^k+6_)DxY2Nrq=L6@v zUy74K0wF(Y+tL`ErecV>%XZ>-1qA*HcIv$9OS*G{l!LJx_aIl(-(TeRjfm`PWS{kG zHt6t|Se7Kdi4G{VRX{_)iOia7&(X+0m$ly$w`64tbZCZyU27jxFQh;t^HPhxBp+1S ze0O*EfUP)!yx$|0c&U8!Z)2{Ob+^lex0|{f4x!)B$;pZ30cKU?*<^d6SFvj{UGXPA zD0&|o{&5ZcI*>5^tbWl+s3xnH4z?VWK&NOIzDXM&5eg?uG{=J|kbfq{Ns&rW O>!>t7X_ag_kRHAC0jkotH8cg#}_fMcWE+lzpZ#L t&JV;_$_LezmEJE* zO=qE(u~5!8YlOeM#3V5S> ;$NZ}5uG5X?K 0W>| *I4q-o_^o zV)7ii57#WO<1=;_>AL~W$`P-UI|vceBNbVQFpA0CWxm0!og5qUtl#%l>6neNPI9Cj zyL0Ex9xST+Z~{Vne0*HN-Dou0A(A{-pi@9|Gc-i5d(A@ ^A_O94Bk)FCFX z`XiTx?I~I3{ya;$c`DYWP?MNv9rA|Ho-*W3P%rf`T~ceUtdfk%eI8s5=taf8yS#g- zaL5}j*UYy{aIeeZ=uolCw?j(m1cBH)I2d?Qyrg$s@q-L|u1xH=q%cMN)+WflqEJy1 z=*h_@UOi<%X+yCB$qs@4>{kjCxWutv5O{-17veJbvP^HS{GhCK;kdKdcYuSi($bTi zUSSA^#z8#Xe#PMJ?2p8T@wzvVB;|&AOY`ySrwdqn$FdJNpMIB5+3EdVq~GlNZ~hy- zmBRyR8)M_CGE8Iug^Lr+P6X-XluZ+{!w}^ lI6ALs4a4&%@3%U>+*?srK7iTSjnu(MOm23=Gy{#L1S0#@C{*49=_+wuMm z7`Jkne2GfTV!um_{q8O`*u#e%TtM?B?~&?2ZrUY@+riz*dAgy9jN00x{{O=AGdyA% zOwiCNS|a}Hush@7*(N-!O-z9O+)pw3O3St7UC8La&}~`ipMPq=7io1X@@b`f?P8F* zl+8w-85c6!d~X0KeXtypA_R!q5Qu3MRh*W^OZ%;uA;vh3HX@lYehZ|E6*qZ>&h+mD zxcS^Y&}cIbuk_kbn%eSkyXp|ix`Y*RQ?Rqv teAtY#@)ru-2aa#Utm zhY2B6IkWP`?DMZh%m5Mqs?s5`#qNaCM=qu~qdVlDwoLabbx5}Vl^J4XsE7El6HGv6 z`U8!2g9L9dmy|l}zlSx|e9@9=neHQ;s^Z3S?G172*v9($1hiIBKV8R2X0t<~l0AyK zB^nvzLswlT(!_mF52Ynex7wtCQ+ pftKZc)SaZPkI-LVL{6!G0MFSavU8D4(B>?BYQ`+=BsK1{M#fN5?Q4_6{JSa-=Q+@+D%cvdOq%$bU_u%q5K#S=PEyI! B=k6pIp^8X5Uw{_^D( z)zdFW$@UE^7u0lg=a-fw=0qM!G5@TIHl#iwFyl7UcYPQgfMnVX_m>a%lsh{+s~Fri zkiw*#MAJAN2cLc}AzDjLXXlv$e(=pJhOwrLE`_9_;`kN$SpV=5Auh$?63QTCQk NaDjTeXwhPwkZBa}_X=HTB;%CdO&7Gtzd8w$ZJXrCP!-uf) zJLR6_z>pq2+Qkr|A%{P6l!oWu;zGmb85O5hR}8&maOLoi0z9-hSbV0?= #dTr``4cQ zcbMR5Y3_~tZFqq!q9aq)$AU`^KZ-_5fU;&CIM4c44h~aqm6)~DrTxxxRau8de*XNa z2^rG0Hwx8@BBeejMI_h=GQ!snAnGv_d;c99Hzn32>CJP2QTF$^U5@13<59ZRv`#dZ zvYZ6)!GnB#!4K_o9u?U7uns{_;@emV+bQ3*pgUaw5b5(kFwDclLkn@#Hkc&x!fG%K zQ%fBBe#`x-cEYUi3iz^lQWaG?+{Ez9D0e0eUCc$OyB}fZohoa68s-X%U+!dl `g*QHd&Rd{N6s_$Is(_ z{)o@L_uPBVdB0z;=j%DVqR`!gsjQd311uXm;x*zWQ_r%lEp%U_UFzj)ARg-pIr8zf zdF6gk0HtxvyP|&8clzy5b_*7`pPWBqjy=|XPRE%YJ@cMvca6bmJ;~IXK>c?4qePD% z@=`FNOoB+M=xPoAxv8l?{H6_u$D+r}fVXawmX)Pmb_e*11Ol!1{GB4>XP~tYcG{XM zvk_&aSO#<2vJ#WBO{MUEXQMz1bl6(I5V(fjYN|M&vH;}5Ayq*Y6%~aS2?+^T;_Svj zf)?;yIY`)w+zm`Ri8(ou-*HX<2J*B3<=Dcb5sz{3b=d6NP7=kU`xiMty0h$V>(Mu5 zettl3sHdf+&1u;v$f(Ov3OpcQ 4uL5z^jh8rc*S8gqW{D)kepjsXdT@I6g zM4-`xJh1XD@E&WY9Y<<5q(iTEDV8G9eO?^5MEE&-iU4fArIhd|0z%kTvqm!gCv@RR zZTPy7_9PMU#__xFDk3D%3lX}j fn*$v f647o?uE3B_ zL)5s29c9wkb?{?w$Vb35YaY&2J`B0zb6Dusj{*dGd$1h(h$Oj)F;d8uZzY3{uY0#) zIpLJ5GopSW?DU4m`N<9%^}y9trxi#W_d~8;YiugpSxt3D>VV&snMC`lyncu4?}`OK z=4GT1NCEhZj?Qz(+1SR$kWo2|l=RiBMrD(WV_&|^f?Mwxc*{3+cXzK!cT4&HJB!VD z*c#ZpthD#l_5Hb8mVUkW(DKO)Gc&V*-ncGKH0k@qj~^u#b#9Fp;FcwV5L` If-f%CI-`M?@;8QBj7m(Z8)2qakk^ zbEGoLNJ0gWP!b+&g>$MjwV9D*q-?h%tZBnnjR^Ag?q?i5TZGle-jA *9F?_|=JGw8(}^LO!8k zar6;$6JIASMh%bqr8fK;CA@HmrRkU3!;ZNYZC!^z } z=A%zT6&btsDexCh7SB%1;k5C0(tK62ZWoTH8LK2YyU-jtX3!_0i;}hZWIFsr*%duL z z9m$&P?h)1sNG74;6PZ3aVO?lmWvX+Y1{NM;q4{jCN-P82=idM9iQVg7 zp%t8MH?>E%?l{E@dFm)oh}D1xRaB4CngC|tX<9)SwFf%#9(*hfth;rkN_d<_vg55? z)~m7!UAoU YFyMRL8* fL#D|ujSW6-yQx`uzb9uyy?q7f12NkXR_81;> z`I$)9z!8LZNX6-1o?7s^Gr||9rs^L0y8Tj~Kh#cfg+R7JHLf9(wmr5SRCWPvN~Pra zn>6+MX0WOX5vfWE!@6Z6DdQkP^b#)5Pp-iElLo;XIi3{Z`~f*VN(>V0c$v~s#7wND zc${o^oSB#t1FF%PE!jDp*)fg*$;yv=qE^?g{6e~0uTx44Jh~fL-7utiOO<>=V^EF0 z3MgKU`T5LfFkPJR{ei_8JA2&+p4ZsaL>;LZb9oM#*Hg{l_F4-wQdF`sSooEj4bZ%< z?Noto0$(c*YtUjuEM2y@moS^ O&IkRN1sjrM>}O#O05Bo5>v{VRFAPhL>z z>*=+e{jbYtJ PbUpAYB zDwGv^cU(*aY4F_`s|5GDI4BSe0J7u&h%h?=nE %JG*-#Fmts-Bb$NLIJ5WHu{mTv7RS;!O zO-n;_^&woVs;JSq9$?a!!G-^8S+dg8gN`rz<`wUXsvaU*wTkJwojGu)pM1HTZ-|3C zMsioM=5mC5`K4;=O`~sW7`Kb!EyYA8h5z!1l01{{))aP3ccOYyCN8%YB`tuHPJkf# ztK6*K7DvfP`2f1T4#Q}ksUxYr@#2zU9~HG27S^pSOL#xZyF0a4S(X~y_2V^4_Y1CG z3o0co)(HEgH^kE3^GP#ii0|>psEm>#(XarNEC~%PgX19xwrBL$ul7%f-vF(tCR|H| z f4?2I9yNez7mg5d<7&p>w-TnI z%bsixlqR93w{G>UG3 %I KcG7GY}5y@y{3_@OqyZN>o);u3lVz?35{jOESP`r|KiEp3PuD@>m}A8~F8i zbN?qOOJa~OWj~Tdd&4CsF$Cr=0^RWC2$-Q(<65A#*?KRXIa-N`n2l}t2&jXdmNYbV z0b&px*%mNY&Wn{AmlMar^5VR8g=F &z;EvO1i zJs*!o?3&fOOtwIDZssjq{5#?XiLMuq&n*A_ec5@7lskC4GMoh3$MLGy;m;s&Xm An`tOc-{ah9Q>)tQG-ZY>sFArf=a@W;$YHoxOA|>PP)ggyj z1yqhXaPFp84v!Bk9=7 xiQ zUU`2vdk73${Qg@K0i6yQH=ykJFIM_fY#ibov4RI+v{P;K58zUy5Krf`F#|}sgz R6+%o;*!VS|pdN=rNCmG5Lu(c6ghdd*yY3fQNNBk? zg0uICh4`ZdiMxr(5tewIuhlewwD;8vCR`?;EjQ30h)qk9DBfwj=Z&mM@#HLArC2)2 zUQ^a#l{RfC-#TqAJnDTH&8m<^M+B|11wyJb!BkVxKXmb%0)XroM_Q{po>F)UpzK>* zfj=s1IbD~4lz-TH#nY@QQ4Gw57yfoD{sYSg7YaW%^6&hVwx+I*ISFlOW0S+q%BtK5 z%BHCDEt9^LgY_LND>6C=a44Hrl;a!9R9E!@G}&G6XireXe*pryM+mI#-Q{BJo?3&a zUBeduJw!^!nAe(j!@c^e%mCl->tV<{ WC)Lh0Xz5Zu2)FWp?Uc9gMGYpM^p^b~jc2 z0?$zcyEr>gXp$`eF=0NCn$}x4dOHHZ;Bwjf$jV*({(aj%JNT#zYlp`+J ;xv-5>FwW_?ZoeKNaGtPo%Fhl#S46fC(KZJrV|4gtFWK zI5?TS*^M4?F&HyFy*EErlCC<=cK$d~rdvo5=cyHS4y|0&$H^F?oh74 FBq8femp zNUty3Vx`JNkVH)eE(%Dy*b)8{9ATI%MJ}M`$QWg)q5JwcTTlvNEDXqmHv*7SH(6e< z5Hs{Cli_io>+WYl^2Ajdqk~#^jZmFoniLK8eSTI?V7d-4?_)SZ>^cwG$OrR}IXBtM zogY59gyJJ;v{Pt=3?U*y6>gy5Q&d+!uYz648FFYc6oQQQ)PRXlpWl4+%a<>6=Dm72 zF8~T5--Of<$QsH7Um` lo) zuD(A^Gaoo@H4A# -=>^ohCVKmvT|_v5 ej^v@E5lxHdE}U>tT{Vw3gtL!gfb7zm~T zP&uR^? P<6eWX2B7*qRU3|~2D-B^AmifC*_>szEGa Ha#1xKhvvIN)OfhMvwv4o63XrGCTRE*7yHhfHtN8B b!q+>SBXiw7%x1+f<2DMxSe*yp*Bqw!-xsys4c)D@6A+i{12k 3wO}RD8=i>=%xe bvSB~pto=Cd@N$u#W-8xOk{>=&TcVgg z{J=BK9i-7gc~uyX(_a(C qDc^4}b_H4`y=?hyiVIxMx(j=^QodwF?F zd%*qfynfxrjQr6lVC#oQ38nO7#LLPwGbY{YmO&SLtKNNfYFURo_J*3eQouj)q0|=4 z+K{`c)BWev@zmsaNFV-H2CDE5G&hpm4p7wR08moY*r +^pS*r&j1L}SAdxF>4!4Ga>Vt~E*owC<)n?@G_o{&ILo za@6u@f&KVb;k6cpEd5KFHuLv=rPr;Jj1hNJGep1aE8T+H`a9K`EsP{8{b^pv@{SgI z TJ});?^YZbu hSL1Qw+)q{j-$4V z^RB6C74e1n>Iqc~qE=EKy~NGl@?)y9C?}kku9QN99pD8hQk980rNZnzJxJwE=lHN= zVB@Dl3JIiocbknQq#1TX0~p8U?Cs=OC|kr@L`X>R5R5K_H;yp$iWAP8Bu}uQu{iO% z5#l+Q0^utt1iRLVLKVS;pt+yPuo@?=g}^_U2gcdtF?pnJRHA$rtooVelZPS0N9s!y zh_{mJ3r8(LF0>yy?88wn5E)P=5W2vU%@X3r(Q_0!?8SNO1WjSkpqX!a4M#yTTySN_ z!W6Q;Tu(a@2}Il1Z2+C)c;b$MIt}bs=#=O^9H{(N{pa@GquBR>QQ8`alwDok4;TXb zW>ZtsWUaY7NOJY~^1-oR`~oOa{~5$pHdz6~tWYSXy0TK*NQe<|*@N_@VlKTl3l-eI zec+#+a5&BGVPj+ayjiCJV&a6;t4cn9{P=N2UeR|#klxF_M@-6{a+-g$0!JV7L{bTl zo~d`sAcBBSQIV_u_N^XsA`BH}7s)+QQ-? ~Qg1+5s^ 5i9Ln3**gOQhWLYo5;VXxgyr@k71m4BmP8W*Hw$%BdExCk^NrIhPcs{AJGH z5kh#zr7r$NmPT%IrHPw(Ext*v@tc{NH%afu?dMj=zO!8;XzVu|xp;yh{wy^J7t)9$ zxtWip*EiJo*H_`7DNI7VtU(G3!e}~)Q_<3Ogej$wcbmERNk<$ITHh+l{ewC|2SE{r zsM{PAtfBns3x`&PCM6UI4vpMQ^D+@8q2WWh90vRNJBMluC3j1h5Li*HXNyLmQ1Oxi zKi=1JdLIj5ITgCc-|1Q2zBQJCiIxXw D$Tf$Kol zbCzzGzRv(;d9;f2zK7G`EPR+49rb#Frtj_%OxT%gJQRgv0#0}8PmjWW`h>r1^LA{- z*VY 3JNf`E_0k+$&C<&f-JeO=wQ2DEc;Z%>Dl({&egto1o_VYBWRgGH05%eDsnBA!ch z{)hDojBm*A)bM|YIqxkP`<+K(g)$Mwf|5>w$c`DgL9>cC#N7b4w%1f=0(Vc0boXOV zDTpAb?Yo6&yMn0*keNOV47%ib1B+@}tSgV1>6bLbIB+qGFl@~b)b0lIt{ji96eQwR z*hn}l!VrCoMr}OFPQZ=bLEXke3`ag25ejXP5Bni^Z=H2NwLZOx@>cLKs|HB7rZ0^_cE|tbhdsC{^SR^#Jege=J zSY6!QEREGKLC4|4a+klC75YOE)A7L-uJvD&Rxo>CYDlJo>6q-|Kr%dJbs()?X2X{k z@^Nu-(Fo5tF*#YA@ZtscY5x _44ms1N5|%abkx6D1+Zkj#(la1uN~&U*7}Cn zTeb%L=%U*=t=-tz(3vUN-gJ;j;=^hH*tSLLnj}DO7C6oatRC&0q!i}-YX(;+ClKl` zLlNtsr67SM=452dRDoq|_^iL}tdWaST|p7-K`fj-_a3-`@%8qX%C^`9LA)G;mGwLG zGOo#Q`iTUc#Q7C1#O+wwq1vx0GToj+<;!-I)S@sNG_ROI7x8P2t^q34y;K+42 3pyuCpb;muLF3eGy%2}F`u)C{20ObM`Z z-p~!oVIP3EA7`)KEuO4e3(Ve9s;!{99%NT1_D Y{_f8&UOn|&TkI+^l9ozMfG2SJ ztjqr2C3I4MiRk<{)3-VQZF2#VwLCd`W J{n@a5bs?p~ zZagNr=?14N;1u~beB(OX%lWsl#O~3sbXZ+oecz*br|uf?{7VB)N@pK1J729#%Li!M z{I_e{D#b(#d+b3CX#-#>O!@LaN(Y)rJw^pL>8|Q4a&?nn1O42PApU`d%$+`=@g2F2 z#Ak%WU*Uc5>YZHQE8A}|Qr(2vLP9TW{pZRlpM6hjZ?v<=LU}ZlKF1jiHOA7t!5Q&F z>!mayz&pB#n>zCCoc*}$!V!Ko5@ZCxBIVy-!T;h}3<_oX711s7d5Y{KpV}>vmBw<5 z*XSVhiFL*2=mV?iB63ru?A^u{bf6|N;;#7KGeQ_W!T2_=Kk1bq7Wgxh!WeTlO*pf6 z1GAx SqYQ0 z0rDUhO*75vWq-$V(I0+%9|C>;h6(H&q6Xzw^>wEQzgBzzEpGcgSJ>Z+A`{U!wT)fg z>a^cf{9J-U@84f-@!8dz=wevCm>I8LJ!|x-1zyC}jg73_;mdya{ai&=eNJ>o<#g+s z+~w;T$Soz9@q{FLgW`-3TNm$+>k%5e&2s*|Wy {C5jp3;?t|$Z*pvmXmpA`BAwWU_!fitafwOZ4(?B%di(jmTbKz=m{hH6s z=7Ucz6$Ck_XKM_jjLm<7n8!D +70lH0PT<6|gc{-jqc zXy<666F4 alIPAz-5btU zmK~j%N}g$Qcl|!s=yQe30K9&&6&rg$7`Pe?NQwC2$R LM)ON2OHrZQ#PZx z$6~ShGqPz0j&Lx-!k>4Q!aS+^gcJh|od90HL1@_B*bA4JMr7I|HHER{1t<~Q7(4nn zcn9_hR*1MZ5(<$LiiUM-lYEe+XnAw |J5}ZiD_HLpG@Pz@Co>E~w01Pg{343LF!bCpwt9 zTVcq*-sFkC%QEw&x+q~Lp}QiW@Z&B-pq!(uS#%yww7EqPR%QyADzBx1PYbw6XcEz+ zDS*(qTn7VG-Zc&ZLBS`A55Lx^Tjksao%IkeRu>EVlI-}&UK(g%C(;0p=iYqvFm2#; zw@uRAq@HaVlky=snR&aRJY=h^z7Nbe!Ay=^O LAUoL~yd6!-ch*1Fxi1%OI4{N!j2$`NQF#8m>y4Vvs_uTW zg-0dz(Eg`SST}bIJwb_hRWQKE+zLO;I0HVy1|am+U8mMO+8h6!csr(8UO|Cxt^CK@ zaO>&MSTF~snf9{-KdV5nyk xBn zJmsjyT~?)PyJ`jB-hN>9luge2z7|v$7~^21MV(~XaJtyv_l#4n!cg&v@0_3rAi)%R zr^|yvVae@q(nN?GFeD=Z?3$1>4;nR66u{3P!F+HVaHVta?7=aK=o4a0DI^4sE5seB zBRDor3GFx>K0_SfT;#%dD7h2p*o;ahMeJ_TOdqP526Pme6t95O><4an%W1!8GgRBH ziP5izUeTU@!L$hM(-@n)V>}b>Iz~Q{yzY{c@)!8MER +A3c=17TFYtLmwZsoh zAM5`7S)Fb6FtEx_|AShoGGM)qwN+D5F>jVp-Fo+Bc ze0Te8_Es@y9+x{#cU6H8hqXwSefHWZIE?JICXwmJZ%B9{K?G<}QD8Lh jQATP4Q(Qd^r(%AR8kSHNEqdbkPoLf z9Y?7r9TY}R8GLTb?9WGRm_EG<(I7`qVQxezT*ryxrFd>gAsJy;;Fw}u2L%L!eTSnL z#xtwu?~ze1*-p}6k>dEDvh4oEcMvSZa9)l&SCTmC^@6g;0c<#yItyw^3P`RRU5)q_*4>ia=` l%+0QnU&9zNN-I{I-!BI3#zyy>6I&WC zGDXW<3v}BFeuCt`v-4!Y=#t|(cyB~H)=5#zY+(Ur2QkD- H435eGy=^B zyUP29aIZa=(S)J`9Rb7IGCF*bLPtOdXaZ$#CZo8Fp=ikS#r(IsWJN(XC>xU8NC>Wj zn0NT_X6l$JTZludJ^*faPq`pMMQ;y?CTn)d4c_71n%_T0xrV=q_)!iS>2aAB(^`qU zUp3=q0Iq2XMv5N5XEFt%M$m4lmv0U6#@UW{>m9LBM1!$*uPHFXy;@sQ;pw~uh_s+9 zix0{+{hryQyyh>GRlZQ;j5)>YEO!mh`I%Z==tyQgeDFXUByoGB3zX Pi9H!bC7MlMaqNTC^=Di$WQaqWSQ$AeL;JDP?O{x@nQd2F}W>_-m z^2WRjSUOi9f-tJ;e~O?8{jxb(7W-%+^kI#Z;-fCV`KqlZew4G??8L;s)uKDY)02~B z*{P`kwSWu^zJ=mrkO~uOeVZin@W-!TzHI=fSN8|AT})6=lZ|583+DW=OS89evuf-$ zyPu+a{!cwVGL 7?%UXc1_`x7-utLh8QU)wx>|AnGdAd znGs`Rr0pYMMif#AEXh5;8;HFau$PCj{&Glqyb8lj1m%8grc7Bz6Y~;7ZyTaw!imaz zj!2LM2yr}pD-P`FT`6O~Ll=@z$6>j-%&oto@A!yhuQehY^vhO_!NSf_Xu@RgWpNj} zq>^xlQVF3rLZn>I5cAMl! ltnpqbN%9&`^F>(&~rtC4kEfZCwSAZ06RQ}6z z{MZ8^Xc<#U27CIq$w|M9iHV5^fDAP-JUBSOLh-usccUu!=VX Q`xLyxE6=hhVlU+L@KC@^fq 4{a$5N%7cE =FToIF4w?t@34vDl5Wt&5Nmm5n;8PR#A&M}{>0a VDQP7|>du;u0t~3kWruX*$aDanhZdvy@sw@ Q3LBIu$_8pWp*HMeg>Wffo&YD6yifA&Zj=6?AJLm86RRK5E(ot3 zfW_KTSQjp_5Infe#EEkh6B8S_=yjTYc5dC=M>NK8o#^GiA$UK~a&RD_ZnGYeN&Th- zMm=9^k0$u(fkdhwP(&+1O+XQS>^v^_UA{;7e8uMKUmF61jH;}KtFwD8E8f$?vtm0K zY1#PdQE=OrSvj8-3h0tul$TKQGoGla(r-V0;KIlC6xo2g8tjE2IAa=WTx4-{sRiT^ zNUb^bTVq48s3)N`i;S@jR-C{X-np=`F*dal-USpF*^pZeAK^*yzkiP$Rks340F@xF zk~%A^Zci;0&Mm1Zr;wV9yO6v%7`Sj{)B0fI0~lH|?4VpeK0an0Y6Mwf`m{C;d-Naa z_ddBdw^&NN5W#~$T52Fyo@O&R2R2%ubkmV|e=#O+tF}Eo(4oL#v A%0SF^q)+aJtRWEZ8>o`57BcSNA;JB zJVK+lD|+&J6{#I0nh>c&5b=Ugzvp_pKe?s}Fg;s%iEe5Y