From 201ac78563b3b57d498c6fd615afd7e617a4d016 Mon Sep 17 00:00:00 2001 From: GlodBlock <1356392126@qq.com> Date: Sun, 29 May 2022 16:23:45 +0800 Subject: [PATCH] begin gui for fluid level maintainer --- .../client/GuiFluidLevelMaintainer.java | 4 + .../github/client/GuiIngredientBuffer.java | 4 +- .../ContainerFluidLevelMaintainer.java | 64 ++++++++ .../common/tile/TileFluidLevelMaintainer.java | 152 +++++++++++++++++- .../github/util/DummyInvAdaptor.java | 62 +++++++ .../com/glodblock/github/util/NameConst.java | 2 + .../resources/assets/ae2fc/lang/en_us.lang | 2 + .../textures/gui/fluid_level_maintainer.png | Bin 0 -> 9349 bytes 8 files changed, 280 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/glodblock/github/client/GuiFluidLevelMaintainer.java create mode 100644 src/main/java/com/glodblock/github/client/container/ContainerFluidLevelMaintainer.java create mode 100644 src/main/java/com/glodblock/github/util/DummyInvAdaptor.java create mode 100644 src/main/resources/assets/ae2fc/textures/gui/fluid_level_maintainer.png diff --git a/src/main/java/com/glodblock/github/client/GuiFluidLevelMaintainer.java b/src/main/java/com/glodblock/github/client/GuiFluidLevelMaintainer.java new file mode 100644 index 00000000..847b26e1 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/GuiFluidLevelMaintainer.java @@ -0,0 +1,4 @@ +package com.glodblock.github.client; + +public class GuiFluidLevelMaintainer { +} diff --git a/src/main/java/com/glodblock/github/client/GuiIngredientBuffer.java b/src/main/java/com/glodblock/github/client/GuiIngredientBuffer.java index c4181619..9fd68d60 100644 --- a/src/main/java/com/glodblock/github/client/GuiIngredientBuffer.java +++ b/src/main/java/com/glodblock/github/client/GuiIngredientBuffer.java @@ -21,7 +21,7 @@ import net.minecraft.util.ResourceLocation; import java.io.IOException; -public class GuiIngredientBuffer extends AEBaseGui { +public class GuiIngredientBuffer extends AEBaseGui { private static final ResourceLocation TEX_BG = FluidCraft.resource("textures/gui/ingredient_buffer.png"); private static final int TANK_X = 47, TANK_X_OFF = 22, TANK_Y = 18; @@ -32,7 +32,7 @@ public class GuiIngredientBuffer extends AEBaseGui { public GuiIngredientBuffer(InventoryPlayer ipl, TileIngredientBuffer tile) { super(new ContainerIngredientBuffer(ipl, tile)); - this.cont = (ContainerIngredientBuffer)inventorySlots; + this.cont = (ContainerIngredientBuffer) inventorySlots; this.ySize = 222; for (int i = 0; i < 4; i++) { mouseRegions.addRegion(TANK_X + TANK_X_OFF * i, TANK_Y, TANK_WIDTH, TANK_HEIGHT, diff --git a/src/main/java/com/glodblock/github/client/container/ContainerFluidLevelMaintainer.java b/src/main/java/com/glodblock/github/client/container/ContainerFluidLevelMaintainer.java new file mode 100644 index 00000000..12105383 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/container/ContainerFluidLevelMaintainer.java @@ -0,0 +1,64 @@ +package com.glodblock.github.client.container; + +import appeng.api.storage.data.IAEItemStack; +import appeng.container.AEBaseContainer; +import appeng.container.slot.SlotFake; +import com.glodblock.github.common.item.ItemFluidDrop; +import com.glodblock.github.common.item.ItemFluidPacket; +import com.glodblock.github.common.tile.TileFluidLevelMaintainer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.fluids.capability.IFluidTankProperties; +import net.minecraftforge.items.IItemHandler; + +import java.util.Objects; + +public class ContainerFluidLevelMaintainer extends AEBaseContainer { + + private final TileFluidLevelMaintainer tile; + + public ContainerFluidLevelMaintainer(InventoryPlayer ip, TileFluidLevelMaintainer tile) { + super(ip, tile); + this.tile = tile; + IItemHandler inv = tile.getInventoryHandler(); + for (int i = 0; i < 5; i++) { + addSlotToContainer(new DisplayFluidSlot(inv, i, 17, 19 + i * 20)); + } + bindPlayerInventory(ip, 0, 132); + } + + public TileFluidLevelMaintainer getTile() { + return tile; + } + + private static class DisplayFluidSlot extends SlotFake { + + IItemHandler icv; + + public DisplayFluidSlot(IItemHandler inv, int idx, int x, int y) { + super(inv, idx, x, y); + this.icv = inv; + } + + public void putStack(ItemStack is) { + if (is.isEmpty()) { + super.putStack(is); + return; + } + if (is.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null)) { + IFluidTankProperties[] tanks = Objects.requireNonNull( + is.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null)) + .getTankProperties(); + for (IFluidTankProperties tank : tanks) { + ItemStack drops = ItemFluidDrop.newStack(tank.getContents()); + super.putStack(drops); + return; + } + } + super.putStack(ItemStack.EMPTY); + } + + } + +} diff --git a/src/main/java/com/glodblock/github/common/tile/TileFluidLevelMaintainer.java b/src/main/java/com/glodblock/github/common/tile/TileFluidLevelMaintainer.java index 368c1aa0..360d9da7 100644 --- a/src/main/java/com/glodblock/github/common/tile/TileFluidLevelMaintainer.java +++ b/src/main/java/com/glodblock/github/common/tile/TileFluidLevelMaintainer.java @@ -1,24 +1,40 @@ package com.glodblock.github.common.tile; +import appeng.api.AEApi; import appeng.api.config.Actionable; +import appeng.api.config.PowerMultiplier; import appeng.api.networking.GridFlags; import appeng.api.networking.crafting.ICraftingLink; import appeng.api.networking.crafting.ICraftingRequester; +import appeng.api.networking.energy.IEnergyGrid; import appeng.api.networking.security.IActionSource; import appeng.api.networking.storage.IStackWatcher; import appeng.api.networking.storage.IStackWatcherHost; +import appeng.api.networking.storage.IStorageGrid; +import appeng.api.storage.IMEMonitor; import appeng.api.storage.IStorageChannel; +import appeng.api.storage.channels.IFluidStorageChannel; +import appeng.api.storage.channels.IItemStorageChannel; +import appeng.api.storage.data.IAEFluidStack; import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IAEStack; import appeng.api.storage.data.IItemList; +import appeng.core.AELog; +import appeng.fluids.util.AEFluidStack; import appeng.helpers.MultiCraftingTracker; import appeng.helpers.Reflected; +import appeng.me.GridAccessException; +import appeng.me.helpers.MachineSource; import appeng.tile.grid.AENetworkTile; import appeng.tile.inventory.AppEngInternalAEInventory; import appeng.util.inv.IAEAppEngInventory; import appeng.util.inv.InvOperation; +import com.glodblock.github.common.item.ItemFluidDrop; +import com.glodblock.github.util.DummyInvAdaptor; import com.google.common.collect.ImmutableSet; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.items.IItemHandler; public class TileFluidLevelMaintainer extends AENetworkTile implements IStackWatcherHost, ICraftingRequester, IAEAppEngInventory { @@ -27,37 +43,157 @@ public class TileFluidLevelMaintainer extends AENetworkTile implements IStackWat private final AppEngInternalAEInventory config = new AppEngInternalAEInventory(this, MAX_FLUID); private final MultiCraftingTracker craftingTracker = new MultiCraftingTracker(this, MAX_FLUID); + private final long[] request = new long[MAX_FLUID]; + private final long[] reportValue = new long[MAX_FLUID]; private IStackWatcher watcher; + private final IActionSource source; @Reflected public TileFluidLevelMaintainer() { getProxy().setIdlePowerUsage(2D); getProxy().setFlags(GridFlags.REQUIRE_CHANNEL); + this.source = new MachineSource( this ); + } + + public void setConfig(int id, IAEItemStack item) { + if (id < 0 || id > MAX_FLUID) { + return; + } + this.config.setStackInSlot(id, item.getDefinition()); + } + + public void setRequest(int id, long amount) { + if (id < 0 || id > MAX_FLUID) { + return; + } + this.request[id] = amount; + } + + public IItemHandler getInventoryHandler() { + return this.config; + } + + @Override + public void readFromNBT(NBTTagCompound data) { + super.readFromNBT(data); + for (int i = 0; i < MAX_FLUID; i ++) { + request[i] = data.getLong("req" + i); + reportValue[i] = data.getLong("rep" + i); + } + config.readFromNBT(data, "configX"); + craftingTracker.readFromNBT(data); + } + + @Override + public NBTTagCompound writeToNBT(NBTTagCompound data) { + super.writeToNBT(data); + for (int i = 0; i < MAX_FLUID; i ++) { + data.setLong("req" + i, request[i]); + data.setLong("rep" + i, reportValue[i]); + } + config.writeToNBT(data, "configX"); + craftingTracker.writeToNBT(data); + return data; + } + + private IMEMonitor getFluidMonitor() { + return getProxy().getNode().getGrid().getCache(IStorageGrid.class) + .getInventory(AEApi.instance().storage().getStorageChannel(IFluidStorageChannel.class)); } @Override public ImmutableSet getRequestedJobs() { - return null; + return this.craftingTracker.getRequestedJobs(); } @Override - public IAEItemStack injectCraftedItems(ICraftingLink iCraftingLink, IAEItemStack iaeItemStack, Actionable actionable) { - return null; + public IAEItemStack injectCraftedItems(ICraftingLink link, IAEItemStack items, Actionable mode) { + final IMEMonitor fluidGrid = getFluidMonitor(); + + try { + + if (fluidGrid != null && this.getProxy().isActive()) { + final IEnergyGrid energy = this.getProxy().getEnergy(); + final double power = items.getStackSize() / 1000D; + + if( energy.extractAEPower( power, mode, PowerMultiplier.CONFIG ) > power - 0.01 ) { + ItemStack inputStack = items.getCachedItemStack( items.getStackSize() ); + + FluidStack inputFluid = ItemFluidDrop.getFluidStack(inputStack); + IAEFluidStack remaining; + + if( mode == Actionable.SIMULATE ) + { + remaining = fluidGrid.injectItems(AEFluidStack.fromFluidStack(inputFluid), Actionable.SIMULATE, source ); + items.setCachedItemStack( inputStack ); + } + else + { + remaining = fluidGrid.injectItems(AEFluidStack.fromFluidStack(inputFluid), Actionable.MODULATE, source ); + if( remaining == null || remaining.getStackSize() <= 0 ) + { + ItemStack tmp = ItemFluidDrop.newAeStack(remaining) != null ? ItemFluidDrop.newAeStack(remaining).getDefinition() : null; + items.setCachedItemStack( tmp ); + } + } + + if( ItemFluidDrop.newStack(remaining != null ? remaining.getFluidStack() : null) == inputStack ) + { + return items; + } + + return ItemFluidDrop.newAeStack(remaining); + } + } + + } catch( final GridAccessException e ) { + AELog.debug( e ); + } + + return items; } @Override - public void jobStateChange(ICraftingLink iCraftingLink) { - + public void jobStateChange(ICraftingLink link) { + this.craftingTracker.jobStateChange( link ); } @Override - public void updateWatcher(IStackWatcher iStackWatcher) { + public void updateWatcher(IStackWatcher iWatcher) { + this.watcher = iWatcher; + configureWatchers(); + } + public void configureWatchers() { + if (this.watcher != null) { + watcher.reset(); + for (int i = 0; i < MAX_FLUID; i ++ ) { + if (this.config.getAEStackInSlot(i) != null && !this.config.getAEStackInSlot(i).getDefinition().isEmpty()) { + watcher.add(this.config.getAEStackInSlot(i)); + } + } + } } @Override - public void onStackChange(IItemList iItemList, IAEStack iaeStack, IAEStack iaeStack1, IActionSource iActionSource, IStorageChannel iStorageChannel) { - + public void onStackChange(IItemList iItemList, IAEStack fullStack, IAEStack diffStack, IActionSource src, IStorageChannel chan) { + if (chan == AEApi.instance().storage().getStorageChannel(IFluidStorageChannel.class) && fullStack instanceof IAEFluidStack) { + for (int i = 0; i < MAX_FLUID; i ++) { + try { + IAEItemStack marked = this.config.getAEStackInSlot(i); + IAEFluidStack fluid = ItemFluidDrop.getAeFluidStack(marked); + if (fluid != null && fluid.getFluidStack() != null + && fluid.getFluidStack().isFluidEqual(((IAEFluidStack)diffStack).getFluidStack())) { + this.reportValue[i] = fullStack.getStackSize(); + if (this.reportValue[i] < this.config.getAEStackInSlot(i).getStackSize()) { + this.craftingTracker.handleCrafting(i, request[i], this.config.getAEStackInSlot(i), DummyInvAdaptor.INSTANCE, this.world, this.getProxy().getGrid(), this.getProxy().getCrafting(), this.source ); + } + } + } catch (GridAccessException e) { + // Ignore + } + } + } } @Override diff --git a/src/main/java/com/glodblock/github/util/DummyInvAdaptor.java b/src/main/java/com/glodblock/github/util/DummyInvAdaptor.java new file mode 100644 index 00000000..464a50ce --- /dev/null +++ b/src/main/java/com/glodblock/github/util/DummyInvAdaptor.java @@ -0,0 +1,62 @@ +package com.glodblock.github.util; + +import appeng.api.config.FuzzyMode; +import appeng.util.InventoryAdaptor; +import appeng.util.inv.IInventoryDestination; +import appeng.util.inv.ItemSlot; +import net.minecraft.item.ItemStack; + +import java.util.Iterator; + +/** + * This a dummy InventoryAdaptor, it has infinity capacity and infinity any items. + */ +public class DummyInvAdaptor extends InventoryAdaptor { + + public static final DummyInvAdaptor INSTANCE = new DummyInvAdaptor(); + + @Override + public ItemStack removeItems(int i, ItemStack itemStack, IInventoryDestination iInventoryDestination) { + return itemStack; + } + + @Override + public ItemStack simulateRemove(int i, ItemStack itemStack, IInventoryDestination iInventoryDestination) { + return itemStack; + } + + @Override + public ItemStack removeSimilarItems(int i, ItemStack itemStack, FuzzyMode fuzzyMode, IInventoryDestination iInventoryDestination) { + return itemStack; + } + + @Override + public ItemStack simulateSimilarRemove(int i, ItemStack itemStack, FuzzyMode fuzzyMode, IInventoryDestination iInventoryDestination) { + return itemStack; + } + + @Override + public ItemStack addItems(ItemStack itemStack) { + return null; + } + + @Override + public ItemStack simulateAdd(ItemStack itemStack) { + return null; + } + + @Override + public boolean containsItems() { + return true; + } + + @Override + public boolean hasSlots() { + return false; + } + + @Override + public Iterator iterator() { + return null; + } +} diff --git a/src/main/java/com/glodblock/github/util/NameConst.java b/src/main/java/com/glodblock/github/util/NameConst.java index a945d159..dffc098c 100644 --- a/src/main/java/com/glodblock/github/util/NameConst.java +++ b/src/main/java/com/glodblock/github/util/NameConst.java @@ -11,6 +11,7 @@ public final class NameConst { public static final String BLOCK_INGREDIENT_BUFFER = "ingredient_buffer"; public static final String BLOCK_BURETTE = "burette"; public static final String BLOCK_DUAL_INTERFACE = "dual_interface"; + public static final String BLOCK_FLUID_LEVEL_MAINTAINER = "fluid_level_maintainer"; public static final String ITEM_FLUID_DROP = "fluid_drop"; public static final String ITEM_FLUID_PACKET = "fluid_packet"; @@ -35,6 +36,7 @@ public final class NameConst { public static final String GUI_FLUID_PACKET_DECODER = GUI_KEY + BLOCK_FLUID_PACKET_DECODER; public static final String GUI_INGREDIENT_BUFFER = GUI_KEY + BLOCK_INGREDIENT_BUFFER; public static final String GUI_BURETTE = GUI_KEY + BLOCK_BURETTE; + public static final String GUI_FLUID_LEVEL_MAINTAINER = GUI_KEY + BLOCK_FLUID_LEVEL_MAINTAINER; public static final ResourceLocation MODEL_DENSE_ENCODED_PATTERN = FluidCraft.resource("builtin/dense_encoded_pattern"); public static final ResourceLocation MODEL_FLUID_PACKET = FluidCraft.resource("builtin/fluid_packet"); diff --git a/src/main/resources/assets/ae2fc/lang/en_us.lang b/src/main/resources/assets/ae2fc/lang/en_us.lang index 985e4d28..3dde123e 100644 --- a/src/main/resources/assets/ae2fc/lang/en_us.lang +++ b/src/main/resources/assets/ae2fc/lang/en_us.lang @@ -7,6 +7,7 @@ tile.ae2fc:ingredient_buffer.name=Ingredient Buffer tile.ae2fc:burette.name=Precision Burette tile.ae2fc:dual_interface.name=ME Dual Interface tile.ae2fc:part_dual_interface.name=ME Dual Interface +tile.ae2fc:fluid_level_maintainer.name=ME Fluid Level Maintainer item.ae2fc:fluid_drop.name=Drop of %s item.ae2fc:fluid_packet.name=Fluid Packet @@ -19,6 +20,7 @@ ae2fc.gui.fluid_pattern_encoder=Fluid Pattern Encoder ae2fc.gui.fluid_packet_decoder=ME Fluid Packet Decoder ae2fc.gui.ingredient_buffer=Ingredient Buffer ae2fc.gui.burette=Precision Burette +ae2fc.gui.fluid_level_maintainer=ME Fluid Level Maintainer ae2fc.tooltip.fluid_packet=Use a Fluid Packet Decoder to\nconvert this back into fluid. ae2fc.tooltip.invalid_fluid=Invalid fluid! diff --git a/src/main/resources/assets/ae2fc/textures/gui/fluid_level_maintainer.png b/src/main/resources/assets/ae2fc/textures/gui/fluid_level_maintainer.png new file mode 100644 index 0000000000000000000000000000000000000000..85118334109b554840b8ec9bcc71ce8bedaf4537 GIT binary patch literal 9349 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 }~T?BFvmgzCjI(0fG6m zc!M_@6KgnAU7zg1sO#}QLa)IKJWq+7I?2A!pu5+{S{w!qkIM(w+^04e<(yI|5hYwu z?>8xdZ__4d@j?l-@|f&)Mt=WZtjxV@OV0Ap@2@$U7nILFPC?^n96~YliY6M)0}u%O zgzEm34Jai(+`0N{Y*bo=P0sIpXwb*P7N7dEcw{9;6Suv^+xwXI4?*b^9WQ0O`f1UI>5L_g1{}h70 z54JDSLbyGW3F{AMqo9^Hin@Krx9z}#n!{gG%iZ)JG=nnXJ!*uClV9|>p+4l2LE*6UD$A$L~@ zj|aBBoG{aVahl!?3F;bCjFnH7`yZzFGV12jVZR=13DQdgx*)=CH3V!}O$Hjw0-m;2 zH8|ugZHldPgLo|$5Jil);@8x{2Mg{qmf-U#50=f%mT=FccLj^I^=o7w+wG9@zJTF_ zke25cW@A4UOQ-@tf+p!a%ZmYFQbXJsE3ch7k#%BXa%VYvFn8I%Z%> z)dANETL_R_+B@{oj*JUGu-PrQ5I_0zaxH`d~5b6#b{7` z{W)s9fSr+gqTdOFYMImy(bF59(EB@w$_-Ip%KcV_&(y&x0d%_6TmAv3{4}O$p(9R< zc>BLv7*#9vI}9zg#8Puag*r!L?i$k@K@(7NSkun?FlyGgfy>Cx9BBW8aAV9(-v z2!%9M@3UN4lW`OsrTXp4``)}QqKsTjs2-Xf+0lBzA$>V6qY1WWU*`Ur38K7KtN-p3)Nn?@OgHmjdRPOdyY%PKAa^Af4t@N z%lq^L*K2l9WsDf{sSaD1l!!59QULAlPKxOg&Grfp6EnLfJsC$_s*r(N+#p9wc4j?- zaBpLo*dt(P96m>FmeL@?kEoL~M<>=tV)x+JVpGE=HoO{;g+nR4^W68(1sIoKE77e# zkS_w4NG@)%+Cun81iA9?+OU3Y1XN8W{ad)}`zj);^(nZ{OIN0QuCkKd%mEs_M|Oq$dTlt-sc{k zLE$OfWjijH*qD;91e*_@cITsI>Tg%y)n#Ya#bAoV7D-R<>ee zywJl7)K%xe+G<}>WIsqX#VyW$CtC6O!$Qf##3wt~cSdo~urw>MhJ=@_o)7W$xg7ec z(&$+`1*+M-aU#ibW(b10YB1nw1#mI6&>k;nf1g9yb0mwBUd!Is4&COlql=9A%=L`p zO*w&88pb@2MdEZQ?Xc7P2P`p+=9K01nIFU&q3%A+rm_NyIxHd?9Y8IGnk~KXS5je3 zWqL-gkWuE_^?VA*m(`EcSb&>hnXXNdi=b#5DY&I};MTq(Y2FDHjad}YQP z@=Zq?CvupzrB`v1fx5pb9%a~u%=3^DcbP^zg3hR6j~ zwIzif2a52>C%YEk%Ib+;M%5<^sJuOrZu+(5hpobc*dE5@#h0NmYp&d@M_;~pAxQ-H z>NUOx9UN-B?D!dxE>m}`1NVwePq>3?MY-aX8m6ZwSsVNu3G6rhD!5dMSfY=1n+nbq1=?YqP_Ma#=o+f3=P;iZy# z9raMjM&U{MIl7hA#CpHxS3Xh;Ztzb;JW5rlYq9Tk!R%>|R-ucpV++b48Y7=S5Gu#5 zzhr*QkIqut_v6GrdUKT2aOl==Gr(!8A8o*{xuTiY?_)>LJnrgJU}|W64YJtu(k$*r73}vNyw#Z8lE?JvY2=8(RxZ;Gzjj1>=X}HY zD@|e@yRP#!N$@j|0AFgc+qYMU!?~me`xTy4^}0nkW9Vn`_f@xEk7;hM{xG)73OxfU zzaKNv9celwB#S**W65a&^gU+^kHM^Im_$X#k-_r`!4>#LD2wD zJC8T6?&bSo7!IhqL%j6Nm^^BtYNKb28Ym^3rDr2cK@acEmK7eYNrEor{sg8|DmBTSxUBuB8^p^!4P#g%T_(DysG3tYV;+y@&&` zyS@mAE&Ac%Au>`(Oe|(?Z7n4-a;C~;pr@xN_WP~ffPjF2xtEvMZr=BLM6Mg=`3kSQ zu&}U6){wX~EPa{$L}3I3;OoNdY^h|i+NRw=ZA36&x&vF9_$Mqr0L{;$I(^u!$;qJv z0Sv0AS%=e8Tg8;$C2pS69eUqDTXS-9-rQZhPr7cj$av8Om@@Na-0SKhXD~k1cV0Z! zqXsT;g8;;X#}c(w{diP>$@WolujVyjg2$=CH8U`zi~V<6_b#olAuxVBrHKP_ey>|) z%>Re(_95`xSG-E`k2j4adPqwJ@B|b}b~Q9?R1Ttm3<2BDWMZ%i4sbSWaIZcku=)7V z>NNkylBz8w7$~i*sv>ry<#e!PlW8CZMs^vRHHsr6w<>{2daLm|8oVBdBZ zIeECd(la#tWOBUZcr||1a{I=@NkHFbWhoe(Pq?+Ub%9WN7+(0ZG#A69(?80%N|&1V z(t1-^zwTj!CJ48bVT$MK{=WUs;{3cpd(flAuMR&!4yo4Y-6eZx z;DbTX;rcrL$jHd*&O}MS<0*Qul?kZb=ZsMz@SX%0ms-LsLEqIy{(zFnZj7`;N%NHGiP2GRCHOjekAz%1Y^E06Qcg#Gv zz7c^6E8=AJ>w6VRWNNI4i}*nuUEMSuAn<cC6wlI->8t~f+2AT!eyJ(2!kYGhT`CuVN_51qIRk={_?04tKKM!39K-c#D-L2l8?@as#2b&b*b8XEc0wSQKI3vJ$hocmq>-VfmArAbj zXn}gSzeN)`zY~2IVOY=;6s+D2w`fAlHwN$hZLyQ7E=Cq2_UwO1{qO2=01gle zKtubN>UgU_oGc{UW@WSxcN@DBD0hP3lR0IV?D+73eUSdo>}dcP7zcnz|2tjA&(Q%P zR1S_i_@7ByyY9(AD!czxi#F_p7Q9P77CvpbW#>zUUE%=xyP4;|jQsEFAb;&f2v&JI zD=M;9pRwKh*v9LaMi&y!{>x0Wbeeb5jE~FuRFc-#EQpB{KLa`ff`Uev(JP>3e^^H_ jep