From e565bbf78cf5a3912ad110cfcafbfab5cec7d988 Mon Sep 17 00:00:00 2001 From: Andrew nuark G Date: Wed, 10 Aug 2022 21:55:00 +0700 Subject: [PATCH] Implemented enchanting pricing --- .../entity/custom/EnchanterBlockEntity.java | 25 +++-- .../network/message/PacketEnchantItem.java | 30 ++++-- .../enchantrium/screen/EnchanterMenu.java | 60 ++++++++--- .../enchantrium/screen/EnchanterScreen.java | 99 +++++++++++++----- .../enchantrium/util/EnchantmentUtil.java | 17 +++ .../textures/gui/enchanter_gui.png | Bin 991 -> 1304 bytes 6 files changed, 171 insertions(+), 60 deletions(-) diff --git a/src/main/java/xyz/nuark/enchantrium/block/entity/custom/EnchanterBlockEntity.java b/src/main/java/xyz/nuark/enchantrium/block/entity/custom/EnchanterBlockEntity.java index 4c5a829..f598c45 100644 --- a/src/main/java/xyz/nuark/enchantrium/block/entity/custom/EnchanterBlockEntity.java +++ b/src/main/java/xyz/nuark/enchantrium/block/entity/custom/EnchanterBlockEntity.java @@ -11,9 +11,7 @@ import net.minecraft.world.SimpleContainer; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.ContainerLevelAccess; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; import net.minecraft.world.item.enchantment.EnchantmentInstance; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; @@ -26,11 +24,12 @@ import org.jetbrains.annotations.Nullable; import xyz.nuark.enchantrium.Enchantrium; import xyz.nuark.enchantrium.block.entity.ModBlockEntities; import xyz.nuark.enchantrium.screen.EnchanterMenu; +import xyz.nuark.enchantrium.util.EnchantmentUtil; import java.util.List; public class EnchanterBlockEntity extends BlockEntity implements MenuProvider { - private final ItemStackHandler itemHandler = new ItemStackHandler(2) { + private final ItemStackHandler itemHandler = new ItemStackHandler(3) { @Override protected void onContentsChanged(int slot) { setChanged(); @@ -71,7 +70,7 @@ public class EnchanterBlockEntity extends BlockEntity implements MenuProvider { } @Override - public void invalidateCaps() { + public void invalidateCaps() { super.invalidateCaps(); lazyItemHandler.invalidate(); } @@ -105,18 +104,26 @@ public class EnchanterBlockEntity extends BlockEntity implements MenuProvider { return itemHandler.getStackInSlot(0); } - public void enchant(List enchantments) { - if (itemHandler.extractItem(1, 1, true).is(Items.AIR)) { - return; - } + public ItemStack getEmeralds() { + return itemHandler.getStackInSlot(1); + } + public ItemStack getNetherite() { + return itemHandler.getStackInSlot(2); + } + + public boolean enchant(List enchantments, EnchantmentUtil.EnchantmentCost enchantmentCost, boolean setUnbreakable) { ItemStack input = itemHandler.extractItem(0, 1, false); - itemHandler.extractItem(1, 1, false); + itemHandler.extractItem(1, enchantmentCost.lapis(), false); + itemHandler.extractItem(2, enchantmentCost.netherite(), false); for (EnchantmentInstance enchantment : enchantments) { input.enchant(enchantment.enchantment, enchantment.level); } + input.getOrCreateTag().putBoolean("Unbreakable", setUnbreakable); itemHandler.setStackInSlot(0, input); itemHandler.insertItem(0, input, false); + + return true; } } diff --git a/src/main/java/xyz/nuark/enchantrium/network/message/PacketEnchantItem.java b/src/main/java/xyz/nuark/enchantrium/network/message/PacketEnchantItem.java index f9b141c..efbbf7c 100644 --- a/src/main/java/xyz/nuark/enchantrium/network/message/PacketEnchantItem.java +++ b/src/main/java/xyz/nuark/enchantrium/network/message/PacketEnchantItem.java @@ -1,36 +1,36 @@ package xyz.nuark.enchantrium.network.message; import net.minecraft.core.BlockPos; -import net.minecraft.core.Registry; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.item.enchantment.EnchantmentInstance; -import net.minecraft.world.level.Level; import net.minecraftforge.network.NetworkEvent; import net.minecraftforge.registries.ForgeRegistries; -import xyz.nuark.enchantrium.Enchantrium; import xyz.nuark.enchantrium.block.entity.custom.EnchanterBlockEntity; -import xyz.nuark.enchantrium.network.Networking; +import xyz.nuark.enchantrium.util.EnchantmentUtil; import java.util.List; -import java.util.Objects; import java.util.function.Supplier; public class PacketEnchantItem { private final BlockPos blockEntityPos; private final List enchantments; + private final EnchantmentUtil.EnchantmentCost enchantmentCost; + private final boolean unbreakingSet; - public PacketEnchantItem(BlockPos blockEntityPos, List enchantments) { + public PacketEnchantItem(BlockPos blockEntityPos, List enchantments, EnchantmentUtil.EnchantmentCost enchantmentCost, boolean unbreakingSet) { this.blockEntityPos = blockEntityPos; this.enchantments = enchantments; + this.enchantmentCost = enchantmentCost; + this.unbreakingSet = unbreakingSet; } public boolean handle(Supplier supplier) { NetworkEvent.Context ctx = supplier.get(); ctx.enqueueWork(() -> { EnchanterBlockEntity ebe = (EnchanterBlockEntity)ctx.getSender().level.getBlockEntity(blockEntityPos); - ebe.enchant(enchantments); + if (ebe.enchant(enchantments, enchantmentCost, unbreakingSet)) { + ctx.getSender().giveExperienceLevels(-enchantmentCost.levels()); + } }); return true; } @@ -41,6 +41,10 @@ public class PacketEnchantItem { buf.writeResourceLocation(ForgeRegistries.ENCHANTMENTS.getKey(enchantment.enchantment)); buf.writeVarInt(enchantment.level); }); + friendlyByteBuf.writeVarInt(enchantmentCost.levels()); + friendlyByteBuf.writeVarInt(enchantmentCost.lapis()); + friendlyByteBuf.writeVarInt(enchantmentCost.netherite()); + friendlyByteBuf.writeBoolean(unbreakingSet); } public static PacketEnchantItem decode(FriendlyByteBuf friendlyByteBuf) { @@ -49,7 +53,13 @@ public class PacketEnchantItem { friendlyByteBuf.readList((buf) -> new EnchantmentInstance( ForgeRegistries.ENCHANTMENTS.getValue(buf.readResourceLocation()), buf.readVarInt() - )) + )), + new EnchantmentUtil.EnchantmentCost( + friendlyByteBuf.readVarInt(), + friendlyByteBuf.readVarInt(), + friendlyByteBuf.readVarInt() + ), + friendlyByteBuf.readBoolean() ); } } diff --git a/src/main/java/xyz/nuark/enchantrium/screen/EnchanterMenu.java b/src/main/java/xyz/nuark/enchantrium/screen/EnchanterMenu.java index d26f85f..5f28a86 100644 --- a/src/main/java/xyz/nuark/enchantrium/screen/EnchanterMenu.java +++ b/src/main/java/xyz/nuark/enchantrium/screen/EnchanterMenu.java @@ -17,7 +17,6 @@ import net.minecraftforge.common.Tags; import net.minecraftforge.items.CapabilityItemHandler; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import xyz.nuark.enchantrium.Enchantrium; import xyz.nuark.enchantrium.block.ModBlocks; import xyz.nuark.enchantrium.block.entity.custom.EnchanterBlockEntity; import xyz.nuark.enchantrium.network.Networking; @@ -33,6 +32,7 @@ public class EnchanterMenu extends AbstractContainerMenu { private ItemStack inputStack = new ItemStack(Items.AIR); private List enchantments = Lists.newArrayList(); + private boolean unbreakingSet = false; private int currentEnchantmentIndex = -1; private final Object lock = new Object(); @@ -42,7 +42,7 @@ public class EnchanterMenu extends AbstractContainerMenu { public EnchanterMenu(int containerId, Inventory inv, BlockEntity entity) { super(ModMenuTypes.ENCHANTER_MENU.get(), containerId); - checkContainerSize(inv, 2); + checkContainerSize(inv, 3); blockEntity = ((EnchanterBlockEntity) entity); this.level = inv.player.level; @@ -55,8 +55,12 @@ public class EnchanterMenu extends AbstractContainerMenu { stack -> EnchantmentUtil.canBeEnchanted(stack) && !stack.isEnchanted() && !stack.is(Items.BOOK) )); this.addSlot(new FilteredSlot( - handler, 1, 8, 43, - stack -> stack.is(Tags.Items.GEMS_EMERALD) || stack.is(Tags.Items.INGOTS_NETHERITE) + handler, 1, 31, 19, + stack -> stack.is(Tags.Items.STORAGE_BLOCKS_LAPIS) + )); + this.addSlot(new FilteredSlot( + handler, 2, 54, 19, + stack -> stack.is(Tags.Items.INGOTS_NETHERITE) )); }); } @@ -79,33 +83,55 @@ public class EnchanterMenu extends AbstractContainerMenu { } } + public boolean unbreakable() { + return unbreakingSet; + } + + public EnchantmentUtil.EnchantmentCost getEnchantmentRequirements() { + return EnchantmentUtil.calculateEnchantmentPrice(this.enchantments.stream().filter(e -> e.level > 0).toList(), unbreakingSet); + } + + public boolean requirementsMet(Player player, EnchantmentUtil.EnchantmentCost enchantmentRequirements) { + return player.experienceLevel >= enchantmentRequirements.levels() + && blockEntity.getEmeralds().getCount() >= enchantmentRequirements.lapis() + && blockEntity.getNetherite().getCount() >= enchantmentRequirements.netherite(); + } + @Override public boolean clickMenuButton(Player player, int buttonId) { - if (buttonId < 0 || buttonId >= 5) { + if (buttonId < 0 || buttonId >= 6) { Util.logAndPauseIfInIde(player.getName() + " pressed invalid button id: " + buttonId); return false; } - Enchantrium.LOGGER.debug("{} clicked button {}", player.getName(), buttonId); synchronized (lock) { if (buttonId == 0) { - Networking.sendToServer(new PacketEnchantItem(blockEntity.getBlockPos(), this.enchantments.stream().filter(e -> e.level > 0).toList())); + var enchantments = this.enchantments.stream().filter(e -> e.level > 0).toList(); + if (enchantments.isEmpty()) return true; + Networking.sendToServer(new PacketEnchantItem( + blockEntity.getBlockPos(), + enchantments, + getEnchantmentRequirements(), + unbreakingSet + )); } else if (buttonId == 1) { - currentEnchantmentIndex = (currentEnchantmentIndex + 1) % enchantments.size(); - } else if (buttonId == 2) { currentEnchantmentIndex = (currentEnchantmentIndex - 1 + enchantments.size()) % enchantments.size(); + } else if (buttonId == 2) { + currentEnchantmentIndex = (currentEnchantmentIndex + 1) % enchantments.size(); } else if (buttonId == 3) { + var enchantment = this.enchantments.get(currentEnchantmentIndex); + this.enchantments.set(currentEnchantmentIndex, new EnchantmentInstance( + enchantment.enchantment, + enchantment.level > 0 ? (enchantment.level - 1) : 0 + )); + } else if (buttonId == 4) { var enchantment = this.enchantments.get(currentEnchantmentIndex); this.enchantments.set(currentEnchantmentIndex, new EnchantmentInstance( enchantment.enchantment, enchantment.level < enchantment.enchantment.getMaxLevel() ? (enchantment.level + 1) : enchantment.level )); } else { - var enchantment = this.enchantments.get(currentEnchantmentIndex); - this.enchantments.set(currentEnchantmentIndex, new EnchantmentInstance( - enchantment.enchantment, - enchantment.level > 0 ? (enchantment.level - 1) : 0 - )); + unbreakingSet = !unbreakingSet; } } return true; @@ -119,14 +145,14 @@ public class EnchanterMenu extends AbstractContainerMenu { private void addPlayerInventory(Inventory playerInventory) { for (int i = 0; i < 3; i++) { for (int j = 0; j < 9; j++) { - addSlot(new Slot(playerInventory, j + i * 9 + 9, 8 + j * 18, 86 + i * 18)); + addSlot(new Slot(playerInventory, j + i * 9 + 9, 8 + j * 18, 150 + i * 18)); } } } private void addPlayerHotbar(Inventory playerInventory) { for (int i = 0; i < 9; i++) { - addSlot(new Slot(playerInventory, i, 8 + i * 18, 144)); + addSlot(new Slot(playerInventory, i, 8 + i * 18, 208)); } } @@ -146,7 +172,7 @@ public class EnchanterMenu extends AbstractContainerMenu { private static final int TE_INVENTORY_FIRST_SLOT_INDEX = VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT; // THIS YOU HAVE TO DEFINE! - private static final int TE_INVENTORY_SLOT_COUNT = 2; // must be the number of slots you have! + private static final int TE_INVENTORY_SLOT_COUNT = 3; // must be the number of slots you have! @Override public @NotNull ItemStack quickMoveStack(@NotNull Player playerIn, int index) { diff --git a/src/main/java/xyz/nuark/enchantrium/screen/EnchanterScreen.java b/src/main/java/xyz/nuark/enchantrium/screen/EnchanterScreen.java index 83b8fb4..9d64076 100644 --- a/src/main/java/xyz/nuark/enchantrium/screen/EnchanterScreen.java +++ b/src/main/java/xyz/nuark/enchantrium/screen/EnchanterScreen.java @@ -2,21 +2,19 @@ package xyz.nuark.enchantrium.screen; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.TextComponent; import net.minecraft.network.chat.TranslatableComponent; -import net.minecraft.network.protocol.game.ServerboundContainerButtonClickPacket; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Inventory; -import net.minecraftforge.network.NetworkHooks; import org.jetbrains.annotations.NotNull; import xyz.nuark.enchantrium.Enchantrium; -import xyz.nuark.enchantrium.network.Networking; -import xyz.nuark.enchantrium.network.message.PacketEnchantItem; import xyz.nuark.enchantrium.screen.widget.Label; public class EnchanterScreen extends AbstractContainerScreen { @@ -29,58 +27,77 @@ public class EnchanterScreen extends AbstractContainerScreen { private Button prevBtn; private Button increaseLevelBtn; private Button decreaseLevelBtn; + private Button switchUnbreakableBtn; public EnchanterScreen(EnchanterMenu enchanterMenu, Inventory inventory, Component title) { super(enchanterMenu, inventory, title); + + imageWidth = 176; + imageHeight = 232; } @Override protected void init() { super.init(); + int sx = width / 2 - imageWidth / 2; + int sy = height / 2 - imageHeight / 2; + currentEnchantmentLabel = new Label( new TextComponent(""), - width / 2, height / 2 + 80, 0xFFFFFF, true + sx + 7, sy + 51, 0xFFFFFF, false ); enchantBtn = new Button( - width / 2 - 50, height / 2 + 100, 100, 20, + sx + 76, sy + 18, 93, 20, new TextComponent("Enchant"), button -> clickButton(0) ); - nextBtn = new Button( - width / 2 + 60, height / 2 + 100, 20, 20, - new TextComponent("\u2192"), + + prevBtn = new Button( + sx + 7, sy + 71, 78, 20, + new TextComponent("Previous"), button -> clickButton(1) ); - prevBtn = new Button( - width / 2 - 80, height / 2 + 100, 20, 20, - new TextComponent("\u2190"), + nextBtn = new Button( + sx + 91, sy + 71, 78, 20, + new TextComponent("Next"), button -> clickButton(2) ); - increaseLevelBtn = new Button( - width / 2 + 90, height / 2 + 100, 20, 10, - new TextComponent("\u2191"), + decreaseLevelBtn = new Button( + sx + 7, sy + 97, 78, 20, + new TextComponent("Decrease"), button -> clickButton(3) ); - decreaseLevelBtn = new Button( - width / 2 + 90, height / 2 + 110, 20, 10, - new TextComponent("\u2193"), + increaseLevelBtn = new Button( + sx + 91, sy + 97, 78, 20, + new TextComponent("Increase"), button -> clickButton(4) ); + switchUnbreakableBtn = new Button( + sx + 7, sy + 123, 162, 20, + new TextComponent(""), + button -> clickButton(5) + ); + addRenderableWidget(enchantBtn); - addRenderableWidget(nextBtn); addRenderableWidget(prevBtn); - addRenderableWidget(increaseLevelBtn); + addRenderableWidget(nextBtn); addRenderableWidget(decreaseLevelBtn); + addRenderableWidget(increaseLevelBtn); + addRenderableWidget(switchUnbreakableBtn); + addRenderableOnly(new Label( + new TextComponent("Selected Enchantment:"), + sx + 7, sy + 41, 0xFFFFFF, false + )); addRenderableOnly(currentEnchantmentLabel); } private void clickButton(int buttonId) { assert Minecraft.getInstance().player != null; - menu.clickMenuButton(Minecraft.getInstance().player, buttonId); + menu.clickMenuButton(Minecraft.getInstance().player, buttonId); } @Override @@ -94,6 +111,11 @@ public class EnchanterScreen extends AbstractContainerScreen { this.blit(pPoseStack, x, y, 0, 0, imageWidth, imageHeight); } + @Override + protected void renderLabels(@NotNull PoseStack pPoseStack, int pMouseX, int pMouseY) { + this.font.draw(pPoseStack, this.title, (float) this.titleLabelX, (float) this.titleLabelY, 4210752); + } + @Override public void render(@NotNull PoseStack pPoseStack, int mouseX, int mouseY, float delta) { renderBackground(pPoseStack); @@ -104,21 +126,50 @@ public class EnchanterScreen extends AbstractContainerScreen { if (enchantmentInstance != null) { var enchantment = enchantmentInstance.enchantment; var level = enchantmentInstance.level; - Component description; + MutableComponent description; if (level == 0) { description = new TranslatableComponent(enchantment.getDescriptionId()); } else { - description = enchantment.getFullname(level); + description = new TranslatableComponent(enchantment.getDescriptionId()); + description.withStyle(ChatFormatting.GREEN); + if (level != 1 || enchantment.getMaxLevel() != 1) { + description.append(" ").append(new TranslatableComponent("enchantment.level." + level)); + } } currentEnchantmentLabel.setText(description); } else { currentEnchantmentLabel.setText(new TextComponent("No enchantment selected")); } - enchantBtn.active = enchantmentInstance != null; + var enchantmentRequirements = menu.getEnchantmentRequirements(); + + if (Minecraft.getInstance().player != null) { + enchantBtn.active = enchantmentInstance != null && menu.requirementsMet(Minecraft.getInstance().player, enchantmentRequirements); + } else { + enchantBtn.active = false; + } nextBtn.active = enchantmentInstance != null; prevBtn.active = enchantmentInstance != null; increaseLevelBtn.active = enchantmentInstance != null; decreaseLevelBtn.active = enchantmentInstance != null; + switchUnbreakableBtn.active = enchantmentInstance != null; + + switchUnbreakableBtn.setMessage(new TextComponent(menu.unbreakable() ? "Make unbreakable" : "Make breakable")); + + if (enchantmentInstance != null && enchantBtn.isHoveredOrFocused()) { + renderTooltip( + pPoseStack, + new TextComponent("Requirements: ") + .append(String.valueOf(enchantmentRequirements.lapis())) + .append(new TextComponent(" lapis").withStyle(ChatFormatting.BLUE)) + .append(", ") + .append(String.valueOf(enchantmentRequirements.netherite())) + .append(new TextComponent(" netherite").withStyle(ChatFormatting.DARK_PURPLE)) + .append(", ") + .append(String.valueOf(enchantmentRequirements.levels())) + .append(new TextComponent(" levels").withStyle(ChatFormatting.YELLOW)), + mouseX, mouseY + ); + } } } diff --git a/src/main/java/xyz/nuark/enchantrium/util/EnchantmentUtil.java b/src/main/java/xyz/nuark/enchantrium/util/EnchantmentUtil.java index 6a10a0f..55cda09 100644 --- a/src/main/java/xyz/nuark/enchantrium/util/EnchantmentUtil.java +++ b/src/main/java/xyz/nuark/enchantrium/util/EnchantmentUtil.java @@ -24,4 +24,21 @@ public class EnchantmentUtil { return list; } + + public static EnchantmentCost calculateEnchantmentPrice(List enchantments, boolean hasUnbreaking) { + if (enchantments.isEmpty()) { + return new EnchantmentCost(0, 0, 0); + } + int lapis = Math.min(20 + enchantments.size() * (hasUnbreaking ? 6 : 3), 64); + int netherite = Math.min(Math.max((hasUnbreaking ? 5 : -3) + enchantments.size() / 2, 0), 64); + int levels = enchantments.stream().reduce( + 0, + (acc, enchantment) -> acc + enchantment.level, + Integer::sum + ); + return new EnchantmentCost(levels, lapis, netherite); + } + + public record EnchantmentCost(int levels, int lapis, int netherite) { + } } diff --git a/src/main/resources/assets/enchantrium/textures/gui/enchanter_gui.png b/src/main/resources/assets/enchantrium/textures/gui/enchanter_gui.png index e490f5f4866e39e7dfebe451edb66d979ad86f85..f28dbdd772d7141aa4dbd9c9c2fb1a610ac4d56c 100644 GIT binary patch literal 1304 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K58911MRQ8&P5D>38$lZxy-8q?;Kn_c~qpu?a z!^VE@KZ&eBerbSDh%1o(|NsB7W5+^6L(R<0%+1X$EG&9@db+#2Gcq#r^YgQ^vdYWL z3kwU2ii+am;!;vla&mG?N=kBbbCZ&i($dn>)6-*OVj?3W6B83NGc)7k<4a3RV`F2Z zqobpuq8b_+A|fI>J3A8+5>iuBdwY9a}kI)t~>FF*9`MH)oC@lg!1B3q}0gzxVbnF0`l=kZ3LW_fh^kQ+k8_!Y>~l#5`bG zaFv+@XyJu@b3tLnfWkFB@c8lN(DRc&=l{94<$Dd2PyVX)-Dy@`UmsQNdTSr+D#tjl zWYV@@9$!mDTsPl5{qyP5FRpvpEM)Eb(m(%veRYf1q|5Vd*&=juFBVzOKAL~}x^#nz zXV;Az;(-k`KJQqu zYL&s!b@B}IUoXCvVyOO86j=A}+PA;?_Dtt1XNAq){%Y~8wR~#5-~KY}SjBqcvvXwK zE&l`G9?4C(@&4GVF8-aKz9AyDYz8L}N!7f2^K<*|TJ8yAM|F(2lfzb*-1n(|FUOEN zbIP$gmQAJGUNgMezPR?W__GbC-h7-r|JyxVhGm{JpQIN3W4N){?6PpM%gUSc{qO(V z^8H<*@D|n7ynM!QYz3ydVrO6G0_cbOO{iW+7eU+yiNx`lsp*kHP0IR?ys@IG_hYM>hvSr`}&vsHXxERdOd zjaQAI;rPUBe2UMX+cVzicQm*R~R1-ez7Raqa_#38$lZxy-8q?;Kn_c~qpu?a z!^VE@KZ&eBzH5L_h%1o(|NsB7W5+^6L(R<0%+1X$EG&9@db+#2J3BjjdwVl7GB$16 zG;`+6zP`RSYu41(*2c%jS5#CiUc9)ds3Tgs9bf8|XxN){Jx zTD0O_*Rpq(v&+sp_3V@S&$+Ae-L1^N^|N<7TdQ)E=-&PRP&IA=!=5ddK1%3qV0iJ0 zmj!53#{EQ4c%X0(aDDjo>s$DH`%~KQ*7!91n_pI`C&YNKuz~5r zzJ`DQZr*yj(}U%~{Doh)`FlUr_HVfS{AbNR5k_wy<13#*%swB+ci-a~x*Ajud@b4; zxBhVM1~!TIlcEo%TQ~gWj9@tSk?+AD=K`7Ym(PkWu;<;LIV5v7;kX^9dTjbVJ0BOaN(*ney)a~!~6+rzs}nFf6pY| z1>0G+^HoQKLkgsO%li!=3IeKX9ky-TJ~f5GwtS_k-R+BOY_|Y)Ox`xNV#a(NT0YHt z;<8qS`Nd9e-H$i>(%vqdn!_-6%i1G{|J(s3R4@PrCn)(rz=ZEA>RGCi3wlA^x7KUy z8LGZTdDrY)9a){BD($d8cbicCLfh?NuYr`nq7}qucw&3DH_DSOLz#i$1Diz}!R-3(*MxW(46EO(GmX?zCf_cPv0c=;^q=Jx9# OKY6