From d96c8ce4a9e871b5db7132dc5ff8cc3e502e7f69 Mon Sep 17 00:00:00 2001 From: GreaseMonk <1354802+GreaseMonk@users.noreply.github.com> Date: Sun, 24 Mar 2024 23:49:31 +0100 Subject: [PATCH] Shipyard RCD (#1130) * Remaining fixes and add to research etc * Bump price * Bump material price * Fix shipyard ammo refuel normal rcd * Fix sounds * Fix sounds --- .../Systems/ShipyardSystem.Consoles.cs | 5 +- .../Access/Components/IdCardComponent.cs | 18 ++++ .../RCD/Components/RCDAmmoComponent.cs | 7 ++ Content.Shared/RCD/Components/RCDComponent.cs | 14 +++ Content.Shared/RCD/Systems/RCDAmmoSystem.cs | 10 ++ Content.Shared/RCD/Systems/RCDSystem.cs | 101 ++++++++++++++++-- .../Components/ShuttleDeedComponent.cs | 4 +- .../en-US/_NF/research/technologies.ftl | 1 + .../_NF/shipyard/shipyard-rcd-component.ftl | 8 ++ .../VendingMachines/Inventories/engivend.yml | 2 + .../Entities/Structures/Machines/lathe.yml | 2 + Resources/Prototypes/Research/industrial.yml | 13 +++ .../Entities/Objects/Tools/shipyard_rcd.yml | 37 +++++++ .../Prototypes/_NF/Recipes/Lathes/devices.yml | 18 ++++ .../Prototypes/_NF/Recipes/Lathes/tools.yml | 20 ++++ .../Objects/Tools/rcd.rsi/ammo-shipyard.png | Bin 0 -> 1927 bytes .../Objects/Tools/rcd.rsi/icon-shipyard.png | Bin 0 -> 2679 bytes .../Textures/Objects/Tools/rcd.rsi/meta.json | 6 ++ 18 files changed, 254 insertions(+), 12 deletions(-) create mode 100644 Resources/Locale/en-US/_NF/shipyard/shipyard-rcd-component.ftl create mode 100644 Resources/Prototypes/_NF/Entities/Objects/Tools/shipyard_rcd.yml create mode 100644 Resources/Prototypes/_NF/Recipes/Lathes/tools.yml create mode 100644 Resources/Textures/Objects/Tools/rcd.rsi/ammo-shipyard.png create mode 100644 Resources/Textures/Objects/Tools/rcd.rsi/icon-shipyard.png diff --git a/Content.Server/Shipyard/Systems/ShipyardSystem.Consoles.cs b/Content.Server/Shipyard/Systems/ShipyardSystem.Consoles.cs index b740900b66d..629d2e12ed4 100644 --- a/Content.Server/Shipyard/Systems/ShipyardSystem.Consoles.cs +++ b/Content.Server/Shipyard/Systems/ShipyardSystem.Consoles.cs @@ -36,6 +36,7 @@ using Content.Server.Shuttles.Components; using Content.Server.Station.Components; using System.Text.RegularExpressions; +using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; namespace Content.Server.Shipyard.Systems; @@ -438,12 +439,12 @@ private void SendSellMessage(EntityUid uid, EntityUid? player, string name, stri private void PlayDenySound(EntityUid uid, ShipyardConsoleComponent component) { - _audio.PlayPvs(_audio.GetSound(component.ErrorSound), uid); + _audio.PlayPvs(_audio.GetSound(component.ErrorSound), uid, AudioParams.Default.WithMaxDistance(0.01f)); } private void PlayConfirmSound(EntityUid uid, ShipyardConsoleComponent component) { - _audio.PlayPvs(_audio.GetSound(component.ConfirmSound), uid); + _audio.PlayPvs(_audio.GetSound(component.ConfirmSound), uid, AudioParams.Default.WithMaxDistance(0.01f)); } private void OnItemSlotChanged(EntityUid uid, ShipyardConsoleComponent component, ContainerModifiedMessage args) diff --git a/Content.Shared/Access/Components/IdCardComponent.cs b/Content.Shared/Access/Components/IdCardComponent.cs index 26e83c5586e..9459f6c1910 100644 --- a/Content.Shared/Access/Components/IdCardComponent.cs +++ b/Content.Shared/Access/Components/IdCardComponent.cs @@ -1,6 +1,7 @@ using Content.Shared.Access.Systems; using Content.Shared.PDA; using Content.Shared.StatusIcon; +using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; @@ -40,4 +41,21 @@ public sealed partial class IdCardComponent : Component /// [DataField, ViewVariables(VVAccess.ReadWrite)] public bool BypassLogging; + + + // Frontier + [DataField("soundError")] + public SoundSpecifier ErrorSound = + new SoundPathSpecifier("/Audio/Effects/Cargo/buzz_sigh.ogg"); + + // Frontier + [DataField("soundSwipe")] + public SoundSpecifier SwipeSound = + new SoundPathSpecifier("/Audio/Machines/id_swipe.ogg"); + + // Frontier + [DataField("soundInsert")] + public SoundSpecifier InsertSound = + new SoundPathSpecifier("/Audio/Machines/id_insert.ogg"); + } diff --git a/Content.Shared/RCD/Components/RCDAmmoComponent.cs b/Content.Shared/RCD/Components/RCDAmmoComponent.cs index 7b1fc001d4d..16a92b6aa25 100644 --- a/Content.Shared/RCD/Components/RCDAmmoComponent.cs +++ b/Content.Shared/RCD/Components/RCDAmmoComponent.cs @@ -13,6 +13,13 @@ public sealed partial class RCDAmmoComponent : Component /// [DataField("charges"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] public int Charges = 5; + + /// + /// ~~~ Frontier ~~~ + /// A flag that limits RCD to the authorized ships. + /// + [DataField("isShipyardRCDAmmo"), AutoNetworkedField] + public bool IsShipyardRCDAmmo; } // TODO: state??? check if it desyncs diff --git a/Content.Shared/RCD/Components/RCDComponent.cs b/Content.Shared/RCD/Components/RCDComponent.cs index 8e1032884aa..68271b1a102 100644 --- a/Content.Shared/RCD/Components/RCDComponent.cs +++ b/Content.Shared/RCD/Components/RCDComponent.cs @@ -48,4 +48,18 @@ public sealed partial class RCDComponent : Component [DataField("floor", customTypeSerializer: typeof(PrototypeIdSerializer))] [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] public string Floor = "FloorSteel"; + + /// + /// ~~~ Frontier ~~~ + /// A flag that limits RCD to the authorized ships. + /// + [DataField("isShipyardRCD"), AutoNetworkedField] + public bool IsShipyardRCD; + + /// + /// ~~~ Frontier ~~~ + /// The uid to which this RCD is limited to be used on. + /// + [DataField("linkedShuttleUid"), AutoNetworkedField] + public EntityUid? LinkedShuttleUid = null; } diff --git a/Content.Shared/RCD/Systems/RCDAmmoSystem.cs b/Content.Shared/RCD/Systems/RCDAmmoSystem.cs index 9481d299aaa..9136e337e28 100644 --- a/Content.Shared/RCD/Systems/RCDAmmoSystem.cs +++ b/Content.Shared/RCD/Systems/RCDAmmoSystem.cs @@ -42,6 +42,16 @@ private void OnAfterInteract(EntityUid uid, RCDAmmoComponent comp, AfterInteract return; var user = args.User; + + // ## Frontier - Shipyard RCD ammo only fits in shipyard RCD. + // At this point RCDComponent is guaranteed + EnsureComp(target, out var rcdComponent); + if (rcdComponent.IsShipyardRCD && !comp.IsShipyardRCDAmmo || !rcdComponent.IsShipyardRCD && comp.IsShipyardRCDAmmo) + { + _popup.PopupClient(Loc.GetString("rcd-component-wrong-ammo-type"), target, user); + return; + } + args.Handled = true; var count = Math.Min(charges.MaxCharges - charges.Charges, comp.Charges); if (count <= 0) diff --git a/Content.Shared/RCD/Systems/RCDSystem.cs b/Content.Shared/RCD/Systems/RCDSystem.cs index 2b9852a6945..f93ecb12fd2 100644 --- a/Content.Shared/RCD/Systems/RCDSystem.cs +++ b/Content.Shared/RCD/Systems/RCDSystem.cs @@ -1,3 +1,4 @@ +using Content.Shared.Access.Components; using Content.Shared.Administration.Logs; using Content.Shared.Charges.Components; using Content.Shared.Charges.Systems; @@ -10,6 +11,7 @@ using Content.Shared.Physics; using Content.Shared.Popups; using Content.Shared.RCD.Components; +using Content.Shared.Shipyard.Components; using Content.Shared.Tag; using Content.Shared.Tiles; using Robust.Shared.Audio; @@ -50,6 +52,7 @@ public override void Initialize() SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent(OnDoAfter); SubscribeLocalEvent>(OnDoAfterAttempt); + SubscribeLocalEvent(OnIdCardSwipeHappened); // Frontier } private void OnExamine(EntityUid uid, RCDComponent comp, ExaminedEvent args) @@ -70,6 +73,56 @@ private void OnUseInHand(EntityUid uid, RCDComponent comp, UseInHandEvent args) args.Handled = true; } + /** + * Frontier - ability to swipe rcd for authorizations to build on specific grids + */ + private void OnIdCardSwipeHappened(EntityUid uid, IdCardComponent comp, ref AfterInteractEvent args) + { + if (args.Handled) + return; + + if (args.Target is not { Valid: true } target || !args.CanReach) + return; + + var rcdEntityUid = target; + + // Is this id card interacting with a shipyard RCD ? if not, ignore it. + if (!TryComp(rcdEntityUid, out var rcdComponent) || !rcdComponent.IsShipyardRCD) + { + args.Handled = true; + return; + } + + // If the id card has no registered ship we cant continue. + if (!TryComp(comp.Owner, out var shuttleDeedComponent)) + { + _popup.PopupClient(Loc.GetString("rcd-component-missing-id-deed"), + uid, args.User, PopupType.Medium); + _audio.PlayPredicted(comp.ErrorSound, rcdEntityUid, args.User, AudioParams.Default.WithMaxDistance(0.01f)); + args.Handled = true; + return; + } + + // Swiping it again removes the authorization on it. + if (rcdComponent.LinkedShuttleUid != null) + { + _popup.PopupClient(Loc.GetString("rcd-component-id-card-removed"), + uid, args.User, PopupType.Medium); + _audio.PlayPredicted(comp.SwipeSound, rcdEntityUid, args.User, AudioParams.Default.WithMaxDistance(0.01f)); + rcdComponent.LinkedShuttleUid = null; + } + else + { + _popup.PopupClient(Loc.GetString("rcd-component-id-card-accepted"), + uid, args.User, PopupType.Medium); + _audio.PlayPredicted(comp.InsertSound, rcdEntityUid, args.User, AudioParams.Default.WithMaxDistance(0.01f)); + rcdComponent.LinkedShuttleUid = shuttleDeedComponent.ShuttleUid; + } + + Dirty(rcdComponent.Owner, rcdComponent); + args.Handled = true; + } + private void OnAfterInteract(EntityUid uid, RCDComponent comp, AfterInteractEvent args) { if (args.Handled || !args.CanReach) @@ -111,10 +164,48 @@ private void OnAfterInteract(EntityUid uid, RCDComponent comp, AfterInteractEven args.Handled = true; - if (_doAfter.TryStartDoAfter(doAfterArgs) && _gameTiming.IsFirstTimePredicted) + // IsAuthorized is part of frontier + if (IsAuthorized(gridId, uid, comp, args) && _doAfter.TryStartDoAfter(doAfterArgs) && _gameTiming.IsFirstTimePredicted) Spawn("EffectRCDConstruction", location); } + /** + * Frontier - Stops RCD functions if there is a protected grid component on it, and adds shipyard rcd limitations. + */ + private bool IsAuthorized(EntityUid? gridId, EntityUid uid, RCDComponent comp, AfterInteractEvent args) + { + if (gridId == null) + { + return true; + } + var mapGrid = _mapMan.GetGrid(gridId.Value); + var gridUid = mapGrid.Owner; + + // Frontier - Remove all RCD use on outpost. + if (HasComp(gridUid)) + { + _popup.PopupClient(Loc.GetString("rcd-component-use-blocked"), uid, args.User); + return false; + } + + // Frontier - LinkedShuttleUid requirements to use Shipyard RCD. + if (comp.IsShipyardRCD) + { + if (comp.LinkedShuttleUid == null) + { + _popup.PopupClient(Loc.GetString("rcd-component-no-id-swiped"), uid, args.User); + return false; + } + if (comp.LinkedShuttleUid != gridUid) + { + _popup.PopupClient(Loc.GetString("rcd-component-can-only-build-authorized-ship"), uid, args.User); + return false; + } + } + + return true; + } + private void OnDoAfterAttempt(EntityUid uid, RCDComponent comp, DoAfterAttemptEvent args) { // sus client crash why @@ -162,14 +253,6 @@ private void OnDoAfter(EntityUid uid, RCDComponent comp, RCDDoAfterEvent args) var tile = mapGrid.GetTileRef(location); var snapPos = mapGrid.TileIndicesFor(location); - // I love that this uses entirely separate code to construction and tile placement!!! - - var gridUid = mapGrid.Owner; - var ev = new FloorTileAttemptEvent(); - - if (HasComp(gridUid) || ev.Cancelled) // Frontier - Remove all RCD use on outpost. - return; - switch (comp.Mode) { //Floor mode just needs the tile to be a space tile (subFloor) diff --git a/Content.Shared/Shipyard/Components/ShuttleDeedComponent.cs b/Content.Shared/Shipyard/Components/ShuttleDeedComponent.cs index ef66b5ff989..1ff534afbcb 100644 --- a/Content.Shared/Shipyard/Components/ShuttleDeedComponent.cs +++ b/Content.Shared/Shipyard/Components/ShuttleDeedComponent.cs @@ -1,9 +1,11 @@ +using Robust.Shared.GameStates; + namespace Content.Shared.Shipyard.Components; /// /// Tied to an ID card when a ship is purchased. 1 ship per captain. /// -[RegisterComponent, Access(typeof(SharedShipyardSystem))] +[RegisterComponent, NetworkedComponent, Access(typeof(SharedShipyardSystem))] public sealed partial class ShuttleDeedComponent : Component { public const int MaxNameLength = 30; diff --git a/Resources/Locale/en-US/_NF/research/technologies.ftl b/Resources/Locale/en-US/_NF/research/technologies.ftl index b706785d5fe..450e9a9d736 100644 --- a/Resources/Locale/en-US/_NF/research/technologies.ftl +++ b/Resources/Locale/en-US/_NF/research/technologies.ftl @@ -1,4 +1,5 @@ research-techology-advanced-personal-propulsion = Advanced Personal Propulsion +research-technology-rapid-construction = Rapid Construction research-technology-hardsuits-basic = Basic Hardsuits research-technology-hardsuits-specialized = Specialized Hardsuits research-technology-hardsuits-advanced = Advanced Hardsuits diff --git a/Resources/Locale/en-US/_NF/shipyard/shipyard-rcd-component.ftl b/Resources/Locale/en-US/_NF/shipyard/shipyard-rcd-component.ftl new file mode 100644 index 00000000000..ee6dac465c4 --- /dev/null +++ b/Resources/Locale/en-US/_NF/shipyard/shipyard-rcd-component.ftl @@ -0,0 +1,8 @@ +## UI +rcd-component-missing-id-deed = No ship registered to this ID +rcd-component-can-only-build-authorized-ship = Can only build on authorized ships! +rcd-component-no-id-swiped = Swipe id card on RCD to authorize. +rcd-component-use-blocked = The RCD whirrs, but nothing happens. +rcd-component-id-card-accepted = You swipe the id card and the RCD makes a accepting noise. +rcd-component-id-card-removed = The RCD powers down, unauthorizing it. +rcd-component-wrong-ammo-type = Wrong type of RCD ammo. diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/engivend.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/engivend.yml index 292d93ab452..0c11ae10c2e 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/engivend.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/engivend.yml @@ -11,3 +11,5 @@ GeigerCounter: 10 # Frontier PowerCellMedium: 15 # NetworkConfigurator: 15 + ShipyardRCD: 10 + ShipyardRCDAmmo: 20 diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index e4e23c8ddc8..cfa378dec6f 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -283,6 +283,8 @@ - AnomalyLocatorWide - RCD - RCDAmmo + - ShipyardRCD # Frontier + - ShipyardRCDAmmo # Frontier - Scalpel - Retractor - Cautery diff --git a/Resources/Prototypes/Research/industrial.yml b/Resources/Prototypes/Research/industrial.yml index 37beffccbbc..eafa72a0b21 100644 --- a/Resources/Prototypes/Research/industrial.yml +++ b/Resources/Prototypes/Research/industrial.yml @@ -198,3 +198,16 @@ - PowerCellMicroreactor technologyPrerequisites: - AdvancedPowercells + +- type: technology + id: RapidConstruction + name: research-technology-rapid-construction + icon: + sprite: Objects/Tools/rcd.rsi + state: icon + discipline: Industrial + tier: 2 + cost: 10000 + recipeUnlocks: + - ShipyardRCD + - ShipyardRCDAmmo diff --git a/Resources/Prototypes/_NF/Entities/Objects/Tools/shipyard_rcd.yml b/Resources/Prototypes/_NF/Entities/Objects/Tools/shipyard_rcd.yml new file mode 100644 index 00000000000..a2ef2214564 --- /dev/null +++ b/Resources/Prototypes/_NF/Entities/Objects/Tools/shipyard_rcd.yml @@ -0,0 +1,37 @@ +- type: entity + name: Shipyard RCD + parent: BaseItem + id: ShipyardRCD + description: An advanced construction device which can place/remove walls, floors, and airlocks quickly. It has a slot to swipe ID cards. + components: + - type: RCD + isShipyardRCD: true + - type: LimitedCharges + maxCharges: 5 + charges: 5 + - type: UseDelay + - type: Sprite + sprite: Objects/Tools/rcd.rsi + state: icon-shipyard + - type: Item + size: Normal + - type: Clothing + sprite: Objects/Tools/rcd.rsi + quickEquip: false + slots: + - Belt + - type: PhysicalComposition + materialComposition: + Steel: 5000 + Plastic: 1000 + - type: StaticPrice + price: 500 + +- type: entity + id: ShipyardRCDEmpty + parent: ShipyardRCD + suffix: Empty + components: + - type: LimitedCharges + maxCharges: 5 + charges: 0 diff --git a/Resources/Prototypes/_NF/Recipes/Lathes/devices.yml b/Resources/Prototypes/_NF/Recipes/Lathes/devices.yml index 68f30ba047f..6565ac3cba6 100644 --- a/Resources/Prototypes/_NF/Recipes/Lathes/devices.yml +++ b/Resources/Prototypes/_NF/Recipes/Lathes/devices.yml @@ -35,3 +35,21 @@ Glass: 500 Plastic: 50 Gold: 100 + +- type: latheRecipe + id: ShipyardRCD + result: ShipyardRCDEmpty + category: Tools + completetime: 4 + materials: + Steel: 1000 + Plastic: 300 + +- type: latheRecipe + id: ShipyardRCDAmmo + result: ShipyardRCDAmmo + category: Tools + completetime: 2.4 + materials: + Steel: 500 + Plastic: 250 diff --git a/Resources/Prototypes/_NF/Recipes/Lathes/tools.yml b/Resources/Prototypes/_NF/Recipes/Lathes/tools.yml new file mode 100644 index 00000000000..48ae087fd7a --- /dev/null +++ b/Resources/Prototypes/_NF/Recipes/Lathes/tools.yml @@ -0,0 +1,20 @@ +- type: entity + name: Shipyard RCD Ammo + parent: BaseItem + id: ShipyardRCDAmmo + description: Ammo cartridge for a Shipyard RCD. + components: + - type: RCDAmmo + isShipyardRCDAmmo: true + - type: Sprite + sprite: Objects/Tools/rcd.rsi + state: ammo-shipyard + - type: Item + sprite: Objects/Tools/rcd.rsi + heldPrefix: ammo-shipyard + - type: PhysicalComposition + materialComposition: + Steel: 1000 + Plastic: 1000 + - type: StaticPrice + price: 100 diff --git a/Resources/Textures/Objects/Tools/rcd.rsi/ammo-shipyard.png b/Resources/Textures/Objects/Tools/rcd.rsi/ammo-shipyard.png new file mode 100644 index 0000000000000000000000000000000000000000..e7ecb3099890d67d7eb22472a10e9bbfaded1f9a GIT binary patch literal 1927 zcmcIlTWs4@7D+N=#~JGEnueVlknd|caU z(!xVEK_?`jmAH*ch>pQ7f{j6u*fdQ8G;VuYL4yfRtJJ-qj;$3EpjAwa<2K1Aw2i)C z$@cL%-}(OUzntGMM%r3el~tEv7`7@D^mm{)Zr>|Q(bF6n`W?OQ(t|xFhB@l&yCnC1 z(*TCu`I{Q;wz|U`1qr4dq6}lekxT0cjbV*VIbD>Jz`|o7u4-Ol{^)T6S7k5JX(vrFPKxG9h7sKL0?Xq0i$K;4SrIz?>+`nI zmzPLbmM&0KHk);1T@Gl(DTe2Higr>?Cy5ZGIjC7;j?~P$0)rozlA-FB3N_qj6k{-B zc?qO+$%V9Dq}9xPm{7o|oTyWbgSK5N0K?&1L(}OZ+Oz@#$Vy)C4T;U@pbn@GV8Vm4Cc~`xn5`(k0r*7=cnOp>mZV)I?nO)o*is19V& z5|;;tvZ@R&2O16wAeH8XrtSBMfB}b7b|}e8}gE7*J7DXu<4gZNWnUAII<XWqRdv+bwhS3kn7 z%y$o2bLXd4==$u%``D+tcI{5?X*u)tSI-ZhXn1d|qVk7kk~}?AdZ22qsc%mIdw%+= z{>r8|*6cm~bLgCVxO57uo+$gA&FtJ$ajyCJl}jH!?#ooQckcM>?605vn4IhlV%0Ux z5BpzwWLLlW$3XA!+{7~*PPJTqaQ>cJtarNa(9vfHhq&m4x7QtkYp#^f_8cBfV_S}u z&%~eJu>ba*123;{9j>jMWbQ9{Zqy$*8D6{N1FCMeCwS_%tw+Ybow;xsi}mge8XJ#} zG<>H2^1z!^wnB@`X;@Sf|oCYmf8qFV4^J~WHTAf0%23Z1H1&dw@;%mGyt(|Qo;Uq zgHVhn3<_X*8j_1KM2}%INTC$tavzxzTMS7s35Mbl6jLHNPRJC5j}J8TgMl@kG7u5! zpdnk}Nd+faEF6KNR;yKPm5NzD5yh2CC5lN1nyoMRnw4mo1&cd)} z$j(UW*c6Kj2A&RvVB$t-&BBmOKrqxsawsmw>>&*RDg6kJOW}== zk6^hZ*1`%&>}#k;mVagdNUPC|*cj6nlWD|+US!4 zYh-y~7dv_wHuG8v62hxe;leTqE=?E43+fnxQ5<0to| z3P2~5(ul;D#1MtT$AIYMloXLrlnzr;I7ZUCVYX170R>4KN3+_!q5vZ(VMBA5}t8K(^uQY4)`bZ2Csf#e}by}c+@u-={x zGzAY$Gh^uSn=B9QwUYn^A!sWd;zeU9Ww(2HnZ zP{9VCH9;iD85uokj}=WdQ$r;_aypQOg?>t^*V+-t)L)k9x24Qr>|;tax;p+JiH79Y zCz0kv8gz0Lwg-dSd*0yTqyL*118bxD<1idFUHk1ww+9a+?+e-tsCn=<2wa=dEfS5t z5~B9k#_W8Omk_)z(c|;2b<11UKe@Htb~^j8=bfFNhK*jfZ0Ouo(kwp}{h2bjbY;D7 zb-mbdc12Z>t~v&)x0GSWPpqkmJRC>L#(OrNYrC+=bwzh#dx5O1Ja^^3{0irW*dJG) zooQ_qUQ|C)K73X1Xmice%~^k7-rc=7Goazaxh=t$`u+W)x;CVyPS41`RAe|4<6wJv zPa=|ivex4<Q zsV!?cZI!r9F88LAl5DGD4(Q*Wr`;(Z13hzZQ>)yFE z>Ld4n6Q>sZGta%`R7p{X)_L|d4QF;}i_U+xv4B!{Slb;EuGfWWb7wxsRp$#oojk>* zuw-FtldpL5{HH%YTifhB|F7HpAjno7e{@!8e~3wK822pL9MtDpBw124HT>Azu1A_8 z=;fh^r(e0Ush0RLuI1K`$WdnM}G8yZ3i@W?(F!!x@303v4mQ$^W`ZoBG`?! zMmfHvOI~@K{q}ls$-<0d>%*fi&YJT{M{isEWN-0`ie|Nuy>PMk>+HNod114cjrWV6 z{La4W^L5V2U**2nssH%xS)UbluQ3$ICPe;VfWs;spT7YDt)CE5arof}1^erPa}O?Z z**zUW;;b#|IAlw5_|$_nFAnwe{%NM@g6*bXSKX{&zc>?4c8BLv>RWZCSGrDnf4b76 zL?KIVS^|l#KAIciG_O<1Rr@4^}B2t;9IY-)i=WQ39Pkg!R zcNJN!m;HmrwcU++n!!Eqit`k0Y3Z4Lu7UU)P05e^*_zUFNPI)L>FR@~ZOuk_M)tp5 zrS7rwZ{MD#E8o#oyLCF0*64Kk{DcR~yIq!jlQTPXUg68*(y534;W~|&v2R|c6XEQ6 zc0t2~UF|J%s!N-zh&LONkG|;Ka3&_Up!c9Waclh1X>jhIn;o4KTW;k}>AsR%y?>?u z^8&a3{88~2tzqIiEBd72fQLH!n`_~B3;NP_hkD*UGV}LNyE4D(|6<$X##hUl9&0pd z