From b6455e5d7ce3d652541ce5ecf10c59554dd7e866 Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Tue, 10 Dec 2024 18:28:40 +0100 Subject: [PATCH] Input Scheming - Tweaks to the factory gauge screen --- .../2d64935085b86659cb7857bad9701dbf9bab6e4c | 6 +- .../resources/assets/create/lang/en_ud.json | 11 +- .../resources/assets/create/lang/en_us.json | 11 +- .../factoryBoard/FactoryPanelBehaviour.java | 9 +- .../FactoryPanelConfigurationPacket.java | 26 ++- .../factoryBoard/FactoryPanelMenu.java | 50 +++++ .../factoryBoard/FactoryPanelRenderer.java | 2 +- .../factoryBoard/FactoryPanelScreen.java | 172 +++++++----------- .../create/foundation/gui/AllGuiTextures.java | 10 +- .../assets/create/lang/default/interface.json | 11 +- .../block/factory_panel_arrows_animated.png | Bin 573 -> 906 bytes .../block/factory_panel_connections.png | Bin 233 -> 489 bytes .../factory_panel_connections_animated.png | Bin 572 -> 906 bytes .../create/textures/gui/factory_gauge.png | Bin 0 -> 2461 bytes 14 files changed, 160 insertions(+), 148 deletions(-) create mode 100644 src/main/java/com/simibubi/create/content/logistics/factoryBoard/FactoryPanelMenu.java create mode 100644 src/main/resources/assets/create/textures/gui/factory_gauge.png diff --git a/src/generated/resources/.cache/2d64935085b86659cb7857bad9701dbf9bab6e4c b/src/generated/resources/.cache/2d64935085b86659cb7857bad9701dbf9bab6e4c index b48befb2a3..2df4c3ff9b 100644 --- a/src/generated/resources/.cache/2d64935085b86659cb7857bad9701dbf9bab6e4c +++ b/src/generated/resources/.cache/2d64935085b86659cb7857bad9701dbf9bab6e4c @@ -1,4 +1,4 @@ -// 1.20.1 2024-12-05T16:08:22.0352298 Registrate Provider for create [Recipes, Advancements, Loot Tables, Tags (blocks), Tags (items), Tags (fluids), Tags (entity_types), Blockstates, Item models, Lang (en_us/en_ud)] +// 1.20.1 2024-12-10T17:47:26.9305006 Registrate Provider for create [Recipes, Advancements, Loot Tables, Tags (blocks), Tags (items), Tags (fluids), Tags (entity_types), Blockstates, Item models, Lang (en_us/en_ud)] 60bbdf92d2ac9824ea6144955c74043a6005f79d assets/create/blockstates/acacia_window.json 6a67703c2697d81b7dc83e9d72a66f9c9ff08383 assets/create/blockstates/acacia_window_pane.json c3ae87b62e81d8e9476eccd793bb1548d74c66a1 assets/create/blockstates/adjustable_chain_gearshift.json @@ -640,8 +640,8 @@ b0d8f08968763a5f74e5cd5644377a76a9f39753 assets/create/blockstates/yellow_toolbo fe8c497aacc641c2f01cec90bba9f19e59cc2ed2 assets/create/blockstates/yellow_valve_handle.json e819e93fdcbe9fd9c050a052d2718ff3b3539365 assets/create/blockstates/zinc_block.json 64121dcb216381c83b4fe28aa361ea07c24c9ad0 assets/create/blockstates/zinc_ore.json -a32412bd9acc0409b78d878e7ea4ecd3a49a97d2 assets/create/lang/en_ud.json -8bd05b90f21f54644f72787a7cc44b094c1c40dc assets/create/lang/en_us.json +b0846becbdff1db28265621f9cdf7e1e88bcd0d1 assets/create/lang/en_ud.json +966950f91561bc1883eda230690ee8ca4fe7021e assets/create/lang/en_us.json a97e1060e00ae701a02e39cd4ef8054cf345fac4 assets/create/models/block/acacia_window.json 103e032c0b1a0a6a27c67da8c91179a564bd281c assets/create/models/block/acacia_window_pane_noside.json fb00b627abda76ad4fea867ca57dbfadd24fffa3 assets/create/models/block/acacia_window_pane_noside_alt.json diff --git a/src/generated/resources/assets/create/lang/en_ud.json b/src/generated/resources/assets/create/lang/en_ud.json index ab767b2ffa..f394fd2798 100644 --- a/src/generated/resources/assets/create/lang/en_ud.json +++ b/src/generated/resources/assets/create/lang/en_ud.json @@ -1189,8 +1189,8 @@ "create.gui.config.overlay8": "uoıʇısod ʇןnɐɟǝp ǝɥʇ oʇ ʇǝsǝɹ oʇ", "create.gui.contraptions.network_overstressed": "˙‾ʇɔɐdɯı‾ ‾ssǝɹʇs‾ ɥbıɥ ɐ ɥʇıʍ sʇuǝuodɯoɔ ǝɥʇ ‾uʍop‾ ‾ʍoןs‾ ɹo sǝɔɹnos ǝɹoɯ ppⱯ ˙‾pǝssǝɹʇsɹǝʌo‾ sı uoıʇdɐɹʇuoɔ sıɥʇ ʇɐɥʇ sɹɐǝddɐ ʇI", "create.gui.contraptions.not_fast_enough": "˙‾pǝǝds‾ ‾ɥbnouǝ‾ ɥʇıʍ buıʇɐʇoɹ ‾ʇou‾ sı %1$s sıɥʇ ʇɐɥʇ sɹɐǝddɐ ʇI", - "create.gui.factory_panel.activate_crafting": "buıʇɟɐɹƆ pǝbuɐɹɹɐ-ǝɹd ǝןbbo⟘", - "create.gui.factory_panel.connect_input": "ןǝuɐd ʇnduı uɐ ʇɔǝuuoƆ", + "create.gui.factory_panel.activate_crafting": "buıʇɟɐɹƆ ןɐɔıuɐɥɔǝW ǝs∩", + "create.gui.factory_panel.connect_input": "uoıʇɔǝuuoɔ ʍǝu ppⱯ", "create.gui.factory_panel.crafting_input": "sʇuǝıpǝɹbuI pǝbɐʞɔɐԀ", "create.gui.factory_panel.crafting_input_tip": "pǝʇɔǝuuoɔ oʇuı ǝbɐʞɔɐdu∩", "create.gui.factory_panel.crafting_input_tip_1": ")ƐxƐ( sɹǝʇɟɐɹɔ ןɐɔıuɐɥɔǝɯ", @@ -1212,22 +1212,19 @@ "create.gui.factory_panel.recipe_address_tip_1": "˙ʇno pǝıɹɹɐɔ sı ǝdıɔǝɹ sıɥʇ", "create.gui.factory_panel.recipe_promises_tip": "ǝsıɯoɹd ɐ 'ʇuǝs ǝɹɐ sʇnduı uǝɥM", "create.gui.factory_panel.recipe_promises_tip_1": "˙ǝʌıɹɹɐ sʇndʇno ןıʇun pןǝɥ sı", + "create.gui.factory_panel.reset": "sbuıʇʇǝs ןןɐ ʇǝsǝᴚ", "create.gui.factory_panel.restocker_address": "˙˙˙oʇ sɯǝʇı puǝS", "create.gui.factory_panel.restocker_address_given": "oʇ buıpuǝS", "create.gui.factory_panel.restocker_address_tip": "ןןıʍ ʇɐɥʇ ssǝɹppɐ uɐ ɹǝʇuƎ", "create.gui.factory_panel.restocker_address_tip_1": "˙ǝɹǝɥ ǝʌıɹɹɐ oʇ sǝbɐʞɔɐd ǝsnɐɔ", "create.gui.factory_panel.restocker_promises_tip": "ǝsıɯoɹd ɐ 'ʇuǝs ǝɹɐ sɯǝʇı uǝɥM", "create.gui.factory_panel.restocker_promises_tip_1": "˙ǝʌıɹɹɐ ʎǝɥʇ ןıʇun pןǝɥ sı", + "create.gui.factory_panel.save_and_close": "ǝsoןɔ puɐ ǝʌɐS", "create.gui.factory_panel.scroll_to_change_amount": "ʇunoɯɐ ǝbuɐɥɔ oʇ ןןoɹɔS", "create.gui.factory_panel.send_item": "%1$s puǝS", "create.gui.factory_panel.sending_item": "%1$s buıpuǝS", "create.gui.factory_panel.sending_item_tip": "ןǝʌǝן ʞɔoʇs ןɐɔoן ɹǝʌǝuǝɥʍ", "create.gui.factory_panel.sending_item_tip_1": "ʇunoɯɐ ʇǝbɹɐʇ ǝɥʇ ʍoןǝq sı", - "create.gui.factory_panel.storage_level": "ןǝʌǝן ǝbɐɹoʇS", - "create.gui.factory_panel.storage_level_and_target": "ʇǝbɹɐʇ puɐ ןǝʌǝן ǝbɐɹoʇS", - "create.gui.factory_panel.storage_level_tip": "ʇǝs ǝq uɐɔ ʇunoɯɐ pǝʇǝbɹɐ⟘", - "create.gui.factory_panel.storage_level_tip_1": "ʇoןs ɹǝʇןıɟ ǝɥʇ buısn ʎq", - "create.gui.factory_panel.storage_level_tip_2": "ǝɔɐɟɹǝʇuı sıɥʇ ɟo ǝpısʇno", "create.gui.factory_panel.title_as_recipe": "sbuıʇʇǝS ǝdıɔǝᴚ", "create.gui.factory_panel.title_as_restocker": "sbuıʇʇǝS ɹǝʞɔoʇsǝᴚ", "create.gui.filter.allow_list": "ʇsıꞀ-ʍoןןⱯ", diff --git a/src/generated/resources/assets/create/lang/en_us.json b/src/generated/resources/assets/create/lang/en_us.json index 447d6dcbc2..7241b62ab7 100644 --- a/src/generated/resources/assets/create/lang/en_us.json +++ b/src/generated/resources/assets/create/lang/en_us.json @@ -1189,8 +1189,8 @@ "create.gui.config.overlay8": "to reset to the default position", "create.gui.contraptions.network_overstressed": "It appears that this contraption is _overstressed_. Add more sources or _slow_ _down_ the components with a high _stress_ _impact_.", "create.gui.contraptions.not_fast_enough": "It appears that this %1$s is _not_ rotating with _enough_ _speed_.", - "create.gui.factory_panel.activate_crafting": "Toggle pre-arranged Crafting", - "create.gui.factory_panel.connect_input": "Connect an input panel", + "create.gui.factory_panel.activate_crafting": "Use Mechanical Crafting", + "create.gui.factory_panel.connect_input": "Add new connection", "create.gui.factory_panel.crafting_input": "Packaged Ingredients", "create.gui.factory_panel.crafting_input_tip": "Unpackage into connected", "create.gui.factory_panel.crafting_input_tip_1": "mechanical crafters (3x3)", @@ -1212,22 +1212,19 @@ "create.gui.factory_panel.recipe_address_tip_1": "this recipe is carried out.", "create.gui.factory_panel.recipe_promises_tip": "When inputs are sent, a promise", "create.gui.factory_panel.recipe_promises_tip_1": "is held until outputs arrive.", + "create.gui.factory_panel.reset": "Reset all settings", "create.gui.factory_panel.restocker_address": "Send items to...", "create.gui.factory_panel.restocker_address_given": "Sending to", "create.gui.factory_panel.restocker_address_tip": "Enter an address that will", "create.gui.factory_panel.restocker_address_tip_1": "cause packages to arrive here.", "create.gui.factory_panel.restocker_promises_tip": "When items are sent, a promise", "create.gui.factory_panel.restocker_promises_tip_1": "is held until they arrive.", + "create.gui.factory_panel.save_and_close": "Save and close", "create.gui.factory_panel.scroll_to_change_amount": "Scroll to change amount", "create.gui.factory_panel.send_item": "Send %1$s", "create.gui.factory_panel.sending_item": "Sending %1$s", "create.gui.factory_panel.sending_item_tip": "whenever local stock level", "create.gui.factory_panel.sending_item_tip_1": "is below the target amount", - "create.gui.factory_panel.storage_level": "Storage level", - "create.gui.factory_panel.storage_level_and_target": "Storage level and target", - "create.gui.factory_panel.storage_level_tip": "Targeted amount can be set", - "create.gui.factory_panel.storage_level_tip_1": "by using the filter slot", - "create.gui.factory_panel.storage_level_tip_2": "outside of this interface", "create.gui.factory_panel.title_as_recipe": "Recipe Settings", "create.gui.factory_panel.title_as_restocker": "Restocker Settings", "create.gui.filter.allow_list": "Allow-List", diff --git a/src/main/java/com/simibubi/create/content/logistics/factoryBoard/FactoryPanelBehaviour.java b/src/main/java/com/simibubi/create/content/logistics/factoryBoard/FactoryPanelBehaviour.java index 6a2c7be28c..42c80652b9 100644 --- a/src/main/java/com/simibubi/create/content/logistics/factoryBoard/FactoryPanelBehaviour.java +++ b/src/main/java/com/simibubi/create/content/logistics/factoryBoard/FactoryPanelBehaviour.java @@ -432,6 +432,11 @@ public class FactoryPanelBehaviour extends FilteringBehaviour { @Override public void destroy() { + disconnectAll(); + super.destroy(); + } + + public void disconnectAll() { FactoryPanelPosition panelPosition = getPanelPosition(); for (FactoryPanelPosition position : targetedBy.keySet()) { FactoryPanelBehaviour source = at(getWorld(), position); @@ -447,8 +452,8 @@ public class FactoryPanelBehaviour extends FilteringBehaviour { target.blockEntity.sendData(); } } - - super.destroy(); + targetedBy.clear(); + targeting.clear(); } public int getUnloadedLinks() { diff --git a/src/main/java/com/simibubi/create/content/logistics/factoryBoard/FactoryPanelConfigurationPacket.java b/src/main/java/com/simibubi/create/content/logistics/factoryBoard/FactoryPanelConfigurationPacket.java index 28ce4c8b4b..99d994e5cd 100644 --- a/src/main/java/com/simibubi/create/content/logistics/factoryBoard/FactoryPanelConfigurationPacket.java +++ b/src/main/java/com/simibubi/create/content/logistics/factoryBoard/FactoryPanelConfigurationPacket.java @@ -24,10 +24,12 @@ public class FactoryPanelConfigurationPacket extends BlockEntityConfigurationPac private int promiseClearingInterval; private FactoryPanelPosition removeConnection; private boolean clearPromises; + private boolean reset; public FactoryPanelConfigurationPacket(FactoryPanelPosition position, String address, Map inputAmounts, List craftingArrangement, int outputAmount, - int promiseClearingInterval, @Nullable FactoryPanelPosition removeConnection, boolean clearPromises) { + int promiseClearingInterval, @Nullable FactoryPanelPosition removeConnection, boolean clearPromises, + boolean reset) { super(position.pos()); this.address = address; this.inputAmounts = inputAmounts; @@ -36,6 +38,7 @@ public class FactoryPanelConfigurationPacket extends BlockEntityConfigurationPac this.promiseClearingInterval = promiseClearingInterval; this.removeConnection = removeConnection; this.clearPromises = clearPromises; + this.reset = reset; this.slot = position.slot(); } @@ -61,6 +64,7 @@ public class FactoryPanelConfigurationPacket extends BlockEntityConfigurationPac if (removeConnection != null) removeConnection.send(buffer); buffer.writeBoolean(clearPromises); + buffer.writeBoolean(reset); } @Override @@ -80,6 +84,7 @@ public class FactoryPanelConfigurationPacket extends BlockEntityConfigurationPac if (buffer.readBoolean()) removeConnection = FactoryPanelPosition.receive(buffer); clearPromises = buffer.readBoolean(); + reset = buffer.readBoolean(); } @Override @@ -88,8 +93,21 @@ public class FactoryPanelConfigurationPacket extends BlockEntityConfigurationPac if (behaviour == null) return; - behaviour.recipeAddress = address; + behaviour.recipeAddress = reset ? "" : address; + behaviour.recipeOutput = reset ? 1 : outputAmount; + behaviour.promiseClearingInterval = reset ? -1 : promiseClearingInterval; + behaviour.activeCraftingArrangement = reset ? List.of() : craftingArrangement; + if (reset) { + behaviour.forceClearPromises = true; + behaviour.disconnectAll(); + behaviour.setFilter(ItemStack.EMPTY); + behaviour.count = 0; + be.redraw = true; + be.notifyUpdate(); + return; + } + for (Entry entry : inputAmounts.entrySet()) { FactoryPanelPosition key = entry.getKey(); FactoryPanelConnection connection = behaviour.targetedBy.get(key); @@ -97,10 +115,6 @@ public class FactoryPanelConfigurationPacket extends BlockEntityConfigurationPac connection.amount = entry.getValue(); } - behaviour.recipeOutput = outputAmount; - behaviour.promiseClearingInterval = promiseClearingInterval; - behaviour.activeCraftingArrangement = craftingArrangement; - if (removeConnection != null) { behaviour.targetedBy.remove(removeConnection); FactoryPanelBehaviour source = FactoryPanelBehaviour.at(be.getLevel(), removeConnection); diff --git a/src/main/java/com/simibubi/create/content/logistics/factoryBoard/FactoryPanelMenu.java b/src/main/java/com/simibubi/create/content/logistics/factoryBoard/FactoryPanelMenu.java new file mode 100644 index 0000000000..77154cd436 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/factoryBoard/FactoryPanelMenu.java @@ -0,0 +1,50 @@ +package com.simibubi.create.content.logistics.factoryBoard; + +import com.simibubi.create.foundation.gui.menu.GhostItemMenu; + +import net.minecraft.client.Minecraft; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.MenuType; +import net.minecraftforge.items.ItemStackHandler; +import net.minecraftforge.items.SlotItemHandler; + +public class FactoryPanelMenu extends GhostItemMenu { + + public FactoryPanelMenu(MenuType type, int id, Inventory inv, FriendlyByteBuf extraData) { + super(type, id, inv, extraData); + } + + public FactoryPanelMenu(MenuType type, int id, Inventory inv, FactoryPanelBehaviour contentHolder) { + super(type, id, inv, contentHolder); + } + + @Override + protected ItemStackHandler createGhostInventory() { + ItemStackHandler itemStackHandler = new ItemStackHandler(1); + if (contentHolder != null) + itemStackHandler.setStackInSlot(0, contentHolder.getFilter() + .copyWithCount(1)); + return itemStackHandler; + } + + @Override + protected boolean allowRepeats() { + return true; + } + + @Override + protected FactoryPanelBehaviour createOnClient(FriendlyByteBuf extraData) { + return FactoryPanelBehaviour.at(Minecraft.getInstance().level, FactoryPanelPosition.receive(extraData)); + } + + @Override + protected void addSlots() { + addSlot(new SlotItemHandler(ghostInventory, 0, 16, 24)); + addPlayerSlots(0, 0); + } + + @Override + protected void saveData(FactoryPanelBehaviour contentHolder) {} + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/factoryBoard/FactoryPanelRenderer.java b/src/main/java/com/simibubi/create/content/logistics/factoryBoard/FactoryPanelRenderer.java index 2d8db4e482..ba7d3cf6b2 100644 --- a/src/main/java/com/simibubi/create/content/logistics/factoryBoard/FactoryPanelRenderer.java +++ b/src/main/java/com/simibubi/create/content/logistics/factoryBoard/FactoryPanelRenderer.java @@ -90,7 +90,7 @@ public class FactoryPanelRenderer extends SmartBlockEntityRenderer inputConfig; @@ -129,51 +133,59 @@ public class FactoryPanelScreen extends AbstractSimiScreen { @Override protected void init() { - int sizeX = 200; - int sizeY = 75 + middleHeight(); + int sizeX = FACTORY_GAUGE_BOTTOM.getWidth(); + int sizeY = + (restocker ? FACTORY_GAUGE_RESTOCK : FACTORY_GAUGE_RECIPE).getHeight() + FACTORY_GAUGE_BOTTOM.getHeight(); + setWindowSize(sizeX, sizeY); super.init(); clearWidgets(); + int x = guiLeft; int y = guiTop; if (addressBox == null) { - addressBox = new AddressEditBox(this, new NoShadowFontWrapper(font), x + 38, y + 30 + middleHeight(), 110, - 10, false); + addressBox = new AddressEditBox(this, new NoShadowFontWrapper(font), 0, 0, 108, 10, false); addressBox.setValue(behaviour.recipeAddress); addressBox.setTextColor(0x555555); } - addressBox.setX(x + 38); - addressBox.setY(y + 30 + middleHeight()); + addressBox.setX(x + 36); + addressBox.setY(y + windowHeight - 51); addRenderableWidget(addressBox); - confirmButton = new IconButton(x + sizeX - 51, y + sizeY - 22, AllIcons.I_CONFIRM); + confirmButton = new IconButton(x + sizeX - 33, y + sizeY - 25, AllIcons.I_CONFIRM); confirmButton.withCallback(() -> minecraft.setScreen(null)); + confirmButton.setToolTip(CreateLang.translate("gui.factory_panel.save_and_close") + .component()); addRenderableWidget(confirmButton); - promiseExpiration = new ScrollInput(x + 112, y + 54 + middleHeight(), 25, 16).withRange(-1, 31) + deleteButton = new IconButton(x + sizeX - 55, y + sizeY - 25, AllIcons.I_TRASH); + deleteButton.withCallback(() -> { + sendReset = true; + minecraft.setScreen(null); + }); + deleteButton.setToolTip(CreateLang.translate("gui.factory_panel.reset") + .component()); + addRenderableWidget(deleteButton); + + promiseExpiration = new ScrollInput(x + 97, y + windowHeight - 24, 28, 16).withRange(-1, 31) .titled(CreateLang.translate("gui.factory_panel.promises_expire_title") .component()); promiseExpiration.setState(behaviour.promiseClearingInterval); addRenderableWidget(promiseExpiration); - if (!craftingActive && !restocker && behaviour.targetedBy.size() < 9) { - int slot = behaviour.targetedBy.size(); - newInputButton = new IconButton(x + 24 + (slot % 3 * 18), y + 27 + (slot / 3 * 18), AllIcons.I_ADD); - newInputButton.withCallback(() -> { - FactoryPanelConnectionHandler.startConnection(behaviour); - minecraft.setScreen(null); - }); - newInputButton.setToolTip(CreateLang.translate("gui.factory_panel.connect_input") - .component()); - addRenderableWidget(newInputButton); - } + newInputButton = new IconButton(x + (restocker ? 54 : 34), y + (restocker ? 25 : 65), AllIcons.I_ADD); + newInputButton.withCallback(() -> { + FactoryPanelConnectionHandler.startConnection(behaviour); + minecraft.setScreen(null); + }); + newInputButton.setToolTip(CreateLang.translate("gui.factory_panel.connect_input") + .component()); + addRenderableWidget(newInputButton); activateCraftingButton = null; if (availableCraftingRecipe != null) { - int outputX = x + 130; - int outputY = y + 15 + middleHeight() / 2; - activateCraftingButton = new IconButton(outputX + 17, outputY, AllIcons.I_3x3); + activateCraftingButton = new IconButton(x + 34, y + 44, AllIcons.I_3x3); activateCraftingButton.withCallback(() -> { craftingActive = !craftingActive; init(); @@ -186,22 +198,12 @@ public class FactoryPanelScreen extends AbstractSimiScreen { .component()); addRenderableWidget(activateCraftingButton); } - - displayedExtraRows = rowsToDisplay(); - } - - private int rowsToDisplay() { - return craftingActive ? 2 : Math.min((behaviour.targetedBy.size() / 3), 2); - } - - private int middleHeight() { - return AllGuiTextures.FACTORY_PANEL_MIDDLE.getHeight() + rowsToDisplay() * 18; } @Override public void tick() { super.tick(); - if (inputConfig.size() != behaviour.targetedBy.size() || displayedExtraRows != rowsToDisplay()) { + if (inputConfig.size() != behaviour.targetedBy.size()) { updateConfigs(); init(); } @@ -220,32 +222,15 @@ public class FactoryPanelScreen extends AbstractSimiScreen { int y = guiTop; // BG - AllGuiTextures.FACTORY_PANEL_TOP.render(graphics, x, y); - y += AllGuiTextures.FACTORY_PANEL_TOP.getHeight(); - for (int i = 0; i < displayedExtraRows + 1; i++) - AllGuiTextures.FACTORY_PANEL_MIDDLE.render(graphics, x, y + i * 18); - y += middleHeight(); - AllGuiTextures.FACTORY_PANEL_BOTTOM.render(graphics, x, y); - y = guiTop; + AllGuiTextures bg = restocker ? FACTORY_GAUGE_RESTOCK : FACTORY_GAUGE_RECIPE; + bg.render(graphics, x, y); + FACTORY_GAUGE_BOTTOM.render(graphics, x, y + bg.getHeight()); // RECIPE int slot = 0; - int slotsToRender = craftingActive ? 9 : behaviour.targetedBy.size(); - for (int frame : Iterate.zeroAndOne) { - AllGuiTextures sprite = - frame == 0 ? AllGuiTextures.FACTORY_PANEL_SLOT_FRAME : AllGuiTextures.FACTORY_PANEL_SLOT; - for (slot = 0; slot < slotsToRender; slot++) - sprite.render(graphics, x + 23 + frame + (slot % 3 * 18), y + 26 + frame + (slot / 3 * 18)); - if (slot < 9) - sprite.render(graphics, x + 23 + frame + (slot % 3 * 18), y + 26 + frame + (slot / 3 * 18)); - } - - slot = 0; - if (craftingActive) { for (BigItemStack itemStack : craftingIngredients) renderInputItem(graphics, slot++, itemStack, mouseX, mouseY); - } else for (BigItemStack itemStack : inputConfig) renderInputItem(graphics, slot++, itemStack, mouseX, mouseY); @@ -253,14 +238,9 @@ public class FactoryPanelScreen extends AbstractSimiScreen { if (restocker) renderInputItem(graphics, slot, new BigItemStack(behaviour.getFilter(), 1), mouseX, mouseY); - if (inputConfig.size() > 0) { - int arrowOffset = Mth.clamp(slotsToRender, 0, 2); - AllGuiTextures.FACTORY_PANEL_ARROW.render(graphics, x + 75 + arrowOffset * 9, y + 16 + middleHeight() / 2); - int outputX = x + 130; - int outputY = y + 16 + middleHeight() / 2; - if (availableCraftingRecipe != null) - AllGuiTextures.FACTORY_PANEL_SLOT_FRAME.render(graphics, outputX + 16, outputY - 2); - AllGuiTextures.FACTORY_PANEL_SLOT_FRAME.render(graphics, outputX - 2, outputY - 2); + if (!restocker) { + int outputX = x + 141; + int outputY = y + 66; graphics.renderItem(outputConfig.stack, outputX, outputY); graphics.renderItemDecorations(font, behaviour.getFilter(), outputX, outputY, outputConfig.count + ""); @@ -299,41 +279,35 @@ public class FactoryPanelScreen extends AbstractSimiScreen { Component title = CreateLang .translate(restocker ? "gui.factory_panel.title_as_restocker" : "gui.factory_panel.title_as_recipe") .component(); - graphics.drawString(font, title, x + 87 - font.width(title) / 2, y + 7, 0x3D3C48, false); + graphics.drawString(font, title, x + 97 - font.width(title) / 2, y + 4, 0x3D3C48, false); // ITEM PREVIEW + int previewY = restocker ? 10 : 50; + ms.pushPose(); - ms.translate(0, middleHeight() - 25, 0); + ms.translate(0, previewY, 0); GuiGameElement.of(AllBlocks.FACTORY_GAUGE.asStack()) .scale(4) .at(0, 0, -200) - .render(graphics, x + 175, y + 55); + .render(graphics, x + 195, y + 55); if (!behaviour.getFilter() .isEmpty()) { GuiGameElement.of(behaviour.getFilter()) .scale(1.625) .at(0, 0, 100) - .render(graphics, x + 194, y + 68); + .render(graphics, x + 214, y + 68); } - ms.translate(0, 0, 350); - MutableComponent countLabelForValueBox = behaviour.getCountLabelForValueBox(); - graphics.drawString(font, countLabelForValueBox, x + 210 - font.width(countLabelForValueBox) / 2, y + 98, - 0xffffffff); ms.popPose(); - if (mouseX >= x + 190 - 1 && mouseX < x + 190 - 1 + 48 && mouseY >= y + middleHeight() - 25 + 90 - 1 - && mouseY < y + middleHeight() - 25 + 94 - 1 + 26) - showStockLevelTooltip(graphics, mouseX, mouseY); - // PROMISES int state = promiseExpiration.getState(); graphics.drawString(font, CreateLang.text(state == -1 ? " /" : state == 0 ? "30s" : state + "m") .component(), promiseExpiration.getX() + 3, promiseExpiration.getY() + 4, 0xffeeeeee, true); ItemStack asStack = AllItems.CARDBOARD_PACKAGE_12x12.asStack(); - int itemY = y + 54 + middleHeight(); - int itemX = x + 88; + int itemX = x + 68; + int itemY = y + windowHeight - 24; graphics.renderItem(asStack, itemX, itemY); int promised = behaviour.getPromised(); graphics.renderItemDecorations(font, asStack, itemX, itemY, promised + ""); @@ -381,14 +355,14 @@ public class FactoryPanelScreen extends AbstractSimiScreen { // private void renderInputItem(GuiGraphics graphics, int slot, BigItemStack itemStack, int mouseX, int mouseY) { - int inputX = guiLeft + 25 + (slot % 3 * 18); - int inputY = guiTop + 28 + (slot / 3 * 18); + int inputX = guiLeft + (restocker ? 88 : 68 + (slot % 3 * 20)); + int inputY = guiTop + 26 + (slot / 3 * 20); graphics.renderItem(itemStack.stack, inputX, inputY); if (!craftingActive && !restocker && !itemStack.stack.isEmpty()) graphics.renderItemDecorations(font, itemStack.stack, inputX, inputY, itemStack.count + ""); - if (mouseX < inputX - 1 || mouseX >= inputX - 1 + 18 || mouseY < inputY - 1 || mouseY >= inputY - 1 + 18) + if (mouseX < inputX - 2 || mouseX >= inputX - 2 + 20 || mouseY < inputY - 2 || mouseY >= inputY - 2 + 20) return; if (craftingActive) { @@ -450,24 +424,6 @@ public class FactoryPanelScreen extends AbstractSimiScreen { mouseX, mouseY); } - private void showStockLevelTooltip(GuiGraphics graphics, int mouseX, int mouseY) { - graphics.renderComponentTooltip(font, - List.of( - (behaviour.count > 0 ? CreateLang.translate("gui.factory_panel.storage_level_and_target") - : CreateLang.translate("gui.factory_panel.storage_level")).color(ScrollInput.HEADER_RGB) - .component(), - CreateLang.translate("gui.factory_panel.storage_level_tip") - .style(ChatFormatting.GRAY) - .component(), - CreateLang.translate("gui.factory_panel.storage_level_tip_1") - .style(ChatFormatting.GRAY) - .component(), - CreateLang.translate("gui.factory_panel.storage_level_tip_2") - .style(ChatFormatting.GRAY) - .component()), - mouseX, mouseY); - } - private void showAddressBoxTooltip(GuiGraphics graphics, int mouseX, int mouseY) { if (addressBox.getValue() .isBlank()) { @@ -531,8 +487,8 @@ public class FactoryPanelScreen extends AbstractSimiScreen { // Remove connections if (!craftingActive) for (int i = 0; i < connections.size(); i++) { - int inputX = x + 25 + (i % 3 * 18); - int inputY = y + 28 + (i / 3 * 18); + int inputX = x + 68 + (i % 3 * 20); + int inputY = y + 26 + (i / 3 * 20); if (mouseX >= inputX && mouseX < inputX + 16 && mouseY >= inputY && mouseY < inputY + 16) { sendIt(connections.get(i).from, false); return true; @@ -540,8 +496,8 @@ public class FactoryPanelScreen extends AbstractSimiScreen { } // Clear promises - int itemY = y + 54 + middleHeight(); - int itemX = x + 88; + int itemX = x + 103; + int itemY = y + windowHeight - 24; if (mouseX >= itemX && mouseX < itemX + 16 && mouseY >= itemY && mouseY < itemY + 16) { sendIt(null, true); return true; @@ -559,8 +515,8 @@ public class FactoryPanelScreen extends AbstractSimiScreen { return super.mouseScrolled(mouseX, mouseY, pDelta); for (int i = 0; i < inputConfig.size(); i++) { - int inputX = x + 25 + (i % 3 * 18); - int inputY = y + 28 + (i / 3 * 18); + int inputX = x + 68 + (i % 3 * 20); + int inputY = y + 26 + (i / 3 * 20); if (mouseX >= inputX && mouseX < inputX + 16 && mouseY >= inputY && mouseY < inputY + 16) { BigItemStack itemStack = inputConfig.get(i); if (itemStack.stack.isEmpty()) @@ -571,9 +527,9 @@ public class FactoryPanelScreen extends AbstractSimiScreen { } } - if (inputConfig.size() > 0) { - int outputX = x + 130; - int outputY = y + 16 + middleHeight() / 2; + if (!restocker) { + int outputX = x + 141; + int outputY = y + 66; if (mouseX >= outputX && mouseX < outputX + 16 && mouseY >= outputY && mouseY < outputY + 16) { BigItemStack itemStack = outputConfig; itemStack.count = @@ -612,7 +568,7 @@ public class FactoryPanelScreen extends AbstractSimiScreen { String address = addressBox.getValue(); FactoryPanelConfigurationPacket packet = new FactoryPanelConfigurationPacket(pos, address, inputs, - craftingArrangement, outputConfig.count, promiseExp, toRemove, clearPromises); + craftingArrangement, outputConfig.count, promiseExp, toRemove, clearPromises, sendReset); AllPackets.getChannel() .sendToServer(packet); } diff --git a/src/main/java/com/simibubi/create/foundation/gui/AllGuiTextures.java b/src/main/java/com/simibubi/create/foundation/gui/AllGuiTextures.java index f256039b0e..8abd8ecc88 100644 --- a/src/main/java/com/simibubi/create/foundation/gui/AllGuiTextures.java +++ b/src/main/java/com/simibubi/create/foundation/gui/AllGuiTextures.java @@ -153,13 +153,9 @@ public enum AllGuiTextures implements ScreenElement, TextureSheetSegment { VALUE_SETTINGS_LABEL_BG("value_settings", 0, 31, 81, 11), // HILO - FACTORY_PANEL_TOP("restocker_and_requester", 0, 0, 182, 23), - FACTORY_PANEL_MIDDLE("restocker_and_requester", 0, 23, 182, 26), - FACTORY_PANEL_BOTTOM("restocker_and_requester", 0, 49, 182, 54), - FACTORY_PANEL_SLOT_FRAME("restocker_and_requester", 3, 106, 20, 20), - FACTORY_PANEL_SLOT("restocker_and_requester", 4, 107, 18, 18), - FACTORY_PANEL_ARROW("restocker_and_requester", 27, 108, 22, 16), - FACTORY_PANEL_ARROW_FILLED("restocker_and_requester", 50, 108, 22, 16), + FACTORY_GAUGE_RECIPE("factory_gauge", 32, 4, 192, 95), + FACTORY_GAUGE_RESTOCK("factory_gauge", 32, 109, 192, 55), + FACTORY_GAUGE_BOTTOM("factory_gauge", 32, 174, 200, 57), STOCK_KEEPER_REQUEST_HEADER("stock_keeper", 0, 0, 256, 36), STOCK_KEEPER_REQUEST_BODY("stock_keeper", 0, 48, 256, 20), diff --git a/src/main/resources/assets/create/lang/default/interface.json b/src/main/resources/assets/create/lang/default/interface.json index 5ff689a054..32f92e59eb 100644 --- a/src/main/resources/assets/create/lang/default/interface.json +++ b/src/main/resources/assets/create/lang/default/interface.json @@ -1035,7 +1035,9 @@ "create.gui.factory_panel.title_as_recipe": "Recipe Settings", "create.gui.factory_panel.title_as_restocker": "Restocker Settings", - "create.gui.factory_panel.connect_input": "Connect an input panel", + "create.gui.factory_panel.connect_input": "Add new connection", + "create.gui.factory_panel.reset": "Reset all settings", + "create.gui.factory_panel.save_and_close": "Save and close", "create.gui.factory_panel.no_open_promises": "No open promises", "create.gui.factory_panel.promised_items": "Promised items", "create.gui.factory_panel.left_click_reset": "Left-Click to reset", @@ -1054,11 +1056,6 @@ "create.gui.factory_panel.send_item": "Send %1$s", "create.gui.factory_panel.left_click_disconnect": "Left-Click to disconnect", "create.gui.factory_panel.scroll_to_change_amount": "Scroll to change amount", - "create.gui.factory_panel.storage_level": "Storage level", - "create.gui.factory_panel.storage_level_and_target": "Storage level and target", - "create.gui.factory_panel.storage_level_tip": "Targeted amount can be set", - "create.gui.factory_panel.storage_level_tip_1": "by using the filter slot", - "create.gui.factory_panel.storage_level_tip_2": "outside of this interface", "create.gui.factory_panel.recipe_address": "Send inputs to...", "create.gui.factory_panel.recipe_address_given": "Sending inputs to", "create.gui.factory_panel.recipe_address_tip": "Enter an address where", @@ -1067,7 +1064,7 @@ "create.gui.factory_panel.restocker_address_given": "Sending to", "create.gui.factory_panel.restocker_address_tip": "Enter an address that will", "create.gui.factory_panel.restocker_address_tip_1": "cause packages to arrive here.", - "create.gui.factory_panel.activate_crafting": "Toggle pre-arranged Crafting", + "create.gui.factory_panel.activate_crafting": "Use Mechanical Crafting", "create.gui.factory_panel.crafting_input": "Packaged Ingredients", "create.gui.factory_panel.crafting_input_tip": "Unpackage into connected", "create.gui.factory_panel.crafting_input_tip_1": "mechanical crafters (3x3)", diff --git a/src/main/resources/assets/create/textures/block/factory_panel_arrows_animated.png b/src/main/resources/assets/create/textures/block/factory_panel_arrows_animated.png index b1e4a37c202da45ff128c0eee266ea361076934f..ca91d61b83914866bb5b7c02a0ba962ef0f04747 100644 GIT binary patch literal 906 zcmeAS@N?(olHy`uVBq!ia0vp^0zllr!3-o#y(K{$#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6-A0X`wF4<9}}eE9H*6DOLQnl4?sv})C=ii(P!o}L8@7QBA_dgsoa&!0bE zyLRo}yLY#5-wxDtZRf=MKuVw_$S?RmDqwh^94iTw<1FxqEM{QfI|Ravq8eTeKtZt* z*NBqf{Irtt#G+J&fW*wa5 zP@RF1u>s=(h?yY!SQkLdnFM5m029zuCa}sNOA8{&803<6b6|wRhCu=S+sGoYe zIEHw5ubtXBsab)?MLB?Vz3spM`T6FGQ{t}PdTltl%lfR+d6y-pLQE|Rrb;DUeQ+Q& z>kx1AW24{*1p__PB~vCgNxN`_=ry~~GkyK;V$BjUotFy_88A6<-3j7gEnXwo<>TFR zS}2^u^5_n`B`HTVRQ{Fp3l%N8WVz@9($FIE@>#u)*Z4(hc+a@pktPwxI zuvE9RT+RL&%S*VAHNO{k^1J_0g#XI)t2ftvbf}G-8*Bgg;`f9Pn?!k>zrOQ)g0)rL)mh1 zuVv=4f4^91@V}Ei`-!sLw>-mgAfdSDpmTQ4lZB?=`1seCikemau-j1mdit}*s~2^4 z+px_~mb<3E_Pe3JMf`!qU+w-ke100U|H02I>dVg^u1PDE3y#{q<=*v;XOk<|*W1OL zRaVShX~6UJVLJ1(2xgl34Hd%F6$ JtaD0e0sybjT8#hz delta 518 zcmV+h0{Q)l2fYN4L4O-iOjJex|Nq6s#oOE4b#-;1prB@EX8HN~v$L}R003SmUXTC) z010$bPE-H?|NsC0|Nj6}Pk0gl000SaNLh0L01m?d01m?e$8V@)0004&NklS?_Aw%phbZMDheIz0}@q=`!?1&VQxQ)+AW#X4a-ahvFWA za9WyzHih)+qNkgom4a+E`qs=!U4}tuwl2$IN<+&6r}apI9tkLT3Zz=G&f^sNX<_oJ z3%*MBN8xKcf`z~O%tMu+7Wycs%vvCjM}2|E4#ar`)A~0LUnTn?TmpKR$Eo~?V3Uw6 z@hBYg9p*eLApkwL6miWX%11aYO+kAuB-?r5N#7nAjyn9j zY%iXd0D=S*lt8w9#v_EeKUgt7UcsK6<2OCoARUy#Xd2rf$JhalNMB~9@RKkEe{}(-RAyZHSrCCR2hPhpxmt4Mt9C6@r zeSbv6fk#T5a)gj3!P!59m}ZVQ#AR9RL6T07*qo IM6N<$f(VWE-~a#s diff --git a/src/main/resources/assets/create/textures/block/factory_panel_connections.png b/src/main/resources/assets/create/textures/block/factory_panel_connections.png index 8d7417bcbf5b4bfc8783d8e0a4ff47580c3a5631..ccd8c1c6b41ddb2af1aba486a5e3c2678f430605 100644 GIT binary patch literal 489 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G|s0G|-o6DLme^z<|}HC?)NY1OJ#6%`c=7Ayct3cQzT0#Y0$L4LviA%Njt z^WI3H7-xY;WHAE+-+mBgv|tTZ0SbzhxJHx&=ckpFCl;kL1SDqWmFW4ohA5co8Gdj6 zJ{hQDOKOB?ny0500|$`9${@wa%D@O@c>%FBlnwHO1|u_AoC(M_WMpFC1JY4IoY~F- z7S8~(L10-KBf|@zS79`Yl?*_M3G56kKy?O2#s-WFAZCK>V_g6-XA+PN0!%X6s4hbT1CVT0=MI(BjltPKW{RhaV~9p@Xdffr0R;|b?ztcS%dc1M4o*^zdm_^N zqSJe~!>T~GB@CqnG3Sjgd_8$tA={ckcvA#0T4~z9%6^ZFf0?>WSgu{X_4N_RZckS~mvv4FO#nt#fIt8M delta 176 zcmaFK{E~5kV?BR>Pl)U7+qX+gOBXCykd~Ilz`($<@M1iWVl4^s3;quT48OPW1p@^* z3p^r=85sBufG}g$wN6f;V34PaV~9p@YhNRmgCYm>^DqDR|2!+2_+bIFC%^1Mfu~tt z>{yC9HZXkXkkdKec5QayqF>_8a#OYz_?vE=sGw=|$ZBC0*LUp$8Vip039PKYr#@@7 ZUBpkOUipO!YJkQwc)I$ztaD0e0szAoMb-cS diff --git a/src/main/resources/assets/create/textures/block/factory_panel_connections_animated.png b/src/main/resources/assets/create/textures/block/factory_panel_connections_animated.png index 8c0d2ce47b5537b7802f9fbace387735cec8d827..ca91d61b83914866bb5b7c02a0ba962ef0f04747 100644 GIT binary patch literal 906 zcmeAS@N?(olHy`uVBq!ia0vp^0zllr!3-o#y(K{$#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6-A0X`wF4<9}}eE9H*6DOLQnl4?sv})C=ii(P!o}L8@7QBA_dgsoa&!0bE zyLRo}yLY#5-wxDtZRf=MKuVw_$S?RmDqwh^94iTw<1FxqEM{QfI|Ravq8eTeKtZt* z*NBqf{Irtt#G+J&fW*wa5 zP@RF1u>s=(h?yY!SQkLdnFM5m029zuCa}sNOA8{&803<6b6|wRhCu=S+sGoYe zIEHw5ubtXBsab)?MLB?Vz3spM`T6FGQ{t}PdTltl%lfR+d6y-pLQE|Rrb;DUeQ+Q& z>kx1AW24{*1p__PB~vCgNxN`_=ry~~GkyK;V$BjUotFy_88A6<-3j7gEnXwo<>TFR zS}2^u^5_n`B`HTVRQ{Fp3l%N8WVz@9($FIE@>#u)*Z4(hc+a@pktPwxI zuvE9RT+RL&%S*VAHNO{k^1J_0g#XI)t2ftvbf}G-8*Bgg;`f9Pn?!k>zrOQ)g0)rL)mh1 zuVv=4f4^91@V}Ei`-!sLw>-mgAfdSDpmTQ4lZB?=`1seCikemau-j1mdit}*s~2^4 z+px_~mb<3E_Pe3JMf`!qU+w-ke100U|H02I>dVg^u1PDE3y#{q<=*v;XOk<|*W1OL zRaVShX~6UJVLJ1(2xgl34Hd%F6$ JtaD0e0sybjT8#hz delta 517 zcmV+g0{Z=m2fPH3L4O-iOjJex|Nq6s#oOE4b#-;1prB@EX8HN~v$L}R003SmUXTC) z010$bPE-H?|NsC0|Nj6}Pk0gl000SaNLh0L01m?d01m?e$8V@)0004%Nkls@V|8HDUWWM9Ebm(sf~-2%N>a(^k*H3?d~s@5qmp}5b$ zbVgc&Hih(Rv8RWkiJ%&TxmHyv%LoWn*JU{@sc2c?vK}SSqW}djfl@2>d0fIgZOpRb zg71?3QTQ2;VBxPm^Drmy!W?#FdVxS5-R@z+(H3M+ozL(6KZeNomtQ6BFl>>-%yNQZO+id~nR4*LctkgeK45kA{{7 z8*{k>sbWRF1Y*TLk4uKxlXVU+jqwG6gGOWmbLWadls^eG~GG1CQ(P zN6a|zNQG045K;^V!wtu@10H_TA>8}FWf$L53l@6$J?;Gg${Q@P)v{7@00000NkvXX Hu0mjfB#Q6G diff --git a/src/main/resources/assets/create/textures/gui/factory_gauge.png b/src/main/resources/assets/create/textures/gui/factory_gauge.png new file mode 100644 index 0000000000000000000000000000000000000000..3a9d6770e1c5cb43c9f1e5ad4e050c5b5afed884 GIT binary patch literal 2461 zcmai02~ZPh7XG`_4I$x3j2t2&LW$uHVB}bi1``m)7`X)Gh;j=eXc$m*Xpl=#K~X_L zjDW}yITVytfe;sT)+;-r;>qC!DkY34s8P0~GgCFSQ?-B9tM}gbj{o)R|MlN{_&iTF ziY^5JpysuL;{yOhh!7x?2o^66^iyctd_3Jj(=EL*LP28OR=WYvT11s^KnXpL@9oDW z`1$L@KOd-Zc3Kg=EjKTCwZPdSc_mi0Q?NI5)k+KN_L8KY`r@0H>&6Ez9gJS<&9XRK zw7F$p{Os(kP$;ysvnwnt92pr|?`StO^VWqqr?0Qi-`_tvI=cKsV|8`)nKNf^#%aSVlR%HUs~As@xtI5dz#0C6s3O-@ZP;{+m@Wz zHM^W=DwmN>e$50b_3!I5ujTbG@|SHUJy<`I588e)gk@(V5A0?IzNN|;>@Y+=-y${D z5Nv26jOKf!}qc=Yn?a*5!^N zpR;KAcyg5;+u}vbUnH(ReIKc0ax1^TuWQF^bk$6caQI~ZwUaCF^>)dk}aGLFF zz+R?OJ(T~CFMRl#KyKw2JK-5}#vN>aX z0)5vJax$!PpO0j({<)~2xjVvd)iRHL5Tqo&H4~i--a0<@Y|QV%{o4`kyo5t=N#wX? zh3DGQR@V|1GGsGwG?u`d5GH&>qPZ>wQZv0c!s~2F-(!j@g$=07PX8P>GH$g*=Crl+bidOZ81oA zwsD7%QcIYK1! z*aUKxu(3^eKPU(0T{?6F6Ium6R&J^{>)ca2oY)`>b59@Al?l4)rPA`nWkgkAn{Q5( z-6|Hn30ziNW)K1maJz;q>%xR5+x8FzmTDx5Xidbs4{sw{kA|AD0d+>2K^e^$7VL1D zyob-TY#~%9lv-!Q;+s{oj#pz?N_f2^&fh@w_{9Y!CB0we^TwpI;DwM(=pY$7<{b0!sVTB%}_C^TVL^?snWo zO_ZUbY5Gmvw`b;CnQPEXSG3;&xD5Lr@~@2Pc`k`-d=GZ4!NNy44kA^=M(K5eP*beH)^^q`*D};EWz%l=R7E?k`sdq5^mvYNcPRoCNz6PNeM8%B3&}Q z2wXiH>!xG=5uHf53TF1-L#^O0WohI4Bl4L=FiokDj9oW`Ig*Xgi9Gr9pRI?oUSLMx z#n*q^4qiHkUq7`+AeY2G**w#W_k`yHr@@Ox&(bsGXZ4luGbYNby5#|`X;61!9keoW