From 1b7189d63da82f75b725e7ac8b474ccf222d39f5 Mon Sep 17 00:00:00 2001 From: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com> Date: Thu, 15 Oct 2020 10:30:50 -0400 Subject: [PATCH] Arcononomy: Personal Departmental Cargo Ordering (#53881) Alright crew, here's the 4-11. This adds a new Modular Computer app, that works functionally identically to the cargo console. but before we delve into that, lets hit the adjacent aspects first. Cargo Packs now contain a new variable, access_view, that is only applied to cargo packs viewed in this app. It determines the access level required to be able to see those individual packs, in the same way that you need certain accesses to open certain crates anyway. This means that outside of certain inter-departmental crates that see overlap in who can/should be able to order it, heads can browse and purchase crates based on their department's needs and wants. The cargo ordering console has been renamed on the DM side. Because now that there's another, similar cargo ordering DM that was going to get confusing fast, as just calling it "Console" gets on my nerves and is harder to spot on VSC for me and everyone going forward forever. Cool, back to buying stuff. heads of staff can download the cargo ordering app on tablets and laptops only, and it gives them access to purchase cargo using their department funds. These purchases are made against the user's department budget, and enables purchasing supplies with cargo without needing to beg them to use their money on your junk, adding it fully to the cargo shuttle's next load, while still giving cargo the first right to refusal if they wanted to, for some reason. From there on out, cargo's responsibility is primarily getting the goods you bought to you, which is technically already their job!. --- .../crates_lockers/crates/secure.dm | 11 +- code/modules/cargo/goodies.dm | 9 + .../cargo/{console.dm => orderconsole.dm} | 8 + code/modules/cargo/packs.dm | 69 +++++ .../jobs/job_types/chief_medical_officer.dm | 2 +- .../jobs/job_types/head_of_security.dm | 2 +- .../computers/item/tablet_presets.dm | 2 + .../file_system/programs/budgetordering.dm | 280 ++++++++++++++++++ icons/obj/modular_tablet.dmi | Bin 19391 -> 18695 bytes tgstation.dme | 3 +- tgui/packages/tgui/interfaces/Cargo.js | 123 ++++---- tgui/packages/tgui/interfaces/NtosCargo.js | 15 + tgui/public/tgui-common.chunk.js | 2 +- tgui/public/tgui-panel.bundle.js | 2 +- tgui/public/tgui.bundle.js | 2 +- 15 files changed, 470 insertions(+), 60 deletions(-) rename code/modules/cargo/{console.dm => orderconsole.dm} (96%) create mode 100644 code/modules/modular_computers/file_system/programs/budgetordering.dm create mode 100644 tgui/packages/tgui/interfaces/NtosCargo.js diff --git a/code/game/objects/structures/crates_lockers/crates/secure.dm b/code/game/objects/structures/crates_lockers/crates/secure.dm index a336567abc9..d12b5944c45 100644 --- a/code/game/objects/structures/crates_lockers/crates/secure.dm +++ b/code/game/objects/structures/crates_lockers/crates/secure.dm @@ -68,8 +68,14 @@ name = "private crate" desc = "A crate cover designed to only open for who purchased its contents." icon_state = "privatecrate" + ///Account of the person buying the crate if private purchasing. var/datum/bank_account/buyer_account + ///Department of the person buying the crate if buying via the NIRN app. + var/datum/bank_account/department/department_account + ///Is the secure crate opened or closed? var/privacy_lock = TRUE + ///Is the crate being bought by a person, or a budget card? + var/department_purchase = FALSE /obj/structure/closet/crate/secure/owned/examine(mob/user) . = ..() @@ -78,6 +84,9 @@ /obj/structure/closet/crate/secure/owned/Initialize(mapload, datum/bank_account/_buyer_account) . = ..() buyer_account = _buyer_account + if(istype(buyer_account, /datum/bank_account/department)) + department_purchase = TRUE + department_account = buyer_account /obj/structure/closet/crate/secure/owned/togglelock(mob/living/user, silent) if(privacy_lock) @@ -85,7 +94,7 @@ var/obj/item/card/id/id_card = user.get_idcard(TRUE) if(id_card) if(id_card.registered_account) - if(id_card.registered_account == buyer_account) + if(id_card.registered_account == buyer_account || (department_purchase && (id_card.registered_account?.account_job?.paycheck_department) == (department_account.department_id))) if(iscarbon(user)) add_fingerprint(user) locked = !locked diff --git a/code/modules/cargo/goodies.dm b/code/modules/cargo/goodies.dm index 892ed7292aa..3cc1f7f48cb 100644 --- a/code/modules/cargo/goodies.dm +++ b/code/modules/cargo/goodies.dm @@ -8,24 +8,28 @@ name = ".38 DumDum Speedloader" desc = "Contains one speedloader of .38 DumDum ammunition, good for embedding in soft targets." cost = 350 + access_view = ACCESS_BRIG contains = list(/obj/item/ammo_box/c38/dumdum) /datum/supply_pack/goody/match38 name = ".38 Match Grade Speedloader" desc = "Contains one speedloader of match grade .38 ammunition, perfect for showing off trickshots." cost = 350 + access_view = ACCESS_BRIG contains = list(/obj/item/ammo_box/c38/match) /datum/supply_pack/goody/rubber name = ".38 Rubber Speedloader" desc = "Contains one speedloader of bouncy rubber .38 ammunition, for when you want to bounce your shots off anything and everything." cost = 350 + access_view = ACCESS_BRIG contains = list(/obj/item/ammo_box/c38/match/bouncy) /datum/supply_pack/goody/stingbang name = "Stingbang Single-Pack" desc = "Contains one \"stingbang\" grenade, perfect for playing meanhearted pranks." cost = 700 + access_view = ACCESS_BRIG contains = list(/obj/item/grenade/stingbang) /datum/supply_pack/goody/combatknives_single @@ -38,30 +42,35 @@ name = "Combat Shotgun Single-Pack" desc = "For when the enemy absolutely needs to be replaced with lead. Contains one Aussec-designed Combat Shotgun, and one Shotgun Bandolier." cost = 4000 + access_view = ACCESS_ARMORY contains = list(/obj/item/gun/ballistic/shotgun/automatic/combat, /obj/item/storage/belt/bandolier) /datum/supply_pack/goody/energy_single name = "Energy Gun Single-Pack" desc = "Contains one energy gun, capable of firing both nonlethal and lethal blasts of light." cost = 1500 + access_view = ACCESS_ARMORY contains = list(/obj/item/gun/energy/e_gun) /datum/supply_pack/goody/hell_single name = "Hellgun Single-Pack" desc = "Contains one hellgun, an old pattern of laser gun infamous for its ability to horribly disfigure targets with burns. Technically violates the Space Geneva Convention when used on humanoids." cost = 2250 + access_view = ACCESS_ARMORY contains = list(/obj/item/gun/energy/laser/hellgun) /datum/supply_pack/goody/wt550_single name = "WT-550 Auto Rifle Single-Pack" desc = "Contains one high-powered, semiautomatic rifles chambered in 4.6x30mm." // "high-powered" lol yea right cost = 2000 + access_view = ACCESS_ARMORY contains = list(/obj/item/gun/ballistic/automatic/wt550) /datum/supply_pack/goody/wt550ammo_single name = "WT-550 Auto Rifle Ammo Single-Pack" desc = "Contains a 20-round magazine for the WT-550 Auto Rifle. Each magazine is designed to facilitate rapid tactical reloads." cost = 900 + access_view = ACCESS_ARMORY contains = list(/obj/item/ammo_box/magazine/wt550m9) /datum/supply_pack/goody/sologamermitts diff --git a/code/modules/cargo/console.dm b/code/modules/cargo/orderconsole.dm similarity index 96% rename from code/modules/cargo/console.dm rename to code/modules/cargo/orderconsole.dm index ac95cf078c7..132dd64b351 100644 --- a/code/modules/cargo/console.dm +++ b/code/modules/cargo/orderconsole.dm @@ -5,7 +5,12 @@ circuit = /obj/item/circuitboard/computer/cargo light_color = COLOR_BRIGHT_ORANGE + ///Can the supply console send the shuttle back and forth? Used in the UI backend. + var/can_send = TRUE + ///Can this console only send requests? var/requestonly = FALSE + ///Can you approve requests placed for cargo? Works differently between the app and the computer. + var/can_approve_requests = TRUE var/contraband = FALSE var/self_paid = FALSE var/safety_warning = "For safety reasons, the automated supply shuttle \ @@ -24,6 +29,8 @@ desc = "Used to request supplies from cargo." icon_screen = "request" circuit = /obj/item/circuitboard/computer/cargo/request + can_send = FALSE + can_approve_requests = FALSE requestonly = TRUE /obj/machinery/computer/cargo/Initialize() @@ -78,6 +85,7 @@ data["docked"] = SSshuttle.supply.mode == SHUTTLE_IDLE data["loan"] = !!SSshuttle.shuttle_loan data["loan_dispatched"] = SSshuttle.shuttle_loan && SSshuttle.shuttle_loan.dispatched + data["can_send"] = can_send var/message = "Remember to stamp and send back the supply manifests." if(SSshuttle.centcom_message) message = SSshuttle.centcom_message diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm index fd438a19f10..74cc72e30d3 100644 --- a/code/modules/cargo/packs.dm +++ b/code/modules/cargo/packs.dm @@ -5,6 +5,7 @@ var/contraband = FALSE var/cost = 700 // Minimum cost, or infinite points are possible. var/access = FALSE + var/access_view = FALSE var/access_any = FALSE var/list/contains = null var/crate_name = "crate" @@ -259,6 +260,7 @@ name = "Ammo Crate" desc = "Contains two 20-round magazines for the WT-550 Auto Rifle, three boxes of buckshot ammo, three boxes of rubber ammo and special .38 speedloarders. Requires Security access to open." cost = 2500 + access_view = ACCESS_ARMORY contains = list(/obj/item/ammo_box/magazine/wt550m9, /obj/item/ammo_box/magazine/wt550m9, /obj/item/storage/box/lethalshot, @@ -276,6 +278,7 @@ name = "Armor Crate" desc = "Three vests of well-rounded, decently-protective armor. Requires Security access to open." cost = 1000 + access_view = ACCESS_SECURITY contains = list(/obj/item/clothing/suit/armor/vest, /obj/item/clothing/suit/armor/vest, /obj/item/clothing/suit/armor/vest) @@ -285,6 +288,7 @@ name = "Disabler Crate" desc = "Three stamina-draining disabler weapons. Requires Security access to open." cost = 1500 + access_view = ACCESS_SECURITY contains = list(/obj/item/gun/energy/disabler, /obj/item/gun/energy/disabler, /obj/item/gun/energy/disabler) @@ -294,6 +298,7 @@ name = "Forensics Crate" desc = "Stay hot on the criminal's heels with Nanotrasen's Detective Essentials(tm). Contains a forensics scanner, six evidence bags, camera, tape recorder, white crayon, and of course, a fedora. Requires Security access to open." cost = 2000 + access_view = ACCESS_MORGUE contains = list(/obj/item/detective_scanner, /obj/item/storage/box/evidence, /obj/item/camera, @@ -315,6 +320,7 @@ name = "Lasers Crate" desc = "Contains three lethal, high-energy laser guns. Requires Security access to open." cost = 2000 + access_view = ACCESS_ARMORY contains = list(/obj/item/gun/energy/laser, /obj/item/gun/energy/laser, /obj/item/gun/energy/laser) @@ -325,6 +331,7 @@ /datum/supply_pack/security/securitybarriers name = "Security Barrier Grenades" desc = "Stem the tide with four Security Barrier grenades. Requires Security access to open." + access_view = ACCESS_BRIG contains = list(/obj/item/grenade/barrier, /obj/item/grenade/barrier, /obj/item/grenade/barrier, @@ -336,6 +343,7 @@ name = "Security Clothing Crate" desc = "Contains appropriate outfits for the station's private security force. Contains outfits for the Warden, Head of Security, and two Security Officers. Each outfit comes with a rank-appropriate jumpsuit, suit, and beret. Requires Security access to open." cost = 3000 + access_view = ACCESS_SECURITY contains = list(/obj/item/clothing/under/rank/security/officer/formal, /obj/item/clothing/under/rank/security/officer/formal, /obj/item/clothing/suit/security/officer, @@ -354,6 +362,7 @@ name = "Stingbang Grenade Pack" desc = "Contains five \"stingbang\" grenades, perfect for stopping riots and playing morally unthinkable pranks. Requires Security access to open." cost = 2500 + access_view = ACCESS_ARMORY contains = list(/obj/item/storage/box/stingbangs) crate_name = "stingbang grenade pack crate" @@ -361,6 +370,7 @@ name = "Security Supplies Crate" desc = "Contains seven flashbangs, seven teargas grenades, six flashes, and seven handcuffs. Requires Security access to open." cost = 1000 + access_view = ACCESS_ARMORY contains = list(/obj/item/storage/box/flashbangs, /obj/item/storage/box/teargas, /obj/item/storage/box/flashes, @@ -371,6 +381,7 @@ name = "Standard Firing Pins Crate" desc = "Upgrade your arsenal with 10 standard firing pins. Requires Security access to open." cost = 2000 + access_view = ACCESS_ARMORY contains = list(/obj/item/storage/box/firingpins, /obj/item/storage/box/firingpins) crate_name = "firing pins crate" @@ -379,6 +390,7 @@ name = "Paywall Firing Pins Crate" desc = "Specialized firing pins with a built-in configurable paywall. Requires Security access to open." cost = 2500 + access_view = ACCESS_ARMORY contains = list(/obj/item/storage/box/firingpins/paywall, /obj/item/storage/box/firingpins/paywall) crate_name = "paywall firing pins crate" @@ -396,6 +408,7 @@ name = "Stun Batons Crate" desc = "Arm the Civil Protection Forces with three stun batons. Batteries included. Requires Security access to open." cost = 1000 + access_view = ACCESS_SECURITY contains = list(/obj/item/melee/baton/loaded, /obj/item/melee/baton/loaded, /obj/item/melee/baton/loaded) @@ -429,6 +442,7 @@ /datum/supply_pack/security/armory group = "Armory" access = ACCESS_ARMORY + access_view = ACCESS_ARMORY crate_type = /obj/structure/closet/crate/secure/weapon /datum/supply_pack/security/armory/bulletarmor @@ -643,6 +657,7 @@ name = "Anti-breach Shield Projector Crate" desc = "Hull breaches again? Say no more with the Nanotrasen Anti-Breach Shield Projector! Uses forcefield technology to keep the air in, and the space out. Contains two shield projectors." cost = 2500 + access_view = ACCESS_ENGINE_EQUIP contains = list(/obj/machinery/shieldgen, /obj/machinery/shieldgen) crate_name = "anti-breach shield projector crate" @@ -651,6 +666,7 @@ name = "APLU MK-I Crate" desc = "A do-it-yourself kit for building an ALPU MK-I \"Ripley\", designed for lifting and carrying heavy equipment, and other station tasks. Batteries not included." cost = 2000 + access_view = ACCESS_ROBOTICS contains = list(/obj/item/mecha_parts/chassis/ripley, /obj/item/mecha_parts/part/ripley_torso, /obj/item/mecha_parts/part/ripley_right_arm, @@ -678,6 +694,7 @@ name = "Engineering Gear Crate" desc = "Gear up with three toolbelts, high-visibility vests, welding helmets, hardhats, and two pairs of meson goggles!" cost = 1300 + access_view = ACCESS_ENGINE contains = list(/obj/item/storage/belt/utility, /obj/item/storage/belt/utility, /obj/item/storage/belt/utility, @@ -698,6 +715,7 @@ name = "Insulated Gloves Crate" desc = "The backbone of modern society. Barely ever ordered for actual engineering. Contains three insulated gloves." cost = 2000 //Made of pure-grade bullshittinium + access_view = ACCESS_ENGINE_EQUIP contains = list(/obj/item/clothing/gloves/color/yellow, /obj/item/clothing/gloves/color/yellow, /obj/item/clothing/gloves/color/yellow) @@ -720,6 +738,7 @@ name = "P.A.C.M.A.N Generator Crate" desc = "Engineers can't set up the engine? Not an issue for you, once you get your hands on this P.A.C.M.A.N. Generator! Takes in plasma and spits out sweet sweet energy." cost = 2500 + access_view = ACCESS_ENGINE contains = list(/obj/machinery/power/port_gen/pacman) crate_name = "PACMAN generator crate" crate_type = /obj/structure/closet/crate/engineering/electrical @@ -739,6 +758,7 @@ desc = "Through advanced bluespace-shenanigans, our engineers have managed to fit an entire shuttle engine into one tiny little crate. Requires CE access to open." cost = 5000 access = ACCESS_CE + access_view = ACCESS_CE contains = list(/obj/structure/shuttle/engine/propulsion/burst/cargo) crate_name = "shuttle engine crate" crate_type = /obj/structure/closet/crate/secure/engineering @@ -747,6 +767,7 @@ /datum/supply_pack/engineering/tools name = "Toolbox Crate" desc = "Any robust spaceman is never far from their trusty toolbox. Contains three electrical toolboxes and three mechanical toolboxes." + access_view = ACCESS_ENGINE_EQUIP contains = list(/obj/item/storage/toolbox/electrical, /obj/item/storage/toolbox/electrical, /obj/item/storage/toolbox/electrical, @@ -760,6 +781,7 @@ name = "Portable Air Pump Crate" desc = "Did someone let the air out of the shuttle again? We've got you covered. Contains two portable air pumps." cost = 2500 + access_view = ACCESS_ATMOSPHERICS contains = list(/obj/machinery/portable_atmospherics/pump, /obj/machinery/portable_atmospherics/pump) crate_name = "portable air pump crate" @@ -768,6 +790,7 @@ name = "Portable Scrubber Crate" desc = "Clean up that pesky plasma leak with your very own set of two portable scrubbers." cost = 2500 + access_view = ACCESS_ATMOSPHERICS contains = list(/obj/machinery/portable_atmospherics/scrubber, /obj/machinery/portable_atmospherics/scrubber) crate_name = "portable scrubber crate" @@ -776,6 +799,7 @@ name = "Huge Portable Scrubber Crate" desc = "A huge portable scrubber for huge atmospherics mistakes." cost = 5000 + access_view = ACCESS_ATMOSPHERICS contains = list(/obj/machinery/portable_atmospherics/scrubber/huge/movable/cargo) crate_name = "huge portable scrubber crate" crate_type = /obj/structure/closet/crate/large @@ -785,6 +809,7 @@ desc = "The pride of Nanotrasen Naval Command. The legendary Bluespace Artillery Cannon is a devastating feat of human engineering and testament to wartime determination. Highly advanced research is required for proper construction. " cost = 15000 special = TRUE + access_view = ACCESS_HEADS contains = list(/obj/item/circuitboard/machine/bsa/front, /obj/item/circuitboard/machine/bsa/middle, /obj/item/circuitboard/machine/bsa/back, @@ -797,6 +822,7 @@ desc = "Secure the longevity of the current state of humanity within this massive library of scientific knowledge, capable of granting superhuman powers and abilities. Highly advanced research is required for proper construction. Also contains five DNA probes." cost = 12000 special = TRUE + access_view = ACCESS_HEADS contains = list( /obj/item/circuitboard/machine/dna_vault, /obj/item/dna_probe, @@ -812,6 +838,7 @@ desc = "Contains five DNA probes for use in the DNA vault." cost = 3000 special = TRUE + access_view = ACCESS_HEADS contains = list(/obj/item/dna_probe, /obj/item/dna_probe, /obj/item/dna_probe, @@ -826,6 +853,7 @@ desc = "Protect the very existence of this station with these Anti-Meteor defenses. Contains three Shield Generator Satellites." cost = 3000 special = TRUE + access_view = ACCESS_HEADS contains = list( /obj/machinery/satellite/meteor_shield, /obj/machinery/satellite/meteor_shield, @@ -839,6 +867,7 @@ desc = "A control system for the Shield Generator Satellite system." cost = 5000 special = TRUE + access_view = ACCESS_HEADS contains = list(/obj/item/circuitboard/computer/sat_control) crate_name= "shield control board crate" @@ -849,6 +878,7 @@ /datum/supply_pack/engine group = "Engine Construction" + access_view = ACCESS_ENGINE crate_type = /obj/structure/closet/crate/engineering /datum/supply_pack/engine/emitter @@ -960,6 +990,7 @@ name = "50 Empty License Plates" desc = "Create a bunch of boxes." cost = 1000 // 50 * 25 + 700 - 1000 = 950 credits profit + access_view = ACCESS_SEC_DOORS contains = list(/obj/item/stack/license_plates/empty/fifty) crate_name = "empty license plate crate" @@ -1017,6 +1048,7 @@ desc = "Contains a canister of BZ. Requires Toxins access to open." cost = 8000 access = ACCESS_TOXINS + access_view = ACCESS_ATMOSPHERICS contains = list(/obj/machinery/portable_atmospherics/canister/bz) crate_name = "BZ canister crate" crate_type = /obj/structure/closet/crate/secure/science @@ -1025,6 +1057,7 @@ name = "Carbon Dioxide Canister" desc = "Contains a canister of Carbon Dioxide." cost = 3000 + access_view = ACCESS_ATMOSPHERICS contains = list(/obj/machinery/portable_atmospherics/canister/carbon_dioxide) crate_name = "carbon dioxide canister crate" crate_type = /obj/structure/closet/crate/large @@ -1057,6 +1090,7 @@ name = "Large Fuel Tank Crate" desc = "Contains a high-capacity fuel tank. Keep contents away from open flame." cost = 2000 + access_view = ACCESS_ENGINE contains = list(/obj/structure/reagent_dispensers/fueltank/large) crate_name = "high-capacity fuel tank crate" crate_type = /obj/structure/closet/crate/large @@ -1074,6 +1108,7 @@ desc = "Contains a canister of Nitrous Oxide. Requires Atmospherics access to open." cost = 3000 access = ACCESS_ATMOSPHERICS + access_view = ACCESS_ATMOSPHERICS contains = list(/obj/machinery/portable_atmospherics/canister/nitrous_oxide) crate_name = "nitrous oxide canister crate" crate_type = /obj/structure/closet/crate/secure @@ -1108,6 +1143,7 @@ /datum/supply_pack/medical group = "Medical" + access_view = ACCESS_MEDICAL crate_type = /obj/structure/closet/crate/medical /datum/supply_pack/medical/bloodpacks @@ -1234,6 +1270,7 @@ desc = "Contains twelve different bottles, containing several viral samples for virology research. Also includes seven beakers and syringes. Balled-up jeans not included. Requires CMO access to open." cost = 2500 access = ACCESS_CMO + access_view = ACCESS_VIROLOGY contains = list(/obj/item/reagent_containers/glass/bottle/flu_virion, /obj/item/reagent_containers/glass/bottle/cold, /obj/item/reagent_containers/glass/bottle/random_virus, @@ -1259,6 +1296,7 @@ /datum/supply_pack/science group = "Science" + access_view = ACCESS_RESEARCH crate_type = /obj/structure/closet/crate/science /datum/supply_pack/science/plasma @@ -1266,6 +1304,7 @@ desc = "Everything you need to burn something to the ground, this contains three plasma assembly sets. Each set contains a plasma tank, igniter, proximity sensor, and timer! Warranty void if exposed to high temperatures. Requires Toxins access to open." cost = 1000 access = ACCESS_TOXINS + access_view = ACCESS_TOXINS contains = list(/obj/item/tank/internals/plasma, /obj/item/tank/internals/plasma, /obj/item/tank/internals/plasma, @@ -1286,6 +1325,7 @@ desc = "The raw core of a flux anomaly, ready to be implosion-compressed into a powerful artifact." cost = 5000 access = ACCESS_TOXINS + access_view = ACCESS_TOXINS contains = list(/obj/item/raw_anomaly_core/flux) crate_name = "raw flux anomaly" crate_type = /obj/structure/closet/crate/secure/science @@ -1295,6 +1335,7 @@ desc = "The raw core of a gravitational anomaly, ready to be implosion-compressed into a powerful artifact." cost = 5000 access = ACCESS_TOXINS + access_view = ACCESS_TOXINS contains = list(/obj/item/raw_anomaly_core/grav) crate_name = "raw pyro anomaly" crate_type = /obj/structure/closet/crate/secure/science @@ -1304,6 +1345,7 @@ desc = "The raw core of a vortex anomaly, ready to be implosion-compressed into a powerful artifact." cost = 5000 access = ACCESS_TOXINS + access_view = ACCESS_TOXINS contains = list(/obj/item/raw_anomaly_core/vortex) crate_name = "raw vortex anomaly" crate_type = /obj/structure/closet/crate/secure/science @@ -1313,6 +1355,7 @@ desc = "The raw core of a bluespace anomaly, ready to be implosion-compressed into a powerful artifact." cost = 5000 access = ACCESS_TOXINS + access_view = ACCESS_TOXINS contains = list(/obj/item/raw_anomaly_core/bluespace) crate_name = "raw bluespace anomaly" crate_type = /obj/structure/closet/crate/secure/science @@ -1322,6 +1365,7 @@ desc = "The raw core of a pyro anomaly, ready to be implosion-compressed into a powerful artifact." cost = 5000 access = ACCESS_TOXINS + access_view = ACCESS_TOXINS contains = list(/obj/item/raw_anomaly_core/pyro) crate_name = "raw pyro anomaly" crate_type = /obj/structure/closet/crate/secure/science @@ -1331,6 +1375,7 @@ desc = "The tools you need to replace those finicky humans with a loyal robot army! Contains four proximity sensors, two empty first aid kits, two health analyzers, two red hardhats, two mechanical toolboxes, and two cleanbot assemblies! Requires Robotics access to open." cost = 1500 access = ACCESS_ROBOTICS + access_view = ACCESS_ROBOTICS contains = list(/obj/item/assembly/prox_sensor, /obj/item/assembly/prox_sensor, /obj/item/assembly/prox_sensor, @@ -1352,6 +1397,7 @@ name = "RPED crate" desc = "Need to rebuild the ORM but science got annihialted after a bomb test? Buy this for the most advanced parts NT can give you." cost = 1500 + access_view = FALSE contains = list(/obj/item/storage/part_replacer/cargo) crate_name = "\improper RPED crate" @@ -1360,6 +1406,7 @@ desc = "These high powered Shield Wall Generators are guaranteed to keep any unwanted lifeforms on the outside, where they belong! Contains four shield wall generators. Requires Teleporter access to open." cost = 2000 access = ACCESS_TELEPORTER + access_view = ACCESS_TELEPORTER contains = list(/obj/machinery/power/shieldwallgen, /obj/machinery/power/shieldwallgen, /obj/machinery/power/shieldwallgen, @@ -1390,6 +1437,7 @@ name = "Cytology supplies crate" desc = "Did out of control specimens pulverize xenobiology? Here is some more supplies for further testing." cost = 1500 + access_view = ACCESS_XENOBIOLOGY contains = list(/obj/structure/microscope, /obj/item/biopsy_tool, /obj/item/storage/box/petridish, @@ -1421,6 +1469,7 @@ name = "High-traction Floor Tiles" desc = "Make slipping a thing of the past with thirty industrial-grade anti-slip floor tiles!" cost = 2000 + access_view = ACCESS_JANITOR contains = list(/obj/item/stack/tile/noslip/thirty) crate_name = "high-traction floor tiles crate" @@ -1428,6 +1477,7 @@ name = "Janitorial Supplies Crate" desc = "Fight back against dirt and grime with Nanotrasen's Janitorial Essentials(tm)! Contains three buckets, caution signs, and cleaner grenades. Also has a single mop, broom, spray cleaner, rag, and trash bag." cost = 1000 + access_view = ACCESS_JANITOR contains = list(/obj/item/reagent_containers/glass/bucket, /obj/item/reagent_containers/glass/bucket, /obj/item/reagent_containers/glass/bucket, @@ -1537,6 +1587,7 @@ desc = "All the miners died too fast? Assistant wants to get a taste of life off-station? Either way, this kit is the best way to turn a regular crewman into an ore-producing, monster-slaying machine. Contains meson goggles, a pickaxe, advanced mining scanner, cargo headset, ore bag, gasmask, an explorer suit and a miner ID upgrade. Requires QM access to open." cost = 2000 access = ACCESS_QM + access_view = ACCESS_MINING_STATION contains = list(/obj/item/storage/backpack/duffelbag/mining_conscript) crate_name = "shaft miner starter kit" crate_type = /obj/structure/closet/crate/secure @@ -1586,6 +1637,9 @@ group = "Food & Hydroponics" crate_type = /obj/structure/closet/crate/freezer +/datum/supply_pack/organic/hydroponics + access_view = ACCESS_HYDROPONICS + /datum/supply_pack/organic/hydroponics/beekeeping_suits name = "Beekeeper Suit Crate" desc = "Bee business booming? Better be benevolent and boost botany by bestowing bi-Beekeeper-suits! Contains two beekeeper suits and matching headwear." @@ -1637,6 +1691,7 @@ name = "Exotic Seeds Crate" desc = "Any entrepreneuring botanist's dream. Contains fourteen different seeds, including one replica-pod seed and two mystery seeds!" cost = 1500 + access_view = ACCESS_HYDROPONICS contains = list(/obj/item/seeds/nettle, /obj/item/seeds/replicapod, /obj/item/seeds/plump, @@ -1691,6 +1746,7 @@ crate_name = "party equipment crate" contraband = TRUE access = ACCESS_THEATRE + access_view = ACCESS_THEATRE crate_type = /obj/structure/closet/crate/secure /datum/supply_pack/organic/hydroponics @@ -1832,6 +1888,7 @@ name = "Bird Crate" desc = "Contains five expert telecommunication birds." cost = 4000 + access_view = ACCESS_CE contains = list(/mob/living/simple_animal/parrot) crate_name = "parrot crate" @@ -1845,6 +1902,7 @@ desc = "Not a very dangerous insect, but they do give off a better image than, say, flies or cockroaches."//is that a motherfucking worm reference contraband = TRUE cost = 5000 + access_view = ACCESS_THEATRE contains = list(/mob/living/simple_animal/butterfly) crate_name = "entomology samples crate" @@ -1857,6 +1915,7 @@ name = "Cat Crate" desc = "The cat goes meow! Comes with a collar and a nice cat toy! Cheeseburger not included."//i can't believe im making this reference cost = 5000 //Cats are worth as much as corgis. + access_view = ACCESS_MEDICAL contains = list(/mob/living/simple_animal/pet/cat, /obj/item/clothing/neck/petcollar, /obj/item/toy/cattoy) @@ -1873,6 +1932,7 @@ name = "Chicken Crate" desc = "The chicken goes bwaak!" cost = 2000 + access_view = ACCESS_KITCHEN contains = list( /mob/living/simple_animal/chick) crate_name = "chicken crate" @@ -1880,6 +1940,7 @@ name = "Corgi Crate" desc = "Considered the optimal dog breed by thousands of research scientists, this Corgi is but one dog from the millions of Ian's noble bloodline. Comes with a cute collar!" cost = 5000 + access_view = ACCESS_HOP contains = list(/mob/living/simple_animal/pet/dog/corgi, /obj/item/clothing/neck/petcollar) crate_name = "corgi crate" @@ -1896,6 +1957,7 @@ name = "Cow Crate" desc = "The cow goes moo!" cost = 3000 + access_view = ACCESS_HYDROPONICS contains = list(/mob/living/simple_animal/cow) crate_name = "cow crate" @@ -1903,6 +1965,7 @@ name = "Crab Rocket" desc = "CRAAAAAAB ROCKET. CRAB ROCKET. CRAB ROCKET. CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB ROCKET. CRAFT. ROCKET. BUY. CRAFT ROCKET. CRAB ROOOCKET. CRAB ROOOOCKET. CRAB CRAB CRAB CRAB CRAB CRAB CRAB CRAB ROOOOOOOOOOOOOOOOOOOOOOCK EEEEEEEEEEEEEEEEEEEEEEEEE EEEETTTTTTTTTTTTAAAAAAAAA AAAHHHHHHHHHHHHH. CRAB ROCKET. CRAAAB ROCKEEEEEEEEEGGGGHHHHTT CRAB CRAB CRAABROCKET CRAB ROCKEEEET."//fun fact: i actually spent like 10 minutes and transcribed the entire video. cost = 5000 + access_view = ACCESS_HOS contains = list(/mob/living/simple_animal/crab) crate_name = "look sir free crabs" DropPodOnly = TRUE @@ -1924,6 +1987,7 @@ name = "Fox Crate" desc = "The fox goes...? Comes with a collar!"//what does the fox say cost = 5000 + access_view = ACCESS_CAPTAIN contains = list(/mob/living/simple_animal/pet/fox, /obj/item/clothing/neck/petcollar) crate_name = "fox crate" @@ -1932,6 +1996,7 @@ name = "Goat Crate" desc = "The goat goes baa! Warranty void if used as a replacement for Pete." cost = 2500 + access_view = ACCESS_KITCHEN contains = list(/mob/living/simple_animal/hostile/retaliate/goat) crate_name = "goat crate" @@ -1963,6 +2028,7 @@ name = "Snake Crate" desc = "Tired of these MOTHER FUCKING snakes on this MOTHER FUCKING space station? Then this isn't the crate for you. Contains three poisonous snakes." cost = 3000 + access_view = ACCESS_SECURITY contains = list(/mob/living/simple_animal/hostile/retaliate/poison/snake, /mob/living/simple_animal/hostile/retaliate/poison/snake, /mob/living/simple_animal/hostile/retaliate/poison/snake) @@ -2295,6 +2361,7 @@ name = "Book Crate" desc = "Surplus from the Nanotrasen Archives, these seven books are sure to be good reads." cost = 1500 + access_view = ACCESS_LIBRARY contains = list(/obj/item/book/codex_gigas, /obj/item/book/manual/random/, /obj/item/book/manual/random/, @@ -2352,6 +2419,7 @@ name = "Funeral Supply crate" desc = "At the end of the day, someone's gonna want someone dead. Give them a proper send-off with these funeral supplies! Contains a coffin with burial garmets and flowers." cost = 600 + access_view = ACCESS_CHAPEL_OFFICE contains = list(/obj/item/clothing/under/misc/burial, /obj/item/reagent_containers/food/snacks/grown/harebell, /obj/item/reagent_containers/food/snacks/grown/poppy/geranium) @@ -2362,6 +2430,7 @@ name = "Religious Supplies Crate" desc = "Keep your local chaplain happy and well-supplied, lest they call down judgement upon your cargo bay. Contains two bottles of holywater, bibles, chaplain robes, and burial garmets." cost = 4000 // it costs so much because the Space Church needs funding to build a cathedral + access_view = ACCESS_CHAPEL_OFFICE contains = list(/obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/book/bible/booze, diff --git a/code/modules/jobs/job_types/chief_medical_officer.dm b/code/modules/jobs/job_types/chief_medical_officer.dm index 7f90ab2ec50..f4f9f7dcd35 100644 --- a/code/modules/jobs/job_types/chief_medical_officer.dm +++ b/code/modules/jobs/job_types/chief_medical_officer.dm @@ -41,7 +41,7 @@ suit = /obj/item/clothing/suit/toggle/labcoat/cmo l_hand = /obj/item/storage/firstaid/medical suit_store = /obj/item/flashlight/pen/paramedic - backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1) + backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1, /obj/item/modular_computer/tablet/preset/advanced/command=1) backpack = /obj/item/storage/backpack/medic satchel = /obj/item/storage/backpack/satchel/med diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm index 5aaf81bf646..76e1f9b3557 100644 --- a/code/modules/jobs/job_types/head_of_security.dm +++ b/code/modules/jobs/job_types/head_of_security.dm @@ -47,7 +47,7 @@ suit_store = /obj/item/gun/energy/e_gun r_pocket = /obj/item/assembly/flash/handheld l_pocket = /obj/item/restraints/handcuffs - backpack_contents = list(/obj/item/melee/baton/loaded=1) + backpack_contents = list(/obj/item/melee/baton/loaded=1, /obj/item/modular_computer/tablet/preset/advanced/command=1) backpack = /obj/item/storage/backpack/security satchel = /obj/item/storage/backpack/satchel/sec diff --git a/code/modules/modular_computers/computers/item/tablet_presets.dm b/code/modules/modular_computers/computers/item/tablet_presets.dm index dd8b2400275..d9925d214c0 100644 --- a/code/modules/modular_computers/computers/item/tablet_presets.dm +++ b/code/modules/modular_computers/computers/item/tablet_presets.dm @@ -38,8 +38,10 @@ /obj/item/modular_computer/tablet/preset/advanced/command/Initialize() . = ..() + var/obj/item/computer_hardware/hard_drive/small/hard_drive = find_hardware_by_name("solid state drive") install_component(new /obj/item/computer_hardware/sensorpackage) install_component(new /obj/item/computer_hardware/card_slot/secondary) + hard_drive.store_file(new /datum/computer_file/program/budgetorders) /// Given by the syndicate as part of the contract uplink bundle - loads in the Contractor Uplink. /obj/item/modular_computer/tablet/syndicate_contract_uplink/preset/uplink/Initialize() diff --git a/code/modules/modular_computers/file_system/programs/budgetordering.dm b/code/modules/modular_computers/file_system/programs/budgetordering.dm new file mode 100644 index 00000000000..c2f9f015078 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/budgetordering.dm @@ -0,0 +1,280 @@ +/datum/computer_file/program/budgetorders + filename = "orderapp" + filedesc = "Nanotrasen Internal Requisition Network (NIRN)" + program_icon_state = "request" + extended_desc = "A request network that utilizes the Nanotrasen Ordering network to purchase supplies using a department budget account." + requires_ntnet = TRUE + transfer_access = ACCESS_HEADS + usage_flags = PROGRAM_LAPTOP | PROGRAM_TABLET + size = 20 + tgui_id = "NtosCargo" + ///Are you actually placing orders with it? + var/requestonly = TRUE + ///Can the tablet see or buy illegal stuff? + var/contraband = FALSE + ///Is it being bought from a personal account, or is it being done via a budget/cargo? + var/self_paid = FALSE + ///Can this console approve purchase requests? + var/can_approve_requests = FALSE + ///What do we say when the shuttle moves with living beings on it. + var/safety_warning = "For safety reasons, the automated supply shuttle \ + cannot transport live organisms, human remains, classified nuclear weaponry, \ + homing beacons or machinery housing any form of artificial intelligence." + ///If you're being raided by pirates, what do you tell the crew? + var/blockade_warning = "Bluespace instability detected. Shuttle movement impossible." + +/datum/computer_file/program/budgetorders/proc/get_export_categories() + . = EXPORT_CARGO + +/datum/computer_file/program/budgetorders/proc/is_visible_pack(mob/user, paccess_to_check, var/list/access, var/contraband) + if(issilicon(user)) //Borgs can't buy things. + return FALSE + if(computer.obj_flags & EMAGGED) + return TRUE + else if(contraband) //Hide contrband when non-emagged. + return FALSE + if(!paccess_to_check) // No required_access, allow it. + return TRUE + if(isAdminGhostAI(user)) + return TRUE + + //Aquire access from the inserted ID card. + if(!length(access)) + var/obj/item/card/id/D + var/obj/item/computer_hardware/card_slot/card_slot + if(computer) + card_slot = computer.all_components[MC_CARD] + D = card_slot?.GetID() + if(!D) + return FALSE + access = D.GetAccess() + + if(paccess_to_check in access) + return TRUE + + return FALSE + +/datum/computer_file/program/budgetorders/ui_data() + . = ..() + var/list/data = get_header_data() + data["location"] = SSshuttle.supply.getStatusText() + var/datum/bank_account/buyer = SSeconomy.get_dep_account(ACCOUNT_CAR) + var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD] + var/obj/item/card/id/id_card = card_slot?.GetID() + if(id_card?.registered_account) + if(ACCESS_HEADS in id_card.access) + requestonly = FALSE + buyer = SSeconomy.get_dep_account(id_card.registered_account.account_job.paycheck_department) + can_approve_requests = TRUE + else + requestonly = TRUE + can_approve_requests = FALSE + else + requestonly = TRUE + if(buyer) + data["points"] = buyer.account_balance + +//Otherwise static data, that is being applied in ui_data as the crates visible and buyable are not static, and are determined by inserted ID. + data["requestonly"] = requestonly + data["supplies"] = list() + for(var/pack in SSshuttle.supply_packs) + var/datum/supply_pack/P = SSshuttle.supply_packs[pack] + if(!is_visible_pack(usr, P.access_view , null, P.contraband) || P.hidden) + continue + if(!data["supplies"][P.group]) + data["supplies"][P.group] = list( + "name" = P.group, + "packs" = list() + ) + if((P.hidden && (P.contraband && !contraband) || (P.special && !P.special_enabled) || P.DropPodOnly)) + continue + data["supplies"][P.group]["packs"] += list(list( + "name" = P.name, + "cost" = P.cost, + "id" = pack, + "desc" = P.desc || P.name, // If there is a description, use it. Otherwise use the pack's name. + "goody" = P.goody, + "access" = P.access + )) + +//Data regarding the User's capability to buy things. + data["has_id"] = id_card + data["away"] = SSshuttle.supply.getDockedId() == "supply_away" + data["self_paid"] = self_paid + data["docked"] = SSshuttle.supply.mode == SHUTTLE_IDLE + data["loan"] = !!SSshuttle.shuttle_loan + data["loan_dispatched"] = SSshuttle.shuttle_loan && SSshuttle.shuttle_loan.dispatched + data["can_send"] = FALSE //There is no situation where I want the app to be able to send the shuttle AWAY from the station, but conversely is fine. + data["can_approve_requests"] = can_approve_requests + data["app_cost"] = TRUE + var/message = "Remember to stamp and send back the supply manifests." + if(SSshuttle.centcom_message) + message = SSshuttle.centcom_message + if(SSshuttle.supplyBlocked) + message = blockade_warning + data["message"] = message + data["cart"] = list() + for(var/datum/supply_order/SO in SSshuttle.shoppinglist) + data["cart"] += list(list( + "object" = SO.pack.name, + "cost" = SO.pack.cost, + "id" = SO.id, + "orderer" = SO.orderer, + "paid" = !isnull(SO.paying_account) //paid by requester + )) + + data["requests"] = list() + for(var/datum/supply_order/SO in SSshuttle.requestlist) + data["requests"] += list(list( + "object" = SO.pack.name, + "cost" = SO.pack.cost, + "orderer" = SO.orderer, + "reason" = SO.reason, + "id" = SO.id + )) + + return data + +/datum/computer_file/program/budgetorders/ui_act(action, params, datum/tgui/ui) + if(..()) + return + var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD] + switch(action) + if("send") + if(!SSshuttle.supply.canMove()) + computer.say(safety_warning) + return + if(SSshuttle.supplyBlocked) + computer.say(blockade_warning) + return + if(SSshuttle.supply.getDockedId() == "supply_home") + SSshuttle.supply.export_categories = get_export_categories() + SSshuttle.moveShuttle("supply", "supply_away", TRUE) + computer.say("The supply shuttle is departing.") + computer.investigate_log("[key_name(usr)] sent the supply shuttle away.", INVESTIGATE_CARGO) + else + computer.investigate_log("[key_name(usr)] called the supply shuttle.", INVESTIGATE_CARGO) + computer.say("The supply shuttle has been called and will arrive in [SSshuttle.supply.timeLeft(600)] minutes.") + SSshuttle.moveShuttle("supply", "supply_home", TRUE) + . = TRUE + if("loan") + if(!SSshuttle.shuttle_loan) + return + if(SSshuttle.supplyBlocked) + computer.say(blockade_warning) + return + else if(SSshuttle.supply.mode != SHUTTLE_IDLE) + return + else if(SSshuttle.supply.getDockedId() != "supply_away") + return + else + SSshuttle.shuttle_loan.loan_shuttle() + computer.say("The supply shuttle has been loaned to CentCom.") + computer.investigate_log("[key_name(usr)] accepted a shuttle loan event.", INVESTIGATE_CARGO) + log_game("[key_name(usr)] accepted a shuttle loan event.") + . = TRUE + if("add") + var/id = text2path(params["id"]) + var/datum/supply_pack/pack = SSshuttle.supply_packs[id] + if(!istype(pack)) + return + if((pack.hidden && (pack.contraband && !contraband) || pack.DropPodOnly)) + return + + var/name = "*None Provided*" + var/rank = "*None Provided*" + var/ckey = usr.ckey + if(ishuman(usr)) + var/mob/living/carbon/human/H = usr + name = H.get_authentification_name() + rank = H.get_assignment(hand_first = TRUE) + else if(issilicon(usr)) + name = usr.real_name + rank = "Silicon" + + var/datum/bank_account/account + if(self_paid && ishuman(usr)) + var/mob/living/carbon/human/H = usr + var/obj/item/card/id/id_card = H.get_idcard(TRUE) + if(!istype(id_card)) + computer.say("No ID card detected.") + return + if(istype(id_card, /obj/item/card/id/departmental_budget)) + computer.say("The [src] rejects [id_card].") + return + account = id_card.registered_account + if(!istype(account)) + computer.say("Invalid bank account.") + return + + var/reason = "" + if((requestonly && !self_paid) || !(card_slot?.GetID())) + reason = stripped_input("Reason:", name, "") + if(isnull(reason) || ..()) + return + + if(pack.goody && !self_paid) + playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE) + computer.say("ERROR: Small crates may only be purchased by private accounts.") + return + + if(!self_paid && ishuman(usr) && !account) + var/obj/item/card/id/id_card = card_slot?.GetID() + account = SSeconomy.get_dep_account(id_card?.registered_account?.account_job.paycheck_department) + + var/turf/T = get_turf(src) + var/datum/supply_order/SO = new(pack, name, rank, ckey, reason, account) + SO.generateRequisition(T) + if((requestonly && !self_paid) || !(card_slot?.GetID())) + SSshuttle.requestlist += SO + else + SSshuttle.shoppinglist += SO + if(self_paid) + computer.say("Order processed. The price will be charged to [account.account_holder]'s bank account on delivery.") + . = TRUE + if("remove") + var/id = text2num(params["id"]) + for(var/datum/supply_order/SO in SSshuttle.shoppinglist) + if(SO.id == id) + SSshuttle.shoppinglist -= SO + . = TRUE + break + if("clear") + SSshuttle.shoppinglist.Cut() + . = TRUE + if("approve") + var/id = text2num(params["id"]) + for(var/datum/supply_order/SO in SSshuttle.requestlist) + if(SO.id == id) + var/obj/item/card/id/id_card = card_slot?.GetID() + if(id_card && id_card?.registered_account) + SO.paying_account = SSeconomy.get_dep_account(id_card?.registered_account?.account_job.paycheck_department) + SSshuttle.requestlist -= SO + SSshuttle.shoppinglist += SO + . = TRUE + break + if("deny") + var/id = text2num(params["id"]) + for(var/datum/supply_order/SO in SSshuttle.requestlist) + if(SO.id == id) + SSshuttle.requestlist -= SO + . = TRUE + break + if("denyall") + SSshuttle.requestlist.Cut() + . = TRUE + if("toggleprivate") + self_paid = !self_paid + . = TRUE + if(.) + post_signal("supply") + +/datum/computer_file/program/budgetorders/proc/post_signal(command) + + var/datum/radio_frequency/frequency = SSradio.return_frequency(FREQ_STATUS_DISPLAYS) + + if(!frequency) + return + + var/datum/signal/status_signal = new(list("command" = command)) + frequency.post_signal(src, status_signal) diff --git a/icons/obj/modular_tablet.dmi b/icons/obj/modular_tablet.dmi index c1e31c59e1756ed592d643e6ce1361eb4bd98ef1..9ccbef928b956c3c7af744c3ebf3b2c8d1c14216 100644 GIT binary patch literal 18695 zcmd74XIxWV+qap7CLl!+K@b8;QHrPtC@ml;iXe(KX#o}KMd>XGief{!KtMW*fD{Qu zdP$Hbf`B4j2-16~Nl2X?ysrCxp1J3ldEfcXhxtHBvi9CvKRL2wSMRwZDP0-<=0;zW#1L-XKtLdPdR>pR`l_ z9a$~;x~oO%;46b`Z}~E8Oa^{7t*}V42$ym-b*t&`5>%WZ7w)fGTOKW43nI~*D)4I5 z4eHjcQg~ z&H}kzhagT{ql07uemFlHaOO1RzS|Hnb_PpF{Bg}ddNL= zDgT&20^J9!{w3u?S&xq#!f^Y|FjwyFN~g%qsMSuyL!(JaYwwunh3w9$$E+aB9#)q; z_)F?_hD8(H4jEp!cO|%$w{`w&+jqN1Zxj*ia5uxpP6K(BXR)>(>D>-WDF*d7vQEDq z8>l-Lq;gre`r5DF+s}<5xr@uwc(|K;tnOreOJ>)l7a5mc1(sEo>L><-1mV8R+-u>@ zjmo6eI`iD4`HyK|E}tyi?S3D8ZumvYVfXt8uK2Atl97ky!vf=9obG>nyW`k&uJ-w| z*`>Rxf}i^2P#0Q4n(l>USCrz#tDL08j$4lfe-4Qe(7N?a*ZcZ7*D_wTFIQu2Xd$;Y z>)o+6rR9ZYtFiBq)z};kxy0!AJ#k!=0jWt-ZrLncOztHe0wR9En8-fZq+2a9KXx}4 zBdRq_E>1Cvm_De~NW<`;efx1Qs(){d-NaGoEOGKOur0@nsUXm$#w!MT=E0+@84+&| z4rJglE-2?ry94kRqe0C_`w!fU`i?5Cef}&Y6?+T&@hp1AS_y79qi{)}%zA$m|Al>=Nup|CsVM%>tj7DKq6{83dvlxq zs|8!eN&6l<28fo2-(MG&0O}qc?S@ww#)#=JjI=LnkkH zZSuKMk`6I8hI`pCn^AG@dgqAXtQw!%BS?azQh7!51Fzvbtinme zhhA5+yI$0W!Dl%1N;e>A&>0KCeAf+3!P8#J~lQrP6j0sZ)?KPEeVrREuK6t&Pml8_tMeIi3+{Eox zXLW_W(qs_)nj{!~KcMOH3{7G<41d1Kua_91O;2&jJTJpAQ6GOzJfc4CpMdUAhK9Rm zWIvV*-O9Z4`@PM=Lygc{-tH@{<#3ljTKQ zQdsgA;AW@bGfv?n(`B0V6wr}&wmUso!aSel3}&iMrEFQ3e15b9E_*xeP^Ucy?;V#Q zsHkBw!qep_I9pFB{P$w5s?A!~#2v9xI@V;@N5 z`~zu+u+_@v)SG$6dRh`4U;a9Ff6S};nOs)5S^Ms8E}1$WgtHjc&(1<%==l`hX*bXU3!QwB7IW3FWn-iA{@JP-zFy7bHvBV% zm@N9zdh&E0yb<|D4l1m?y|i4W^VgR&9mL=W%U9rx)9MdqE;G<%Q}Nh->3Tb8N<}Pa3qPHOI7?4hL%@6CXMY3aNo;7SqaICKxm1@?uLVo{9m1x<7QS ze*QI_Fi|;TZWW!!DOlc>CTpx445g)}31yM!P|< z?fy{I(w>?5&^GMMC)d7G@xDbDi0s?>W&P-Bg$Ri9{vFsEL3px+nMeBgn~cD;-ZN@; zUh^+;N^1?yQZcy0F5i1wtjPh67TE-&pI%M%6@I$ADX1$JPB^3{dlrw@??dL`3Nicq z@i}mIPW>^v9MH*)`Zg~mZa?a5xP*BJGyr4aTxXHhL-Du3YS4_aQKgWXeJI-%kLeIf zc2}semEEmFPwOz&LNt!`Ae1&X3 z9PIR=)k8?fVKD^zi{r@R0T8Uy)DTi0U#QYq_l-jcGZvu%*DqV@cL+&JP%Qe+JnT7| zPn>t#qIdbX&ZKKB=v;#1`RY2jVcBl>6dzfYRB(YK6&?l|3~FR3f&8 zm9c->Bac{gQXFSd|ShdJ0Ybvh_(Z{0fl_11~+a>dC99+;Mt)h6!QKy z%s28#QU-E<_=cVDXeqWy%R@aJm2v06ofQ0vb`*De`hhpdt*Y4jh$+`V`go?(In2qL zn2t2YeDw&oCJ5Wi(&0Pzp&zn^*%`DA5rzlj8;M`Zx2{;sgYlZXa%cCGRT|=MRpIYK zMb}-5LFXZpnuuy%!?J)!Phm!gYIypCF*#dzn3pQ0QfAVJm-2$j*777D{pldTl8WKX zH&aj1ziNM&oifI3dV*@9bD7uq{6_`Ky=7^M)9X3xGctF_?ZP+OLbkGKD=uAiqVe*F zGDW!3OO+2l7iP~0W$TW>eisEFACzbtH3*WEbj6HCTIrE9FwJ(w0+n+C=D<7*RhaDW zw!{*i4_#)nbE$$J9XS@V-2_$XQ5L>u%Qbpwus!H=-;)o zNJx8yyLBt6&>+CPxsFzhx&^L)+++K40L3MLeLg}@T3--xbECHqZJ5w|?4=kl)G|=% zsU(%z&dQ!Gyl@`8T!P9m2VaT+P6^px#})GqP7G<1bz`=E@Fr%*hE>Ye{*(9`L#JVk!)xmo)^zmV+L#ImJ#yJrE|kz zmRH#93Xn5j{xTwft(LMLzBQ7ceB{A9)jg={2f7Fvhl9gC;xOacy#h;8Ic1(IL@$^~{JnQjw-X^>}sUVo`5apq<~$hw}AD>o2So zld31bKgJ~qr>pH}*zb=vqFW`YAwH?ZX)BpDP|0M1bT8Z@(!sZ z5oz^<@d!%QeRbr#0P~3_k{P59*i<8NqJ@ZI(BkPrNKGp>V9tf-njYW5oHDx4Nq zD`#D&v_|p2tAp@9sHw63eD;dVQKz5K8Q=S5QEmc4Lz^;yvCqqfR24dkpFuATIj}9S zhgo0<^48I~B66iwvj){+0Nuzn_?uXWShXnSg%!asTpo~fyZ{J}h1@45y1?I4U_sy| zKs7*;I6-8AKW|W+|B@wtK8@mh1AJPbVdU`g?P_h+bHrcA)#TdYM~(umG zv*_SSE`KwfmR8p5EIz+^RcMXssvRMc zda~F4P`TS@)}fu1u6j+)#0aV~1G4`8tsMvH-NrZ4LL8-TFd`YLXHVVkH^n$$=K1}@ z?N8>Y_jq^rjOA~w!NoX44|;)YZBD%{=p4Fp@`&c4;mQ{Xp#k)7-r-_$=-GjR-B9C} z-z{GSCdmdz$H2#vnOuHO?axztSKyY8?nw7*zrR9`Ly#At`w;jDY=zj19!)%VeC6`s zTW5kNS#^4}Q@+x(*oFaE*M{B00Rb|}s>fd;pX9KVmgVQq0J!CKUi+vy6{5ZVV0c`p zW4%l=YCkC40_z~=D9#zOS{Kl?R`0uFO%|FWWG2}Rejk7P6V{RoIayg;Uvble1KWE> z$S&ko2}jdvqIS(@`TY@FBI~cnQ)R%*5HBYm79tnx%dCxPIWT1$RFr3(XjKeZSy{`2 zTkO=Ut%dSCbawa`jIlX|?i=3bukmBKo26U@<`V09^+Xk3%<^M=ypwboNs@Q$ecRC; ze&^~jbMHuNaN16NKxsO@cI@&q_Z16tB9YdjS+|IF@etw1K zI``#kvN@l(xROV*egSXls~L}PwiNqS&g)hJfja1MFFa`B^5!Q=f<@;{y_vuX*mjsyCz31m`_S>hk@>-SE3+k;Hzzq_Y(&S2bbEVGv%CK}^d`VW0 zZi}_hMQsGkusQm^x#S8SIl|$T7*m%t>{YRe2ZitqA>XoAG4nJ28V?aX4$rLSJ$xyrlOz*L$M}8Yt_*wa zOK<5jP(SjJkHO!7hEAQbpe5N#rIMTr&GI=Pf4|yop{vD}w6S%ZR~-=!mNg-QycH+2bt^#@3O7gl@uMF<3Bqc`+({MeMqF;d_7>sW@XDJLf~o` zrilHdI4Cx=s~)shY<_S&m0#PyXG`sHdG-dj{9$M#+l2ZAai=<9i{n=$)M$Qw*V-(vYxMMieNIlZkgIhp$CT*Xee zoncXZj%2e&vbdce|Mi@X)MJKDv4Bjhn4F%qtD5_A!y>dnDw0FZ>nqyVUZ6N)L`>kT z{G@6c-#ejr{{21Gsy6r0e}-Qngw1t1*HzVIl>-HHgQ+O=FvQSO1g37HZe%NJ-5PU z?C#bbH{HsO^Jf*_Y@Tm-$s<2mVG_#3ZJ6(Hn$CXN^A=YYKd}tU;h4 zcDh@PX!>mfO@sIZ44(ycU@FM3RuAA}C;k=~7v|!fQy*Y>q(83?4jrt+M`jg_gVG29 zb#*iw0qO!U8rmtQGzCcl%YP;zwxYj+=|B9YQwJkDdNph_!jJa-`uTD$Z*E(L1J`4Z zC=ChsBiJ+psE=>F<{$p9jsTS}X`dk72kH84T^wzHE);RSV{Wk=W`;+QUGb#v38ka(Zm>-#EHQi*3Vb;Mr+eIrqecW^V$OQ zZ2eO~jH|oo{4(1g@?QTY-1iA-8KIqeMA(t|?)SQra~fmknSldG_K`XPPUoSVfUAXs z-P`^08)?Ho;GjlyOX}cX{^JdnpMwJT{YrZL4e#kfc{04Zvk=NC0$2AsUe98V)mP`7S>PobH&K5(Xs8F5p)T(#=3dBP*REA=XbLtmA)lQqaPrv3lIX)g?_Z7bpmEL*!|N3#zZY)bXBR@M zl7O>TLliHninH8S1REair#PD69|aaY>`LJ~bAbJ?gXtDNWn9*V0M4uXZ(cJK{erMW z{VoB~5X6?i{it%Oh1){+-^fpz>0|5Ue>{lBGED@c5DweYvvAV0aBM4_$X1wqPzWt@ zzkUn+hEM_7AH=qh-|w8o*L527H9EWH$)c1aMt`z!qn__vlUhl*VQ_7yiHjLQL&(4i z!W;S}Lr78q8`;O}be2I3841N?t-JkyMxdyX#Z?Pz zNIUM@Zzz?f!&U#Qwa7!HZdm#BMQ5CAK^AuZl-z0NQ`CUA1+s-3sRhZ3&g*I7c6v5?r65Ug!b>sHAnbKd&;DG{AR($eN#I+GtHcvgU> zbw$^iSJvW6p9i~?^1+0QP(eWflE&-R+0!EiwmqNLxN3eThJ3E$s~M(SRu`HZvUz^( zvcXbdyb|&w(@ovhm}`qm2I3JD4`=OoHjxG4J{q%mxr=ym1)|T31&e&roM9y9E7y zf)>JD=tO6RSTAy)T}2=Au3uFv17d%ipdmOiLh@PgmP%rPZXWJ^9?K7coily-2zfCd znN2bAK|WkhHpT)rgKcQq)4S=zjH*U7mum9SB6f_Mn%AiGYUf}oVrRf=Rv1INc%$de zwZt&IA|eI{$jLWqsth^@;*ixSd(71rlxdG?SIpv^;I;xL&S~+Cx{S5X;VhrsFC??1&^?B+iawAV-2I9!zamm%KG~6 zJFib45ZMg8kK7{Nn_T_e(Z|&IIG<38U=-#qguYWzZ)BO~?eD(=AQE*v+xm=BQn&>W zlJGLP*?$fmdw%}q^zhExcN0NZkBO#iH(_+=Pv+wjZYMUlSe6=-v{)#d&048Qx$XFPU& zGQ$)SP|vtlD1INKmW`V<1kmDwP-=riO{B68Fb;5WME@9c9MQXlLT>%CB`u}Ds6bjy zRVr7+mj_(^=?HyTC9qb6Zj!euv3us+mDH(ZTZOf`XF4hmZ>h2(hU}2%i_xI+&qw>uL4xym`~rKF9`n zxcJzK@i7=qsB}!nF)aQbTd~}(&6Pkpb=SJ~flRS{@Y2tMzFOrPQH4oTUGT(%`EY9m zessUr!hg!{y1N z04r$DX6?1n4;5$5m+ui+ExuvJHpGB@!qeF#C0qX7oa~Q_X4T^3;%`5qL}1ggHdMlD ztg8J|Reimmt`{R%!dK|#(On2=4Z}eE4h)U&l$cyAIrl{22>bRJOKebEBs(zEVf+6) zg-i~+S)ujX`Aplm;0)O3x7>7rL%`tre1$%Sf1V1#S~!5mifqF+E91-8q+E-Ht(Ts* zWE1*_W7z{F4!e_GO#mr+79)W$@NoqNwi9HwFT(g oRB|U{fk~hzQ@gw!@x(e-QdG)NUf(U~tt{*AI9d`G> ziO4ATY*Hd#$|EZ$)rwb+cn_*>uMi^nHoF{mw#N~{l3YT%wHwiuJS4gyg{E8$k@(PQ z-Fo|UQgUnJzcBd*5YD%|XPk{RATJ0f<6WxATTnoEceU@W(eb_IlC{UT)4DuqsQ<^H z#Q(&|)_spgSOFQHPF)Ma6fHdWfRleDv(mT(*5_PC!KF?3Mfyj&MivXIP=}CaP zMlP*1dN^IApN83&tQ5N1R(mU1S9x~&D%ynCIEsSuOw#f`2~N%_x9M($Sz?J3x2OVE zfXO@U|?-|WEb}r&=vo1SYM$lb6$v34(0V_qVp~3`mtV{*s1YTWLM{SR9pmHTj+oG<>~zdpR_p zrAB5;6bF_~*8$i0Zn0p2MZTu-Q8c4>H3hZDyaWulc-CI9;Q9@|C@qNvQ&7?~ zbVn2j|5btC!$yJVuR$j-O3eNl>(X1{T2}rSe${&Y z3A0G*@`^6)fJ*0SKo3<`R)$oAPVKgJf>1-~2yJb9v0~f^xD=P)~VBgib)Vi(E&pUXh5tkmvlY4xA`KWA_HWJJy3|PYAo;Yqo=EUhDU~0>RT%~ zk{4rbI3`uHc3--D4k*cK@d?TIN@E8Nht3Y_(%J<|Eh0%*3^e2=Z;IU}=d4z%q4LyP zw++X9LQLdtD{R~v7Ichzvsn(w0|4u|T>;@eSRGNSY!Xr+dBS09RMmz?m=D^r{q@|o zk`(aU16tb2^%OzQsR6&wqM$e+J?~v1_8FiY&S8g0>wbr7r;k_>I7lXMT^*sIx}1Zoj(rA(PbIapK40KEMn2IW-XOC-!#}^vq90vP&R&>T zcA6C!?|zjsGIqPm-)YoG8mw8w?{ThA$44COrF1hw^qMM`lp~IzUACsgUbF7ci+$}( z4J{SO*7mL@t**X51GW(d<&W(mhKrmBgw|u!C-zNVqON1Xj$FReAH*W>Vn=wii3kH&!y)i5!|V)cHprDbi`;AvS+*bjGk3g4=Lv-`;AvHE+OSIFJLRC(SHOU(4&XLmNpMEiZdw{DuW@yq;DTvMrbqX?1%f6s}M#$q~A*o@CFRV>$mXu*Nb6-|G!D_{cO1@JqH+Gw|w+eTdV!*fK5N|E{a0rAV6;@Bs=&rY$HbQbs%I; zM@P69L)0n}3$Z600%aocyDJ)l5ukN?Jh5_D39Tw_K%H08K}+5XYq)0K;X_P&igQO& z!dAl>t6uM>Y3jSXFrST(ra>!l#?QKn9~qIErse%?;%s9PnxQ(JiO{Q~w2zo!963|S zclk#yTi0N*{foh*Lqj`5zx-lI3p;RhMIl{$wsbF?T!=c zWwLIIgJw5kyF*qp-(9(WpV~XU+dvx63ut2WMEUq|%MHaeT_ckX?Z-oRGA&=D*7ayJ?G&F(w+r}O77ZFc1tZR@ah3-E0d#p3zZWg;ae-@>Z&kleg-3Y=!*rGM$2K zhB-o5OZ%bQ-#Z2lA+Mt&=b2J1ljF$v7RtoD-ogr;Z&qtxKM1}hF;KqAx?&lSqQMhQ zpQIguoaGJz`wHuhh=U$)KwZzu4C|=UuA2R9v^afvkvMmMm__8uO?8Cc!9$l&{;@hZ)!6AaM-UiQBa0<44!o81Z( zXO>vbHK&xEVqm*K8VG!bsl701HKythx|aUMv}*p7LInA_fN!PryKD!;N@?Aj=tug0 zZtWYdZrRMTaaCPBzG3!En*R%))3%*NpA!BhBG{wd3R03}^vOoxZNag3kG6}Nu)()@cFu5&%YYyf|cUf;0G@V=?LTLMBGP5uw`VAh#(%m+{ z2q0g{_^YF*x#lhQSZt!hP+_0gYOpo2`$Wq5x;)__>mrJ>@n#0`^{;i zy35;XJ3zMXyW#&gjK98ld%qVq(ZWCE+3+U24CbOS;IcA<%uYLDaGp;5faIZS{S$ld z0G{Lpz~$T@NW!8y*Vnhq7C`EcpWH|U%O&uyc6#Oszere1!KF&58GErX zT>{|h$bGa4&f%|1ZARYJA#K|H|C~Av)4=K}_(dp&t>|e;J(%+@y}$>gj(NlSMg7~Z zNe5IFa~V3K3>CLpz(kmaPPIPkqfn4Khb}ywq*1AWEB^p_OTTY$>#IeAq<6$IxGcCf z^i1nF^x|S<4Qck~R{0g{PapUH^3ee7nQ*L~_C*YaarZ_nC+6iWZ_I0u!5G&noj&}q zf#rgt3*%fP4Xce(*7M9tc%Bx79XcE}qnB*w$ zp(l^t5cH=yTg>u=Nn-JV*lZ54ctOIm4d9gEB{e6%wb9*B7p97fhG9PtSneDf5R;t!kZ zx48t&rlM=uUH#H>N@u!yLU?NpM#0YN7R$LOsH@_<-d~j4Y4)Kk#Usk8-dC$JV{bR~ z+Td$-8q~nhut&uD^Eor>G&(RHzfhgC{!PFuUs@|_-(L?pb~jwdU|hfNLL}q&!Mh^h1m1 zN<-w`YCISh z#)&yTF7hJ!C^DuehGz^|i7Y!qOm|xy^6=jk1OJnj{GaLx{)$(X@&{P|G>6foc9O4QgKGBWc4Gpr;Z9$+$3NQR z3owpLM4f#Ki4X1K7TKf|;;OkNQ&`OkwzDEM5+6PQmMOCtEQ*;U|07WrnG$8LIxm{% z<{0Q-{KEu6s>(yl;>Wxk?laxwJ;JrjDJz4=SRY)x(=2~`!dn-Tf?N{I=AuPJK=h-( zerboE$6U_3CHX&52%zeJfshLE@oO6D!xd%BuBx_n9E2gZ+iav;l6m9Rg4ts}`Bh%Fgvy`YBay(BIkxC& zznE9OjXbR1*GZi~>6UQx;rM?31j$jjhb+`sH5XtX$q2PWSixQ`-;Q5=b@g;Q$hZz|J+CB#~l#kbt-R5%V$vTb0q*`JQqBK3M)bPYP9xyKi7~2FVc;M<4C7 z0-svt2xZ_ug5q=}6)TO>kPFvSOg<-Rl1K@Q4sxi`F2u75EAQjf@`dfOc>7gn46E;1 zYsUBV-ZgaNN^+df5>YcFvco5wq7S3F(`Ln^EY^e-3!L_0f8)o`D_5WHmd+ZzYRNjM z9CEHs3b`tyAURE@RSH7)aJ8$~=Cirwl3%SJj7d{5qRS66$KD7zG7o-ngHKcDgRzUJ!-{{V9v;F3#zmhs6ia7D{t?KwBqW~Rzw zN}J2HT)}nmlNU~G`DFA5?Kc~u0O3L=S?F=(M!wHRxst+nL&2TWGpmrxvXSQUG?%Pv zx!KGt46_;ETU;2__YCLv%eEOaw`kpI(8!ygZ+ zJ3@*ncYbvFAWTg8-Y-#oX4I=#XO=pf$u1^6vQEWeO2I&o&V+2Q^kmMr&BSl?Uz;adv=nVGLz;^cKw0^y170p=q zYjcPv2lr~sgOl>-%mA2hps?*juhn%`cl}xp)D*Fi7E`HC3`trK#kkc+-}4|7ONg@X zRXCru?QO5gRi7~WV|7p$G9&HDPdsBTVE3Ab#s^Xu#i$jEf|i~~bL-H*_%If%R+aBS zOTFa$&nzNZlPeW--S%a>Q(qmMir>{-VA+9{a-rN{s4yQBs5Ca^zt!DWfNQI+jX^MGVk4c|Xe& zSu%f~V!iG~N2VvI^-4pjEzc*ZxxCTpvy5n=MV?Y`1|w8)obH{^Zt(lx7(#dh{ajIA zPA-iz(t8;y@_?w`FR(lWdnJCAq48zS=fY3z_p8a53%_F4J9de&g3ffnSVo4}B-OPf{Qs{I zgy!EPkUf(?1vZS^PZ{4Oev<&P5X_WKN@H1c6z%^v1oHWTK?7-Dq`o3k>GmVtIYm&6 zB0WX#Xfq%&k&Ut6A~TWW&qU71VtxYmrtk|Phu9DslWicV-Us`f8n-L{JK=&ZBo0JY zsUF1bdko)GM!)DkI7z(8JefPUSCqf%FdaN4pfsPIwgn33(j9FA%67>ru_39<#+dyL ze`BjK?ykUQAXqv}gxRV2q)UwJP%lf|>4Mq5N~6-P&&oB|sD#_Zqf%dgfp& zJBrD~e5PAChVA^@FH{NSG1=>EkMiWfSab1FsiLkE-<>Wlp^skK&biY=7=r#SfFd}tx zb_4{vKcMj?*+sv}SEB zRAFc|j5DI{X7Lzq%Gc6@3YKvk*hntp8xG_*W>TzRk7DukVz{vb&o22}t$|iZK%D@O zAJrlr6?^y^_8Q~#Bf5~nw|1XhVe4nXj}0E=_RYhkDy|GG1qyB`zk!CcxysbLhcGI< z$}V7Vi~E5foopBSQoLv?FwB^m1>v}Q4Cnpt%0~|{E(RgHv>n2rjzo2-_KzU%AtKxM z^OLzDEOD%*U#h-Ws$sh<+2_^8F=AbokQbHmgksaT_)Kzq^kGSjildOlXEQa!b zJ$eH8*MD-mS~GNFRpHSNP?f*Pt3f9u4jf<{HQX6s<~>IETgJgWq2OVnTlg&D@Q40TTUp_H|Ke!4RbA zMkKzB1=W~-FUrY6Jfe4iK3T1cSPb2H2OQF}DqLp$a-8WjN;{roHTmDFCjZaGt-+Ic z;XA`agR+1p_{7?{^P=D%m6H!ErZ+n%e4@Or)>rx?Z2F;>fD#d{Jz_=vH)8!uSy|J7 ziaae)79>v1sQ^Ro1|sr5|K-bBGRv*Gup=oFSdI&hQLlqBt=wij|EQ+#_!LwE=+#IR zMLU$G@E_H5iri&{%{5w+1Tq|d^jFIqF$0zV<-)f>TeNP_pKuj%hjTxz0-?LF3Me@l z#4CW|GBQ=qKi^ExyYfs}MfrlupJvE1^$0jF%YEIQX+$PzgD(7_yln^oa7Az!UBFU) zD=6{x@KJ-n33~O#eRc{m@uGh&i`yJi4JW!88MAm*idQ=%=sKbGa}+b z5eBWSz8xCT?p01(#RH#$&RS##GIn@fEZU$35S@F$EC5HhtzsE(^cV@1_ol0<)y`^7 z`q4(xQlECCc@syZR}R%bBl#@z4EmS`4s6nG`?;mN`SkM7sxt+t&mc12E62kNg|rk~ zB`|I&m0n&VGK$s4>77pz^oICV-Ith^N2Q~Wu}4qX2R&4d7g)WZ%fSTx4hgyNK?`9G z90o`N=%A$`HAtfnM@SW2MQ8>Qo=j8bZ-K|cpsN?M$7i*U+waW*i#a@=NKmU9DV}^hG4rnw&=l31Z zlM|u;miU;bZMRpRFlRluMaq@wQ|zsn|D4eOD?Wzwnb!>>ElpKww>lHlT5lVjOl8&$ z{RzVI5p3R$=QoJE|9c8vm7P2=7}bs6mwpf;fMV^-s;#77I=-J7US@g(ElWUk@I07w z5cP@kDs9I_V(Zg8_J2HzQnGe*_DxVv&o)ShA9Cz{D@Dd3&i$Y8$4j8}Go979TlN?J zyV&)AuLZazwZs;&D)-v|h>YaidOA5>s=m7Yzt^16(ZQyZ9^P4PsM6^Q@L`eR#+ z615=QAt~RFuP?j1g6fek_(wu0Q0AZ@`4I}cHg=WGp1|x!k79eX0PhwkHrjlURIn8J zdW*T00!GuIdECp(w^g6XOL#J!)g;yU%A2vxlMCm7A;Hp<$e#V0xZ$)k-a@e16C6+h zl8@1#R(pdiznv9Be)XjWwqA-fR3*2y|LXJb_(u3?fpC#CsK!lf+}NQ5 z2Rl4`J-7Eva#k57z=Dv;f_{4$NMX3%3zZqCL%$hUBhS3cf}!u)KKn20;ljVy1B{l0 z+~}rKgnNO*IK&xKDS*WY5+CphGl>brWZ=E83u0W@JbUT?tcRFAXESRak2kD;&EL!S zFkK4VNr-)i8Al;EjAuHOO~+xwcou*5tIVkf|B+STe~863+L6)^%l<#h+w-h6kkw{#B&MQ& zS$uC-(3+|-hS0LD1e(+*26z%zNo|q=oWT+(Q}AWmN6F;nUadK|i+|f7s0k*~u7Wcp z@J`@_4iBgoF^!9?1!cyv@GKSLzVASt=ZSLCmxf-v+af%_bp^tXI*6)J(az?J!tOh< zfq15Fz*M`y6EW=fv8eLi8U*$S9f5~Yw-ybk)N8qP8t0Ukwn{>!jgk3ZKHr=0UKU!U?@f0g1jB=|04LXp zhyR{j(V1*dx|GY|G(y}uv|k<18lEK^AkRsXKT&4!9^P#s=BeNXs_Op90M~@Fpkit> z+q+cytNY)#zDVJVyoS+6X#Tm)wK zEbYPd>B}DISEj0*03SL$yPz2@O_RbS^CV(-w7+g}Nx?eqyIvj9h z^OQ#2)iA)dBmz6OH?x%s*^5DWehoWslCF1yh|KbSKpji4+DdGWsWOb7Ui zQw(xT_5M)ssv^_CW?w18&4ggRfvuPMVnR7g`>O)gsLoiooL=6OIhaxEY86ZTFr7>t zywT@S9aAC3UFa-yPH+uK2$QWiGlfcNZhIOu#Ia6vZTR)@XsBf{WrI#{$BBU(93dTS`%I74Vr#J z_?`^+=nSPxsz;jVgnM^rBCw}G+9LDNUfM#{6Y~GYZj)^LqWJkn>8Jd1p3gsv@NU^p zrfua`RprW4F)!NI4nIXOql$t)x}4Dn)n|Bf!Av*gjNtlBSZ(FIF5O?7#G==B70&>Y zsZaY*TDoIuT!wdqw1XGFn^iD*?c_uZ)P$E5!)^u1*o#!~l1(Jzajb5|`VWZcUhu6k7Ug#O^*>&}K*uZ-a`3=h# znD`|tHfE>D_0}sAH#>(mv%dP-$)jSlwLz$jZ{POz?3@%@y1nl+Y`BQ1T}RJZq0*yT z7ea6NPfXmg8lW)M$v4uoR0B_W&LX3zJQ3Wlm%xt0CW-!m@29h=nL_LIsBKlWD6nvG z>2PNmk+z}hJoK-mt43|d&k#C9hIF(n9{*7M<|A-aU*g#D{VuCE;+3{D3M2H`N-T`VSKF-5^(i-TN2+{V2G;Z;MKoRuIl?% z#TZ9=$f2G!d#=B1^SKIo2@BmwqtgpM=$P<<(~2*c$k8LUsCM!Ni-p0D2x0VtxioHX ze1ve?U@iE}Zyct_m%8(0+Vm9tIK@I*Lh#CmRH|%gd`ZDf(N_nVD{p+QpZPTc zVOCg~rk{iv5d*fn56w=q3t;XD>rQSM(rD^OFm8+jGah}ctirncIj+&WM=dQPM-{HQ z?6_q?fsxH&`GU6xqZ5S2h4WY)&(h@=K*>QkCes zG1eHUfLW)xuxarp_^3)_-5s;>EWGYWG^Y`--rr>tnxZv0&=OYb@M~ zh$hCRY4ve>)SGujIV?QUe3UTbqmg9r3^PjDaILMc_WpW5Whmfa;dhdY*el$3@FWGQ z1e20=OFab!^^K1_V{YV{uqif`W(RhkoSL5Brz?bE6r--ZUR+yId~-5TlONdEei&v5 zDhy`De#(k-|A6ZHcmXJFsRQ=0ABIh%rw)K*Z++$xX)kmfXS(oR+r8r{+bW25pR@F5 z0}Q`}sD%>LYBd2?9l#!sxyzexWTv&?d5y8Ic@!k8}i@>Cqe!T<}w0_7!9ES zq|7s8=S`K>SV-~Sb`Z2QV!^zqG?6uZj`?K@Z$Uu+nz|jK$0I*%U zcK6L+t_BAeUbnwF%0hyk-#DW8{G9IPSB+he zkB5J56&IZs@y#f^z+2ZqxfJHgM?1h3`_806Y-d+-7SGAe zAL{9wd1FIpn{V2uKT|xayK($;p&2sXN9sc+-5oDYu9s$RW2n?G;ur|C4dmsS%b~fi zw5%4a-Yc_3*_LpRWHW9Q+ZJ$>cg?%`?)C4~zm`&hPGukB5@4rRYpK0k59UmpzehWt zQM=c3iRXK3xPf5Q^-`)=-KF8LW{AVZ=8uC4Q3BxZ>5+!_PIoyp>(4ee%}r z_%*NaMe8!yPp!Ps{WJ;lW}K?pM3#HJc=ndRc>d7%7oF&qI~lIFbL4f6*TfE~!DYl6 zTwj!PaIqQx3IK$GOXswW1L6qPA#aZK#nWiHXC!^d5qEp@Uu~&B)CuFWQxJ1QMm$KM zUMe40ora+Rrs}EYHWWLXhO=NxT}eJZm~Ce*E9+YCFF2?M`fanQ}A+MWS3L`^}E< zHDUY-o^N-Y+V<`V(;l<&zu8GQOcZQ_r&5M5llA1_Y*VMgqQzvFX3=m=&=+1*_nFJ~ zRzgBsY;}W(W$xK$M{~bnDAf;Zw9CLOZ*R_TT_y+N2^(y6t~jdc)mW9APib%6^GHk+ zZ2U@3K7STHNp(|N1YBsa_apMcG~)7(c0*9`$_t^eA=-R;qF^*_AuBI8ASx@bMy;261tXh( zq4PlpNqp>1?QBRg{!UV8dOunh6&lJ^6UwA9ihLEE$*_uf`VjlQ-h0oxK7YHOX3-Pd zess-gZf{4-0PewCgT()H=ciTVdE9rsHP1=mn&q!6CCl{y`{d@CQ?@M|Fqd_kfQF|7 zRO^YeL+Nl^dnL&u!+7;4?xW#dzK@AD>Op~k!er57Ysq1A3&S~b9{&KzL&(5&u3_-2 z=~2v0b+e_AQV#GbuWi*aQq5PQ34nc?@}eF^#bIz$@C^x6s98z%3r@Xb;Va0zwt%@> zgSF-oT;$5=O!>+RlBf<~n_eDY+gr@TO(kmFmn$5UyzoNcDZfWqZ2)=g`+22Usp?W5 z8CI^H#)%2ukY`w*#)%lRSwu2+R`<*vZzv|1d@ZHA+RBm<*x9F@x9dqGv~HE%8Q6?D zke`Zp$QL|>Quhgw3KhGA6b_cH0f+F%#c@*c92`K}Gr{GbJZ5`bNQNd;2Df9_wLYry zold_sXSRZ9L+cx;6N-%1iFg#ecYCu(W|-DH@i5>3zz#*7E>e5u(V?Q$U^THywaCx?%{{u?4uk52n;idOoB&NiQIG7ssU{;&W?&~J&h@U#_RnZX zl>1rOI{6-NHuY@KwwRc{N^4S<#A;&$2Wyo?1-G=br_FlS)fslHTHGeboW3Z7?wH{9 z7M^KWI9BCkhyz7m`FW z*}e+M;DtGJCnO1^wS`TpVXWv6(DPAi117i?lFM(4d_&(i1aqMqQV1=rq69$nf( zWFi_0wM|h>wudFMXFb1B)4Cd{PJIml;H%$LINygf6W>Yyr`8A~PmfwMw*~^`mU{RgRfuC*j@y z`0}aV_xuyC<6VcsCZ}+re7=5+HaEvoozTb=)i5;V!?zcxhW!C{8Cve8kSh* z`R%Nh2#Ra}XG zF*H38k$Fmj@qFLww+}uo{J5-rZOc9_si+j^udaG?j$NyQ;%%Gnh|yY?*Mh$qgC;2e zc;q?{1o*jj-Zv!JIIa05f6X=C#(V7UWE_m-|^|G!T|BdCAU_{3$m5|o`FL!N5$Y%@|=cU>(7*AdU7NS-L zWUU`VE8Mh&0V=CwB5^7UWldzvZkwmLV9jbROlV(4PHMI{HYkdEC?#r_v&zV_gg9sS ztTC~76B#!5&z}J@;zoNO-FAA*@|aCV!};_-x13&Nd`KM7__ULncDIcD=`|2%9Fy3| zyi~N$zAui%Q_>Q3A>|+8teK^+9l0N9-S2CT%sF2yG+*5{8uwXzl^ig^>xi}Ydike) z8ExpOo>+*5ytNdc*TXT^fta<0CUg#;XD6#U_Z5s6Z%Wi@rz)_HFZpEod{3N(ZXTaM z3$&uSYFR(<$%;Tv{(X;_vY8#R#ZFl0F&L{XRP=}NfBnJ%Ti6JZhl*bMdw-P`yMm!1 z=ahqK6r%U?6Zq>_5Zr>0-}ck%a^trsyq*_-pk22Tci$HL`v17zi#PY!%S>tZtPV_9 zna;x0IL5z~a6*pAR~Ag0N%T8_H-I}+yNIGkEn*HN!j`i4o`CU=2@c<6FUQ3qXtioZ zhv8Msvx($yNOG*o69mWBsAWI!%`3_lEL7-ym^h;~(#vz(F@A!+Hf-L%rk zo}+DES`(_122*$;C$g3d)JyGDqw+e;)Ni4u}Fy5YCJCQkJCs|7m9V9QUMN zU*6^ZtLRpB!?aPG7WWelOz>aSBWIfw&6pLv>wpoB6iUeYw(_15qk-A`tK;kF)Q$^+gF%HGQC-`pb}udb|rq9fvypp!yq5P&n^HeD-w3_ z69mK&W|4EyUr#+@;zjQV@3P4PoZ#J~AfC`w#Q3i4<87y5*NWrU91Sis9}DkZ#`_FE zh#}uWRw*1pjwZrI!ZN-Qy~Fgc9O$^&n%n;AOYyquvllJPUIFKNfs2)>CLv9KE9I<$ zL#~m;Y^C(5;I$Jt0xShjCg+&L5hR50#)cIu#XWLYSu@$SnknwzIqJuH4s@6#bQvMv zP3(n`0HBSH?QVi>a#7X&x_!PYBTotY*DT;DiT;l21@-7%3iPhU^$;0qdtX*p|Ik8N z$n3FuD-DVM7&GXA^X};l!c9QB^~vt%lrA3{2f?W6CcsaBZlu^oQ=` zTC^k*KIh!L8h@Q~%p5mL@`WJB2UBkF1wBX^fI)+<~Q3y+q<>SwLN!9#VgBE4O(P#W#506mpGvy-! zCJM5DTHMOV+3}OsH%=sw@2VqtV`RkO?|O#@if>yoS89K}C~()1qd zw?x#Mo&0fQl1O;ao_ktv^Yfs7+Wke}hN^nqS1_F+il`%#OWV2lnM%a+)i?0DS({rW zY7#kIo&7@*``g-*w^epL zd-u&$qFNf5;-?P5XMMk3K83gmYoH0NE{Wpqr~84-wXYN=Oh5v;gXtm1l0>2X^Bfzy zdOsEvT|3o+lqeD2Zua-3UXK7J#MJ>BmC#~OqYU$x&Cp_p3v-XHzktVKapGwJ)A-Ut_D@xnj*yBe2VrO8Z~^V-&G#SkGUaTosIo9yF3A0@7{%oNcvaKo zu2TeL`bw|s$0eqG9KNbYZV*M=wiR;x$SSRdiGz3`{gkEEP?qz3Ppz!hISQJ*m>;3Qj+ZhaiW743@%cSSQUJWzUQ5p6d3t&0O& z%v#6~805H@Zpj}#np%qWBZYMA4jt9j&(Xgw^Pndl@_E$rw!7^h#)l-wrShC1U)t<# zXrO9N3x#Uvs!dciA3fK_C^Yv^wyd_1KOjddSkmrywq~@}*36U(p!isHz`yLA>Ln1> zXR|gmWS=tS3CiR##Yo0UYZTd##(h1^xwnbMHm~ihL}Qbz@;&$_Y){?g3C=mo5L%H$ zfHba{aBUOK2w#KZg0H<1xyl`L4r98f9!O2DU`hCizO#f2VZU5zLsZq` zOth!bYjMrtwq0z;=cF%LC-#Yc`Dh#rXLHO~Q+>CJ`>=bMX`=ZCw19s>l0Jc3t^& zl3=Etm5WLOmK1v%HIHa1s*x)e@`8kGJm5y7TfEY?fO@^HzmD6WZ$rzS3l;im&5k4(LlA*wGq%Mhya^Ui+nyfPz2& z9(LkdPmUOY&!z#cy-#`4AQs+6v$viN3SG!HH(^@xJ9Pa>YFAzmZ_=`$@Mt{%*&^z^ zP!C*TtY!=N{PSb?(#rz91Dp-QH!3D^hke<71%bveiG7rIU*u3^Npz|I61m9i)|MYB zClSME9?#&`P~je}$!@D4Hfi+jOWuJomd>1Qw2X;wGO12(HB<0*!Z-^~0MDyru~BlqSJ zGa67X4tpVd_k(Kb;h4obL7?h4nWpF6B<_yN5fuby@`T2pe{iIL-t1jdiO zG0Ky@xZ}fT5%dGY@mQaBCb0Jh@$@U{>KOXvMMVE~+A($ila272w6ulY7Gzk6;8!Og z;TJcxa|rtXoZ@_8OIpz-=)ftP>aw65X%pc9QaA+P`0v;Z_uS|Ln03qyIgXy(4P8ib ziATh~yEK3`WyZR+*coIoR~T5!k6U!L9 z1o(o^-D21qksmJDp}Oi2zRtC$drhR>BYR_6T?}gV&toHF@vNB!wZ-qimEJZaU?Z#Y zN)JywoITcN)GE+#a*Fel_)Ud;>U=Jy_^o?MMf@$iXv#@a4va5rm@v*+!qU#+>gbw4 z+ln#Mj$Uf9l;#go{%S$|sw(r9r9ap+UGBUIY807HlP{%V&N*VioZG#}YDQ8kgVNXK z&hKALT;3a|eW|`R1$0*5@SqT2AVuXsk{$?$nwjtvJ>3$>_On*p*815`$<9#YsCt((Zv$(vAx21i^(~e1t;>){66ry@Rj4JX*MM|WYVBTnCAFdwUGE>-BD$nX zmjRoc6WR}Bo0FFO+i?|F8LuJAzdsMPFV%kH2aFXdTAX2*XRfR$LZZNbBjvSyG|XC$r$QR*!jb0%#M z&4}MMxmlj;gEd1g#&w1o`16QeQYzrUZbNfa6TjImo*SyP?XgDvF2FAl${q{x_w$nw zzqCo8=eS{bMZul#(#}7V{79AlOW3)GuzCV#Axx|vVor>I;#)5r@XFse3hG}`r_$`5 zQQ=+5_<8t){ov1NKT?}Tp8v|o&5FLvo~)f-5kzB7`sSS>0PXpUpqUQV~6RtMX&@XNRANQ=*H7Czzb`HgdH5Of&xh6(3Oz z&tmM<@U#WpgOnLB!odkvtIjn%o1yK{0na6vQ9$#x5{X6i=kC${2L`LtIe*3sb@ugH z1-2&n4Sloh@9TR$8l>huk#;9_ha=MFxLeRn{*EG`%8>?_L_R-{;Qri(ojEd|z4&Sh zbps%}4**Fc#Y;zQa~8*rL?gI-XCq;qkG*J}zlkH+KRFSpha}E_QP~aisy|4-Vn!@7 zBYbffkb8{E#|9bX99=bn!>G=O{mG$p%lm4 zARrsEw|S9aEz%Iz50r!JFt+GzC)W+9nNA^Jiw+<-;Tm#jVCNk-PA~c}y_h8jEuUb@b z$ND!%mF2=+gIP59QUisYuYxN}!m{Tt^RJqNQoFYDV62n_ROXq5Hl+^YI}Kkkr}Zs3 zzM0mj&brYl$M~$I`X%#4w>-?H?!}c&N-X|tboL$`obMfOJx@U{ny9~TvKlQJ2~v(Ea36h|BcW+lz_6x^Zz3w}@@AWcA2>U<+WeE2rj4uh^3cTK+3||W3i1ug|9vK*TlAA02 zObA|cyLu@yA8^E8BI1162L$itY=EoVPKhkv^B*8g;e59DYEbC2THduw9@aI%_f5u4 z29H(m_KgCYjFU?-b@@lFLS^TXcp#h85zY7k!hJD_=|hQNQkm`9S#{4b4N03C0Y`2P zhvp+4-nGz^FZlASEleGh-Q(nJF@a7v`_O`kF?c`Z1D~+GmLqze0EvM1kGpWxiDt@= zm&HL%525RJ{Klu=4+2m903|ynb2OiAg`CzlFyBCUka;_ai(lEpSdQFu8O+dUXwgrv zw2J@CAVtl{3e^FO6yJee5YpgXV5#3#_Xz?r=+K6^I#fB+MH zI4Boy)3ZA3Hju9!cdz+0AP&u=hzewm$LGIVA+lk?M5g+c)tP9pby(RGQqZel?;B+E z<@dBH8zbPcDpvd5&ngS_-M_tInZDI*OOhL$MgI(o*n_pBLv3&7FMwWPlJb8JoIzE^ zhn&&QLeFn@_quj}3P5wpC7UCQtJ))*HDLmJ_Wf{UCb#$~HI|Z^q4b=MOU(eiz4jbN zRRv5AT*{MPb%Jj738!)y)3cHZvwD6uvT27gdlHogh8BZmjN-XkTG1aN+_p^S!UR6s zp+NLx*jbWE2vwQYktv%YT|{X+TjobbE@<#0{}*8Q=knbs50Z6kRZZUB4Q>(yKz2Z^ z%u4oqZ`dAnFDXxzs79hpQ}SZD;5<7KiP@#VY_uGiGW>1*z_9X}kNOl?zB525vU z9KW;`#bPkpyuIvbV%4FwJCaA~bN_fXdsbE7-VyD6>Mo_tz5cen+(!t{tAjdEEVru) zjEby~w=+W2Y&6hkUJFKdA2cs>N>(Y5ilb@GLNsI_QsVZ@$!I^nbQ?Mqn;%%*loVfL=W2MoWbiXxey9BnHE17!;|*l7^+6mNxsge^c6)1aN5B_HujUmPuN7ZXyCn99 zkRONE2$*9aJ;$CH#;k3QOdsr^;!XVhXC||_(AW4CA0tB4;ZZ3skc@4FTG2OaVoZe%Ma%FNcAXR6sXJJcO z)ZCcf5Sn#nipMw8hlNTQYqBIFCk`9xc7naIN4Asi%uNtfXzr09ZWx$ve(%pC8EAln z4POaM>xO3t>fNnAI)HS@U(WJN5zg5vwRt-4>rk|urD#&O_m%3Li`lwt?|Q+->NCmG zJ7)(pFAn8xd;&ecyQgowNzI|o+!x#E53-(ZsHj9-u+eHrt)}{$)4&eNXR z`-3E4NukkX+63c$9NZmPbJgs9x%8YUYk8bsO#pC5 z^an9~Q11~wkE&&N^YEhQuQFX0Y0ff3{>zfjU6!N`5G#^zoQoVjme+u~^JaMBEBKP8 z9r=Rj>-}cpAYz2Ee$ZBAeM;Ak2mYUG$J;bnrcY5elm#=;*N`rsI{Xl+!_?Tnouf#h z=>NF-a-FD$2?Sx*I>bFlpONmjtV2>qF|#F%io4&*0bh!MeCM9enGPJ1f-fFLvYYBd zvAthzIDE09SfBWTr!IX}4558g^+^q;Z7yy>u_NDS9uFa>pG!D=xk8uH)DDXnsRAhA`NmfzZTc{*Y$&&tu-j)l*I~=^FH*Mms3H{ zT?e!dJRg%M?%@1>nHcHah^fd^mwXK4)wp7n-0NPQyyEr!7-HX;UNJnA2|dqI8Fh8& zo#dBW{pEp*L$@rBm9DVvob3`q(MHOBtT;BUNlT3$G&X1r5BfS#$fOx`P_nJ#kuT;_ zG!uGrFG?~fM-X>Gt7$KYX?%m#c4c)Uckz2^k!@bRX5qz33}MIaZq0abhkMMas~I0w z#>zQ|r@0_kB49};ue=+ll2&BiERG(-`ZdVauv}F^J{VrUOa{C_L9TAla$!1%BU7-Jf}BVB+daH4H+ z^g<67QWGEnrgx6V=xR;{{IcS;hA1FlYk)?w@78A0wO4qy0$y3}w>BxC31BTGeU5$r z{Mdl_qT-Uqri>Cf0zwM>o@*YzrlB%IAA|qbng0;w5AjTl89;`q+lU6`* z2SlAw6Qd=`c`Q`ffVxH6cHY9_aAs8u*wpu;5MZoq>3N-R_0Qa9@-`FNFJ2rYZJKwJ z(`EGIo$nVW5f*}P@$Ob{km*~66w~Zl;mp=(HlUad09B$~8IuSBDPP&w#{a$=k|YqE z(?V!5zT*y@I9AHqB1IY+9L*V7dAoalf+((Qj4YLF+P28kWWRSPIs&*`$Al&`Y3cKM zy-6L-Q{NE`S}Ne$FwpL(d42#hA761_9I)*_zQlIYJhyb=v{pnBRx65*dl$GDA%;m=N^xg-HtkT$Z zaL`r}dtZY*EqmGZ&>LHSpIaQ;*Q_CKM7@wL?~w%|6FwDomFb`8&}P=oxa~l~Sh;+@ z26KfxXh)Ya+4u~tn27eX23WPIeRp?U{_xaKYH@Xa>b*BtZug{&+X)k^i6|Dw{xnx! zc1joNbud@H&=)}%YZyGErRLDNA&MQpu$HJ!3W6xp`_sd_2h^Uu~(S zb^2ieg=dqY^DEeB;h5PMU!t98U z^?#P9`CCs=#K(o(PbdQ}fr6{HWnve(J)R#Zwa{*6M|&g|4->>v%6Jqm@oWQGB(m$R zDt{GLvHNW3PKe46zC2EAGi|-DgKHHB)FL|IcW0d1iCbG;c^}I5eadJ)^d88_5)gpJ zjeu_g;I`seYygtjMPK3VOp7DBVO3)`#Dk{~OL_k|=XVElSaqR8M^(S!`{2LaUdY*4pk zIM>F_mtbGMU*>qYw3k>h*K0J~l1qE@ScU;8StjW^554dk?GHg+d?OU8UgwSxn*!sJ z3{fuNW^TapNO^!@JBNsWknkEvAqD)Ex?FWVo-8~4;qt@fx2L$UmKV2T+Yi7-_<_R3 zsF!w#S7s=nj9iH4v4#K`#8_jY$0A(lcw5dW>N*V2YR$8BliV1dJq_x+M5K)dbhY`d z#1*~_zRgzb&v%S@mcNK#za;?2C_-7gfdP;Td{G~m&`W#a%aCYu+G#eMZ+y>%9*ya7 zS5S5gCekK_44yQbkv0)0lz_SPMCv(GNM%xv`PXYfMqVsWpbfz*ST>-Ez)*5H6_QlP zmkALrr><=F`W`)TkA@&=A2GF|`}q0RLd#Z(RJ; zdqY|aXt0@I<)3AklnOzzZ0e0GAD}&)?DFoTT14=KAgT8!DHQlImii10LAGx~87yk8 z=d}v$pu&P|KoECK46Dj}iDS5LoU?Uh)SPvA>KyD@Mculq&HEMqP0Y>v`CMpo#naa@ z0qZk$3_mS-2`$Hoi@6w6%tO8GnCvk;s<$ttUPf;E%4LTm$md<>&u8y?q$1Zw2(r1s8J~DxhEOYL)Z0_a zw`lusZ{gq0G-Up0rZm0b;m@J%+dReu^tKH~P@U3~mYemH@!;ld2>J8ZItY3JHKkU8BvTJ@C|A4FIXM5o zq%8XY@0RShpwfEYEib$B`|Ja<2U_?A-+*uDtZ-n$=^Uk_xeuD!BRg%fF^UokySYjz zu1@RgrlkGN4MHy2mm=Uq@X zxS9Q#)VECQeipG8OCvNs31Z=FSoU^6mUh7ZL5(Jkg;QxYS%Ctp09gRGq&o!8e%1kZ za-P{|$%nD;xvLCrE_akNllvYqA#2WSL$OL_V=CpkH7VS*OL*Xu*cHn~5D$)${a*1~ z{7LUoixYG+VZ_J2kGS6Hw|HCW;fjsb-6l8;-0h86Wy8{m^MJT`+5?6e^^+ccW^j`*bpBB%$mZe()+E39x@eEODHX=!wz;B$t^nhFi|!bX2E` zKLhy`!v*dD=%oJl!0i9q*3X}WbV?+!Z1-;ld~EIRx-R&hZohzwcFLtKk|x{f%^qRy zM8maH0&^Jbh!=XkCl$9?66BE3Yd$D9b(F zycl;2Qvdjo^uFH}+Dva-5kj|REWf*_-qsTKr%If29NcIrhJx%b;M+7~g$5!pQ*;Na z$8$qS)6I6yD7nq^5tW&I!OlJ^z}K>v_HOji(+;?hFICH`b7odNL4~Jr%kwSEi9JY4 zZa#Skr`rP62Gf6UqB&02ngX}`QZG_MqVySznCrGsq`)cqhWY|x^@+*rJ`yRaBG7k- zM4j%q61#bpwUvsDtS1ixZaih~} zz>bA7&uk=5!Vw&wSFM}vp8CpiMx2jmEkBDk5s z9u(q4m8%!7ywAy?wX`?)AP?NzYMD3ov+=Dh+Ua>%&|AVR_;a4KF7HwkTSP-o*?O5hch6V5U`557CP9OQJJa^dx>0dHIiCW{X{NAx`{06GMu!)>dudT4V z*m0BTrO8TOV%ewFU3IqS1@BElZAkdUw>33$6oYoi9&~g+%dW<=YaF^u6<>tUtWdq@ z4o0_VFIDWnQ2)^7mN326Zse^yLYd}PsF{Zc2k&?FVN^V;p)U?6opom{)u{9=<+m!(So4aG=f5gjT1SrCAAHWKaM4z zgo0k^d{i>(5SqF9@bDgJf?~m4CwkQ76W>49Zva9Y&S2}USB41Qr{$-7-~I2*)1lCP zqEo`)!%b*A9t99+{;hD%e+PRsgc>_*Z1Xav_2sYV@fUN2|CzTv6W}X%_#oB!V5E-6 zZv=o-PHzUm#$O-s6rC}XR(iKf*f7q}`CG{$@)%@Con1#n+=kGDR~5}4;rR_p^M)CC z+CSOBg13$E`a_8?DAwLkKvSWI)A+k{@QvfC}rqeJmi;(rLnTeR#;zyWPuSL>*; zyh^GXg`| zms%3#iR*Q)Q;C>q3XkP}$&g$Pw&XEX57*oFG-wnG(hhlV_yCtklLx9zdbl3FecoU* z7llyi@{p$Y0_ReP*_x>R5fmN6TD7bmccHrlJjyJv_A&3heMUn~Ka2ox1Epj1|7Fdcpj>lrsfr_S^-409vx zMUrGe4k#m5NeFFv4EQ;N&|hY+Ruz zoDe4e7rk?gndEVgus{KkC$(<)>F(Re=MG4c23H|3*zaIVdu$xTR7J!&yHXxQAX>nj zOic{@xNwx%FQ|I-K7wOJ@>#J_|NM#oXCd?7BtO~!O{lOWq+>B1Qx=svqjv&Hi_BlY z|75qp)JPiU9SNDbr=@ALQ(!Q=VYk4;`Wu{X%y&RlOqt0XBGFLU?45mTuu}>+yMxdixR%1>}t+Tc0@5-ciCq6{H_dEhIPlKBS+zhe@ z%uFLexN^V!7vheH#9?UZckFoIn%&o%l=Zi?9N4W;dBRfk{pxy2gT*h;Q2k0m3vF!oog0Y7 zEe&t!XN!1oW#)5m+g`4Bv572T&nCKIp7-{_bufy60VhN3vjvfo8;`J88iZ8_{=gg# zT#|D)Qh!Qpp=d>NlRD@C=QSz6-iQ_!*qmmnAlD*aJWY3 zmQ zF^?~S@=(QeO3B;jE+{Ox44SX0pPpvP-SPF8)=aQ`&4g7|or-UpT_ zX^ypKHvoOco$Z^%R}t$n*2W^mt{uNVo8$e!oY~<%$iM=|LvVF!J(L8EgOX9{+*kWQCZB%D@0c`rIAK zj}|atbs;6syFC-lj-bJY|AMk_KURhr(tb5mxqZ*+iIsA#%3@;i4%A(u3)N4+`{(8{ z7A%VL{(J{1z1V*|fG0oV^Mg^0m-YQo^5iD8#gz!F;N%8*hK)l9lv2@*!$=UuY!x&z zJ~KM55fP(vb1N$qhy0#%eA$W$EHk26vZqyI1l0CT-RtTFi=B^pjt3X0&T7xMqMALe z=X&jU&Belgo;PB7q!nJ@_d}d~ti)sX#WZvSCIn-}@_NgB3rCo_-+oqBDr@K8(DZFG z?>}+wMbpYH+70d&VmE#ArjK|MyjEmceN}vGvkug}pi-%IvulJqXa|O4ElhMRP!emw zdkWDjnYBL_V=ygb0brIvVMR~c=&9?F&o-6!)%U23P{2)cWfJ|g%Rk5doPim$>Wl)+ zM-D9?6M`6p#3U*G)*yyyC`NR&e>Nbzc>gMBe_R#iysk|*Hbd*A@6sXie|vc5-%(qC zm7eA^JI|n?t)3{TtlI0B>zx;VgUGyLCZ6Pck(s9OB!Boff*GQY(rrmPjPWz*u|XNo zK&MTxBI<&c;2*$BGzRzbYWT2DK-l@Ytsa|^*)P%m)2a_N2R8r%@e)8&f%Vxz2GZFB zC;0!C_6gkLo&^L7nel7RY)y1+`)kh3aU4O9+HX@mO@sjOHK+IJsWu@sz);EXVuvzX zj|8d5a$m{PQpyE=x8K!-50H}?Rux3@f5^)ES$j`Q`3)2ad=TGfEzYf@WPnuPOz*JS z!_YcmRpa4_6U&&R04Yp$B72F-yE{gp(wu3326S6zC0J9l*v+CdXI zos37qRM?7KGm7YcyQ`zRxY2uo~v9df9v<=I^gNe@`ttxz!DX zWK=)CKtpiL=57m8eW6$5X7@sAp_jEr4ZW#})Y=7N8#-suFx)Pw-cI06W^<2_Jnf$G zj{3XvOE{}v&mfDFWXz4X6TS9EjbJD0oYp z`1f^Q03%bBp*yVTeCBH8UL;)~9Q-?XlrWw=zJl0G4cyr9$DxOGY%zMF_WxnAY?7G9 z1<<3z`;GS6S5NaR)%_&lYyTtMZuFYc$c^AD)W?! zn3g$a3ky5K9(HYy;1@MSE7q(|#4NACrKvW3W*$6`@XdJ(J(OKgnjR*H%7cMzo)&`x z6#fWU&3wr8ycos6vYY2Qz9S`zx~cb*=yS4~3UZwSDg%fsmBeIn?BJe2ZO2O5HHFe> z;ufOFo&4lpPeNdvweKQ$%nwg92?352YvoYq|NHED$SDp873O^H$f5$-HQyKGOP>#+ zbl1k!eKWM!uX|`O%n7L(zj=^~Z>DBol4OmeVd!a0k0%?W|EQuHk)mPNr;rDuk z-)}tpCKbzL=f}&$eIQh5=++oK8wOsnhiBE>+Y;5|_u=NFDyZ=6C_Rd2u>je%UDhG)N&)bIh@-nIIksu;oJ56VJ)r^yLjfDgpu1_O)!_TAC$nb2v zM;P66i+{@mCz<8RtR~-Hlaao>)%suiSKt_CkX`oJ=+rd1|IaDS4(T~AKuxD+;s8+Z zBE|Lxe$&5sf)Q!a2YyA09P$4p*#92d{J+3E|A(7yLBqpYiJAiFu8+^&gp2;7U$CXu zLFVE8|1BAD_r*xPGYxf{Tu|=kKiC73(nSZ#Zx9pu0K#;F=sqRBKHa#_2SVRRJc`yA zp!D=}_4DyBtTwl9_9Y@I$PLefKtB6`$_)x+Zqg6vf4@j;Zv8_@?_Z8qNHx)WL%UqK@SzP0tI6+nDUdmjzqd@%K?%aQELIXTyVsiIBOeI1p4FNV<&U zRSy_?7cj_UtKV7|+*8)o+w`nZ65#4NN_><5UGDdiPZqPYo)l`H4UGA`lx-HKZRXhx zO3EtK!O4P^N{a(Yids9x!G-2HEr!)1Z|{mxqS|{`U}%d7n9Uj8amdAT#>^C()`Mxn zZWUlu2*)w4On!pgZ=PEnEMJrt(QVgB8`>ZO{5aL&P7^yJe#w zFStdkks=W?Zfw}=XW^TPxQ3r3I{m=%z@VU#5~?~Y^cefkBx94YE<8SpUBp%c#rlmE z^5hAReDz+f z|2Ixp-{;yK+umMC=YZMziXNX%ehP-_;i+zw*l*G5?ojX)+RlEFb0=%+qd^R?8z9W5 z>mKQ!Sm3n*eEz?1vGNl?J+Hw&Kp;sut?apXbPCHfCmRQVkR63W*w=yD*T zgCXT4H+YcY$iy&XJk0C*8R%Z8tgH_65>Z9ChL2L?i7T6@ z789n-ZyT2R=p^C5Gs`A}aw1{hK5FshE7pIM%-o0rm#i0+z)E^LVy*LRR5n=BeCRQz zRm_nG<;0k|(CPa_^)GB4Z26)yo;Im%{`=7~^%eZn{RjiEU`8V~SXD}FeKfPk!-rZt zcKZPv9tQqn>@`4sm+Y#aNdAxmMe9ixUM;b4Pj9+30qWoK0MgReeJ714^q|h0gDOxZ z`f?CNj+_P$^PtX4E|l-pBVo%nN+}Z#M4#+YjN65t7JY2my7c4b-1bR}WB7G^yBzz@ z#}`ijV-~>0d|)?zW(1mW=g)P~=J%7^8eg|KtQM%)B+)WCs(x;Z9BBTkxg~({M?gbv zs<8Qgqr(Sk6ulZNB3mRkto!x4JYy zc^wh24QdT0E{NPTAFTFhxy%O3XPXyGl&pE|a8~~FgVjwtep&M8fTY7UDsv|Wvo z`t@@A#<$GT(?7AdPJgql=-BRD>D_ZV6`T2iXBoZFV^+MCk)MA(mc_}#>!WO&3n-iZ j#9B>3w{#x(&;K{f|HO*~ey diff --git a/tgstation.dme b/tgstation.dme index a4ef909bccc..0840ced6451 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1693,7 +1693,6 @@ #include "code\modules\cargo\bounty.dm" #include "code\modules\cargo\bounty_console.dm" #include "code\modules\cargo\centcom_podlauncher.dm" -#include "code\modules\cargo\console.dm" #include "code\modules\cargo\coupon.dm" #include "code\modules\cargo\export_scanner.dm" #include "code\modules\cargo\exports.dm" @@ -1701,6 +1700,7 @@ #include "code\modules\cargo\gondolapod.dm" #include "code\modules\cargo\goodies.dm" #include "code\modules\cargo\order.dm" +#include "code\modules\cargo\orderconsole.dm" #include "code\modules\cargo\packs.dm" #include "code\modules\cargo\supplypod.dm" #include "code\modules\cargo\supplypod_beacon.dm" @@ -2551,6 +2551,7 @@ #include "code\modules\modular_computers\file_system\programs\atmosscan.dm" #include "code\modules\modular_computers\file_system\programs\borg_monitor.dm" #include "code\modules\modular_computers\file_system\programs\bounty_board.dm" +#include "code\modules\modular_computers\file_system\programs\budgetordering.dm" #include "code\modules\modular_computers\file_system\programs\card.dm" #include "code\modules\modular_computers\file_system\programs\cargobounty.dm" #include "code\modules\modular_computers\file_system\programs\cargoship.dm" diff --git a/tgui/packages/tgui/interfaces/Cargo.js b/tgui/packages/tgui/interfaces/Cargo.js index 9854e21833a..584a6996fa7 100644 --- a/tgui/packages/tgui/interfaces/Cargo.js +++ b/tgui/packages/tgui/interfaces/Cargo.js @@ -6,6 +6,19 @@ import { formatMoney } from '../format'; import { Window } from '../layouts'; export const Cargo = (props, context) => { + return ( + + + + + + ); +}; + +export const CargoContent = (props, context) => { const { act, data } = useBackend(context); const [tab, setTab] = useSharedState(context, 'tab', 'catalog'); const { @@ -14,53 +27,48 @@ export const Cargo = (props, context) => { const cart = data.cart || []; const requests = data.requests || []; return ( - - - -
- - setTab('catalog')}> - Catalog - + + +
+ + setTab('catalog')}> + Catalog + + 0 + && 'yellow'} + selected={tab === 'requests'} + onClick={() => setTab('requests')}> + Requests ({requests.length}) + + {!requestonly && ( 0 + icon="shopping-cart" + textColor={tab !== 'cart' + && cart.length > 0 && 'yellow'} - selected={tab === 'requests'} - onClick={() => setTab('requests')}> - Requests ({requests.length}) + selected={tab === 'cart'} + onClick={() => setTab('cart')}> + Checkout ({cart.length}) - {!requestonly && ( - 0 - && 'yellow'} - selected={tab === 'cart'} - onClick={() => setTab('cart')}> - Checkout ({cart.length}) - - )} - -
- {tab === 'catalog' && ( - - )} - {tab === 'requests' && ( - - )} - {tab === 'cart' && ( - - )} - - + )} +
+
+ {tab === 'catalog' && ( + + )} + {tab === 'requests' && ( + + )} + {tab === 'cart' && ( + + )} + ); }; @@ -75,6 +83,7 @@ const CargoStatus = (props, context) => { message, points, requestonly, + can_send, } = data; return (
{ )}> - {docked && !requestonly && ( + {docked && !requestonly && can_send &&(