From 33028e1087de64ec7665450ef003132329835eab Mon Sep 17 00:00:00 2001 From: zelophed Date: Fri, 23 Apr 2021 18:11:32 +0200 Subject: [PATCH] please confirm --- .../config/ui/BaseConfigScreen.java | 7 +- .../foundation/config/ui/ConfigScreen.java | 108 +++---- .../config/ui/ConfigScreenList.java | 16 +- .../foundation/config/ui/ConfigTextField.java | 17 ++ .../config/ui/ServerSubMenuConfigScreen.java | 52 ---- .../config/ui/SubMenuConfigScreen.java | 265 ++++++++++++++++-- .../config/ui/entries/BooleanEntry.java | 19 +- .../config/ui/entries/EnumEntry.java | 11 +- .../config/ui/entries/NumberEntry.java | 15 +- .../config/ui/entries/SubMenuEntry.java | 10 +- .../config/ui/entries/ValueEntry.java | 71 +++-- .../create/foundation/gui/AllIcons.java | 16 +- .../foundation/gui/ConfirmationScreen.java | 186 ++++++++++++ .../gui/DelegatedStencilElement.java | 1 - .../create/foundation/gui/ScreenOpener.java | 2 +- .../create/foundation/gui/UIRenderHelper.java | 14 +- .../ponder/NavigatableSimiScreen.java | 10 +- .../assets/create/textures/gui/icons.png | Bin 2923 -> 17866 bytes 18 files changed, 610 insertions(+), 210 deletions(-) delete mode 100644 src/main/java/com/simibubi/create/foundation/config/ui/ServerSubMenuConfigScreen.java create mode 100644 src/main/java/com/simibubi/create/foundation/gui/ConfirmationScreen.java diff --git a/src/main/java/com/simibubi/create/foundation/config/ui/BaseConfigScreen.java b/src/main/java/com/simibubi/create/foundation/config/ui/BaseConfigScreen.java index 545548153..36e75d18b 100644 --- a/src/main/java/com/simibubi/create/foundation/config/ui/BaseConfigScreen.java +++ b/src/main/java/com/simibubi/create/foundation/config/ui/BaseConfigScreen.java @@ -10,6 +10,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screen.Screen; import net.minecraft.util.text.StringTextComponent; import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.fml.config.ModConfig; public class BaseConfigScreen extends ConfigScreen { @@ -29,14 +30,14 @@ public class BaseConfigScreen extends ConfigScreen { TextStencilElement text = new TextStencilElement(client.fontRenderer, new StringTextComponent("CLIENT CONFIG").formatted(TextFormatting.BOLD)).centered(true, true); text.withElementRenderer((ms, width, height) -> UIRenderHelper.angledGradient(ms, 0, 0, height / 2, height, width, ConfigButton.Palette.button_idle_1, ConfigButton.Palette.button_idle_2)); widgets.add(clientConfigWidget = new PonderButton(width / 2 - 100, height / 2 - 15 - 50, (_$, _$$) -> { - ScreenOpener.transitionTo(new SubMenuConfigScreen(this, AllConfigs.CLIENT.specification)); + ScreenOpener.open(new SubMenuConfigScreen(this, ModConfig.Type.CLIENT, AllConfigs.CLIENT.specification)); }, 200, 30).showingUnscaled(text)); clientConfigWidget.fade(1); TextStencilElement text2 = new TextStencilElement(client.fontRenderer, new StringTextComponent("COMMON CONFIG").formatted(TextFormatting.BOLD)).centered(true, true); text2.withElementRenderer((ms, width, height) -> UIRenderHelper.angledGradient(ms, 0, 0, height / 2, height, width, ConfigButton.Palette.button_idle_1, ConfigButton.Palette.button_idle_2)); widgets.add(commonConfigWidget = new PonderButton(width / 2 - 100, height / 2 - 15, (_$, _$$) -> { - ScreenOpener.transitionTo(new SubMenuConfigScreen(this, AllConfigs.COMMON.specification)); + ScreenOpener.open(new SubMenuConfigScreen(this, ModConfig.Type.COMMON, AllConfigs.COMMON.specification)); }, 200, 30).showingUnscaled(text2)); commonConfigWidget.fade(1); @@ -47,7 +48,7 @@ public class BaseConfigScreen extends ConfigScreen { serverConfigWidget.fade(1); if (Minecraft.getInstance().world != null) { - serverConfigWidget.withCallback(() -> ScreenOpener.transitionTo(new ServerSubMenuConfigScreen(this, AllConfigs.SERVER.specification))); + serverConfigWidget.withCallback(() -> ScreenOpener.open(new SubMenuConfigScreen(this, ModConfig.Type.SERVER, AllConfigs.SERVER.specification))); } else { serverConfigWidget.active = false; text3.withElementRenderer((ms, width, height) -> UIRenderHelper.angledGradient(ms, 0, 0, height / 2, height, width, ConfigButton.Palette.button_disable_1, ConfigButton.Palette.button_disable_2)); diff --git a/src/main/java/com/simibubi/create/foundation/config/ui/ConfigScreen.java b/src/main/java/com/simibubi/create/foundation/config/ui/ConfigScreen.java index 93a5d571b..64deb5e76 100644 --- a/src/main/java/com/simibubi/create/foundation/config/ui/ConfigScreen.java +++ b/src/main/java/com/simibubi/create/foundation/config/ui/ConfigScreen.java @@ -1,6 +1,8 @@ package com.simibubi.create.foundation.config.ui; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import java.util.stream.Collectors; import javax.annotation.Nonnull; @@ -11,10 +13,9 @@ import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.systems.RenderSystem; import com.simibubi.create.AllBlocks; import com.simibubi.create.content.contraptions.relays.elementary.CogWheelBlock; +import com.simibubi.create.foundation.gui.AbstractSimiScreen; import com.simibubi.create.foundation.gui.GuiGameElement; import com.simibubi.create.foundation.gui.StencilElement; -import com.simibubi.create.foundation.gui.TextStencilElement; -import com.simibubi.create.foundation.ponder.NavigatableSimiScreen; import com.simibubi.create.foundation.utility.animation.Force; import com.simibubi.create.foundation.utility.animation.PhysicalFloat; @@ -22,40 +23,32 @@ import net.minecraft.block.BlockState; import net.minecraft.client.gui.screen.Screen; import net.minecraft.util.Direction; -public abstract class ConfigScreen extends NavigatableSimiScreen { +public abstract class ConfigScreen extends AbstractSimiScreen { /* - * - * TODO - * zelo's list for configUI - * - * match style with ponderUI - * cache changes before setting values and saving to file - * don't exit on ESC - * reset text field focus for any click inside screen - * adjust transition animation of screens - * allow backspace in text fields - * move config button's animations to ponder button or a new superclass - * get some proper icons for reset button and enum cycle - * some small shadow effect for top and bottom of the list - * add the 'think back' button back, just with a different caption - * add a title to the current config screen, maybe in the form of breadcrumbs - * - * some color themes maybe? - * at least a helper class to unite colors throughout different uis - * - * FIXME - * - * tooltip are hidden underneath the scrollbar, if the bar is near the middle - * misalignment of the label-streak and textboxes/enum stuff - * - * */ + * + * TODO + * zelo's list for configUI + * + * adjust transition animation of screens -> disabled for now + * move config button's animations to ponder button or a new superclass + * get some proper icons for reset button and enum cycle + * + * some color themes maybe? + * at least a helper class to unite colors throughout different uis + * + * FIXME + * + * tooltip are hidden underneath the scrollbar, if the bar is near the middle + * misalignment of the label-streak and textboxes/enum stuff + * framebuffer blending is incorrect + * + * */ + public static final PhysicalFloat cogSpin = PhysicalFloat.create().withDrag(0.3).addForce(new Force.Static(.2f)); + public static final BlockState cogwheelState = AllBlocks.LARGE_COGWHEEL.getDefaultState().with(CogWheelBlock.AXIS, Direction.Axis.Y); + public static final Map changes = new HashMap<>(); protected final Screen parent; - protected static final PhysicalFloat cogSpin = PhysicalFloat.create().withDrag(0.3).addForce(new Force.Static(.2f)); - protected static final BlockState cogwheelState = AllBlocks.LARGE_COGWHEEL.getDefaultState().with(CogWheelBlock.AXIS, Direction.Axis.Y); - - protected StencilElement testStencil; public ConfigScreen(Screen parent) { this.parent = parent; @@ -63,47 +56,24 @@ public abstract class ConfigScreen extends NavigatableSimiScreen { @Override public void tick() { - cogSpin.tick(); - - widgets.stream() - .filter(w -> w instanceof ConfigButton) - .forEach(w -> ((ConfigButton) w).tick()); - super.tick(); - } - - @Override - protected void init() { - /*super.init(); - if (backTrack != null) { - widgets.remove(backTrack); - backTrack = null; - }*/ - - - testStencil = new TextStencilElement(client.fontRenderer, "POGGERS").at(width*0.5f, height*0.5f, 0); + cogSpin.tick(); } @Override public void renderBackground(@Nonnull MatrixStack ms) { - //fill(ms, 0, 0, this.width, this.height, 0xe8_101010); net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.client.event.GuiScreenEvent.BackgroundDrawnEvent(this, ms)); } @Override protected void renderWindowBackground(MatrixStack ms, int mouseX, int mouseY, float partialTicks) { RenderSystem.disableDepthTest(); - if (this.client != null && this.client.world != null){ + if (this.client != null && this.client.world != null) { fill(ms, 0, 0, this.width, this.height, 0xb0_282c34); } else { fill(ms, 0, 0, this.width, this.height, 0xff_282c34); } - /*ms.push(); - ms.translate(width*0.5f, height*0.5f, 0); - renderCog(ms, partialTicks); - ms.pop();*/ - new StencilElement() { @Override protected void renderStencil(MatrixStack ms) { @@ -120,18 +90,6 @@ public abstract class ConfigScreen extends NavigatableSimiScreen { } - protected void renderCog(MatrixStack ms, float partialTicks) { - ms.push(); - - ms.translate(-100, 100, -200); - ms.scale(200, 200, .1f); - GuiGameElement.of(cogwheelState) - .rotateBlock(22.5, cogSpin.getValue(partialTicks), 22.5) - .render(ms); - - ms.pop(); - } - @Override protected void renderWindow(MatrixStack ms, int mouseX, int mouseY, float partialTicks) { int x = (int) (width * 0.5f); @@ -155,4 +113,16 @@ public abstract class ConfigScreen extends NavigatableSimiScreen { String s = Arrays.stream(StringUtils.splitByCharacterTypeCamelCase(key)).map(StringUtils::capitalize).collect(Collectors.joining(" ")); return s; } + + protected void renderCog(MatrixStack ms, float partialTicks) { + ms.push(); + + ms.translate(-100, 100, -100); + ms.scale(200, 200, .1f); + GuiGameElement.of(cogwheelState) + .rotateBlock(22.5, cogSpin.getValue(partialTicks), 22.5) + .render(ms); + + ms.pop(); + } } diff --git a/src/main/java/com/simibubi/create/foundation/config/ui/ConfigScreenList.java b/src/main/java/com/simibubi/create/foundation/config/ui/ConfigScreenList.java index 237ba65f2..304ae9cce 100644 --- a/src/main/java/com/simibubi/create/foundation/config/ui/ConfigScreenList.java +++ b/src/main/java/com/simibubi/create/foundation/config/ui/ConfigScreenList.java @@ -7,6 +7,7 @@ import org.lwjgl.opengl.GL11; import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.systems.RenderSystem; +import com.simibubi.create.foundation.config.ui.entries.NumberEntry; import com.simibubi.create.foundation.gui.TextStencilElement; import com.simibubi.create.foundation.gui.UIRenderHelper; @@ -22,7 +23,7 @@ import net.minecraftforge.fml.client.gui.GuiUtils; public class ConfigScreenList extends ExtendedList { - public TextFieldWidget currentText; + public static TextFieldWidget currentText; public boolean isForServer = false; @@ -31,12 +32,16 @@ public class ConfigScreenList extends ExtendedList { func_244605_b(false); func_244606_c(false); setRenderSelection(false); + currentText = null; } @Override public void render(MatrixStack ms, int mouseX, int mouseY, float partialTicks) { //render tmp background - fill(ms, left, top, left + width, top + height, 0x10_000000); + //fill(ms, left, top, left + width, top + height, 0x10_000000); + + UIRenderHelper.angledGradient(ms, 90, left + width / 2, top, width, 5, 0x60_000000, 0x0); + UIRenderHelper.angledGradient(ms, -90, left + width / 2, bottom, width, 5, 0x60_000000, 0x0); super.render(ms, mouseX, mouseY, partialTicks); } @@ -50,6 +55,13 @@ public class ConfigScreenList extends ExtendedList { RenderSystem.disableScissor(); } + @Override + public boolean mouseClicked(double x, double y, int button) { + children().stream().filter(e -> e instanceof NumberEntry).forEach(e -> e.mouseClicked(x, y, button)); + + return super.mouseClicked(x, y, button); + } + @Override public int getRowWidth() { return width - 18; diff --git a/src/main/java/com/simibubi/create/foundation/config/ui/ConfigTextField.java b/src/main/java/com/simibubi/create/foundation/config/ui/ConfigTextField.java index 27655f40b..a0dc991ab 100644 --- a/src/main/java/com/simibubi/create/foundation/config/ui/ConfigTextField.java +++ b/src/main/java/com/simibubi/create/foundation/config/ui/ConfigTextField.java @@ -30,4 +30,21 @@ public class ConfigTextField extends TextFieldWidget { font.draw(ms, unit, x + getAdjustedWidth() - unitWidth, this.y + (this.height - 8) / 2, 0xcc_aaaaaa); } + + @Override + public void setFocused2(boolean focus) { + super.setFocused2(focus); + + if (!focus) { + if (ConfigScreenList.currentText == this) + ConfigScreenList.currentText = null; + + return; + } + + if (ConfigScreenList.currentText != null && ConfigScreenList.currentText != this) + ConfigScreenList.currentText.setFocused2(false); + + ConfigScreenList.currentText = this; + } } diff --git a/src/main/java/com/simibubi/create/foundation/config/ui/ServerSubMenuConfigScreen.java b/src/main/java/com/simibubi/create/foundation/config/ui/ServerSubMenuConfigScreen.java deleted file mode 100644 index 023cd9ddb..000000000 --- a/src/main/java/com/simibubi/create/foundation/config/ui/ServerSubMenuConfigScreen.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.simibubi.create.foundation.config.ui; - -import com.electronwill.nightconfig.core.UnmodifiableConfig; -import com.simibubi.create.foundation.gui.AllIcons; -import com.simibubi.create.foundation.gui.DelegatedStencilElement; -import com.simibubi.create.foundation.gui.UIRenderHelper; -import com.simibubi.create.foundation.item.TooltipHelper; -import com.simibubi.create.foundation.ponder.ui.PonderButton; - -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.util.text.StringTextComponent; -import net.minecraft.util.text.TextFormatting; -import net.minecraftforge.common.ForgeConfigSpec; - -public class ServerSubMenuConfigScreen extends SubMenuConfigScreen { - - protected PonderButton missingPermissions = null; - - public ServerSubMenuConfigScreen(Screen parent, ForgeConfigSpec configSpec) { - super(parent, configSpec); - } - - public ServerSubMenuConfigScreen(Screen parent, ForgeConfigSpec configSpec, UnmodifiableConfig configGroup) { - super(parent, configSpec, configGroup); - } - - @Override - protected void init() { - super.init(); - - list.isForServer = true; - - if (client != null && client.player != null && client.player.hasPermissionLevel(2)) - return; - - list.children().forEach(e -> e.setEditable(false)); - - int col1 = 0xff_f78888; - int col2 = 0xff_cc2020; - - missingPermissions = new PonderButton(width - 30, height - 50, () -> {}) - .showing(new DelegatedStencilElement() - .withStencilRenderer((ms, w, h) -> AllIcons.I_MTD_CLOSE.draw(ms, 0, 0)) - .withElementRenderer((ms, w, h) -> UIRenderHelper.angledGradient(ms, 90, 8, 0, 16, 16, col1, col2)) - ).customColors(col1, col2); - missingPermissions.fade(1); - missingPermissions.getToolTip().add(new StringTextComponent("Locked").formatted(TextFormatting.BOLD)); - missingPermissions.getToolTip().addAll(TooltipHelper.cutStringTextComponent("You don't have enough permissions to edit the server config. You can still look at the current values here though.", TextFormatting.GRAY, TextFormatting.GRAY)); - - widgets.add(missingPermissions); - } -} diff --git a/src/main/java/com/simibubi/create/foundation/config/ui/SubMenuConfigScreen.java b/src/main/java/com/simibubi/create/foundation/config/ui/SubMenuConfigScreen.java index 58de335ac..408343255 100644 --- a/src/main/java/com/simibubi/create/foundation/config/ui/SubMenuConfigScreen.java +++ b/src/main/java/com/simibubi/create/foundation/config/ui/SubMenuConfigScreen.java @@ -1,8 +1,9 @@ package com.simibubi.create.foundation.config.ui; import javax.annotation.Nonnull; +import javax.annotation.Nullable; -import org.apache.commons.lang3.mutable.MutableInt; +import org.lwjgl.glfw.GLFW; import com.electronwill.nightconfig.core.AbstractConfig; import com.electronwill.nightconfig.core.UnmodifiableConfig; @@ -11,30 +12,93 @@ import com.simibubi.create.foundation.config.ui.entries.BooleanEntry; import com.simibubi.create.foundation.config.ui.entries.EnumEntry; import com.simibubi.create.foundation.config.ui.entries.NumberEntry; import com.simibubi.create.foundation.config.ui.entries.SubMenuEntry; +import com.simibubi.create.foundation.config.ui.entries.ValueEntry; +import com.simibubi.create.foundation.gui.AllIcons; +import com.simibubi.create.foundation.gui.ConfirmationScreen; +import com.simibubi.create.foundation.gui.DelegatedStencilElement; +import com.simibubi.create.foundation.gui.ScreenOpener; +import com.simibubi.create.foundation.gui.TextStencilElement; +import com.simibubi.create.foundation.gui.UIRenderHelper; +import com.simibubi.create.foundation.item.TooltipHelper; +import com.simibubi.create.foundation.ponder.ui.PonderButton; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.IGuiEventListener; import net.minecraft.client.gui.screen.Screen; +import net.minecraft.util.text.ITextProperties; +import net.minecraft.util.text.StringTextComponent; +import net.minecraft.util.text.TextFormatting; import net.minecraftforge.common.ForgeConfigSpec; +import net.minecraftforge.fml.config.ModConfig; public class SubMenuConfigScreen extends ConfigScreen { - ForgeConfigSpec spec; - UnmodifiableConfig configGroup; - ConfigScreenList list; + public final ModConfig.Type type; + protected ForgeConfigSpec spec; + protected UnmodifiableConfig configGroup; + protected ConfigScreenList list; + + protected PonderButton resetAll; + protected PonderButton saveChanges; + protected PonderButton discardChanges; + protected PonderButton goBack; + protected PonderButton serverLocked; + protected int listWidth; + protected String title; - public SubMenuConfigScreen(Screen parent, ForgeConfigSpec configSpec, UnmodifiableConfig configGroup) { + public SubMenuConfigScreen(Screen parent, String title, ModConfig.Type type, ForgeConfigSpec configSpec, UnmodifiableConfig configGroup) { super(parent); + this.type = type; this.spec = configSpec; + this.title = title; this.configGroup = configGroup; } - public SubMenuConfigScreen(Screen parent, ForgeConfigSpec configSpec) { + public SubMenuConfigScreen(Screen parent, ModConfig.Type type, ForgeConfigSpec configSpec) { super(parent); + this.type = type; this.spec = configSpec; + this.title = "root"; this.configGroup = configSpec.getValues(); } + protected void clearChanges() { + changes.clear(); + list.children() + .stream() + .filter(e -> e instanceof ValueEntry) + .forEach(e -> ((ValueEntry) e).onValueChange()); + } + + protected void saveChanges() { + UnmodifiableConfig values = spec.getValues(); + changes.forEach((path, value) -> { + ForgeConfigSpec.ConfigValue configValue = values.get(path); + configValue.set(value); + }); + clearChanges(); + } + + protected void resetConfig(UnmodifiableConfig values) { + values.valueMap().forEach((key, obj) -> { + if (obj instanceof AbstractConfig) { + resetConfig((UnmodifiableConfig) obj); + } else if (obj instanceof ForgeConfigSpec.ConfigValue) { + ForgeConfigSpec.ConfigValue configValue = (ForgeConfigSpec.ConfigValue) obj; + ForgeConfigSpec.ValueSpec valueSpec = spec.getRaw(configValue.getPath()); + + if (!configValue.get().equals(valueSpec.getDefault())) + changes.put(String.join(".", configValue.getPath()), valueSpec.getDefault()); + } + }); + + list.children() + .stream() + .filter(e -> e instanceof ValueEntry) + .forEach(e -> ((ValueEntry) e).onValueChange()); + } + @Override public void tick() { super.tick(); @@ -46,23 +110,88 @@ public class SubMenuConfigScreen extends ConfigScreen { widgets.clear(); super.init(); - int lWidth = Math.min(width - 66, 500); - list = new ConfigScreenList(client, lWidth, height - 30, 15, height - 15, 50); + //leave 40px on either side and dont be wider than 500px + listWidth = Math.min(width - 80, 500); + + int yCenter = height / 2; + int listL = this.width / 2 - listWidth / 2; + int listR = this.width / 2 + listWidth / 2; + + resetAll = new PonderButton(listR + 10, yCenter - 25, (x, y) -> { + new ConfirmationScreen() + .at(x, y) + .withText(ITextProperties.plain("You are about to reset all settings for the " + type.toString() + " config. Are you sure?")) + .withAction(success -> { + if (success) + resetConfig(spec.getValues()); + }) + .open(this); + }) + .showing(AllIcons.I_CONFIG_RESET.asStencil()); + resetAll.fade(1); + resetAll.getToolTip().add(new StringTextComponent("Reset All")); + resetAll.getToolTip().addAll(TooltipHelper.cutStringTextComponent("Click here to reset all configs to their default value.", TextFormatting.GRAY, TextFormatting.GRAY)); + + saveChanges = new PonderButton(listL - 30, yCenter - 25, (x, y) -> { + if (changes.isEmpty()) + return; + + new ConfirmationScreen() + .at(x, y) + .withText(ITextProperties.plain("You are about to change " + changes.size() + " values. Are you sure?")) + .withAction(success -> { + if (success) + saveChanges(); + }) + .open(this); + }) + .showing(AllIcons.I_CONFIG_SAVE.asStencil()); + saveChanges.fade(1); + saveChanges.getToolTip().add(new StringTextComponent("Save Changes")); + saveChanges.getToolTip().addAll(TooltipHelper.cutStringTextComponent("Click here to save your current changes.", TextFormatting.GRAY, TextFormatting.GRAY)); + + discardChanges = new PonderButton(listL - 30, yCenter + 5, (x, y) -> { + if (changes.isEmpty()) + return; + + new ConfirmationScreen() + .at(x, y) + .withText(ITextProperties.plain("You are about to discard " + changes.size() + " unsaved changes. Are you sure?")) + .withAction(success -> { + if (success) + clearChanges(); + }) + .open(this); + }) + .showing(AllIcons.I_CONFIG_DISCARD.asStencil()); + discardChanges.fade(1); + discardChanges.getToolTip().add(new StringTextComponent("Discard Changes")); + discardChanges.getToolTip().addAll(TooltipHelper.cutStringTextComponent("Click here to discard all the changes you made.", TextFormatting.GRAY, TextFormatting.GRAY)); + + goBack = new PonderButton(listL - 30, yCenter + 65, this::attemptBackstep) + .showing(AllIcons.I_CONFIG_BACK); + goBack.fade(1); + goBack.getToolTip().add(new StringTextComponent("Go Back")); + + widgets.add(resetAll); + widgets.add(saveChanges); + widgets.add(discardChanges); + widgets.add(goBack); + + list = new ConfigScreenList(client, listWidth, height - 60, 45, height - 15, 50); list.setLeftPos(this.width / 2 - list.getWidth() / 2); children.add(list); - MutableInt y = new MutableInt(15); + configGroup.valueMap().forEach((key, obj) -> { + String humanKey = toHumanReadable(key); - configGroup.valueMap().forEach((s, o) -> { - String humanKey = toHumanReadable(s); - - if (o instanceof AbstractConfig) { - SubMenuEntry entry = new SubMenuEntry(this, humanKey, spec, (UnmodifiableConfig) o); + if (obj instanceof AbstractConfig) { + SubMenuEntry entry = new SubMenuEntry(this, humanKey, spec, (UnmodifiableConfig) obj); list.children().add(entry); - } else if (o instanceof ForgeConfigSpec.ConfigValue) { - ForgeConfigSpec.ConfigValue configValue = (ForgeConfigSpec.ConfigValue) o; + } else if (obj instanceof ForgeConfigSpec.ConfigValue) { + ForgeConfigSpec.ConfigValue configValue = (ForgeConfigSpec.ConfigValue) obj; ForgeConfigSpec.ValueSpec valueSpec = spec.getRaw(configValue.getPath()); Object value = configValue.get(); @@ -77,21 +206,58 @@ public class SubMenuConfigScreen extends ConfigScreen { if (entry != null) { list.children().add(entry); } else { - list.children().add(new ConfigScreenList.LabeledEntry("n-" + o.getClass().getSimpleName() + " " + humanKey + " : " + value)); + list.children().add(new ConfigScreenList.LabeledEntry("n-" + obj.getClass().getSimpleName() + " " + humanKey + " : " + value)); } } else { list.children().add(new ConfigScreenList.LabeledEntry(humanKey + " : " + value)); } } - - y.add(50); }); + + //extras for sever configs + if (type != ModConfig.Type.SERVER) + return; + + list.isForServer = true; + boolean canEdit = client != null && client.player != null && client.player.hasPermissionLevel(2); + + int colRed1 = 0xff_f78888; + int colRed2 = 0xff_cc2020; + int colGreen1 = 0xff_88f788; + int colGreen2 = 0xff_20cc20; + + DelegatedStencilElement stencil = new DelegatedStencilElement(); + + serverLocked = new PonderButton(listR + 10, yCenter + 5, () -> {}) + .showing(stencil); + serverLocked.fade(1); + + if (!canEdit) { + list.children().forEach(e -> e.setEditable(false)); + resetAll.active = false; + stencil.withStencilRenderer((ms, w, h) -> AllIcons.I_CONFIG_LOCKED.draw(ms, 0, 0)); + stencil.withElementRenderer((ms, w, h) -> UIRenderHelper.angledGradient(ms, 90, 8, 0, 16, 16, colRed1, colRed2)); + serverLocked.customColors(colRed1, colRed2); + serverLocked.getToolTip().add(new StringTextComponent("Locked").formatted(TextFormatting.BOLD)); + serverLocked.getToolTip().addAll(TooltipHelper.cutStringTextComponent("You don't have enough permissions to edit the server config. You can still look at the current values here though.", TextFormatting.GRAY, TextFormatting.GRAY)); + } else { + stencil.withStencilRenderer((ms, w, h) -> AllIcons.I_CONFIG_UNLOCKED.draw(ms, 0, 0)); + stencil.withElementRenderer((ms, w, h) -> UIRenderHelper.angledGradient(ms, 90, 8, 0, 16, 16, colGreen1, colGreen2)); + serverLocked.customColors(colGreen1, colGreen2); + serverLocked.getToolTip().add(new StringTextComponent("Unlocked").formatted(TextFormatting.BOLD)); + serverLocked.getToolTip().addAll(TooltipHelper.cutStringTextComponent("You have enough permissions to edit the server config. Changes you make here will be synced with the server once you saved them.", TextFormatting.GRAY, TextFormatting.GRAY)); + } + + widgets.add(serverLocked); } @Override protected void renderWindow(MatrixStack ms, int mouseX, int mouseY, float partialTicks) { super.renderWindow(ms, mouseX, mouseY, partialTicks); + int x = width/2; + drawCenteredString(ms, client.fontRenderer, "Editing config: " + type.toString() + "@" + title, x, 15, 0xff_eaeaea); + list.render(ms, mouseX, mouseY, partialTicks); } @@ -106,4 +272,65 @@ public class SubMenuConfigScreen extends ConfigScreen { init(client, width, height); list.setScrollAmount(scroll); } + + @Nullable + @Override + public IGuiEventListener getFocused() { + if (ConfigScreenList.currentText != null) + return ConfigScreenList.currentText; + + return super.getFocused(); + } + + @Override + public boolean keyPressed(int code, int p_keyPressed_2_, int p_keyPressed_3_) { + if (super.keyPressed(code, p_keyPressed_2_, p_keyPressed_3_)) + return true; + + if (code == GLFW.GLFW_KEY_BACKSPACE) { + attemptBackstep(); + } + + return false; + } + + private void attemptBackstep() { + if (!changes.isEmpty() && parent instanceof BaseConfigScreen) { + new ConfirmationScreen() + .centered() + .addText(ITextProperties.plain("You still have " + changes.size() + " unsaved changes for this config.")) + .addText(ITextProperties.plain("Leaving this screen will discard them without saving. Are you sure?")) + .withAction(success -> { + if (!success) + return; + + changes.clear(); + ScreenOpener.open(parent); + }) + .open(this); + } else { + ScreenOpener.open(parent); + } + } + + @Override + public void onClose() { + if (changes.isEmpty()) { + super.onClose(); + return; + } + + new ConfirmationScreen() + .centered() + .addText(ITextProperties.plain("You still have " + changes.size() + " unsaved changes for this config.")) + .addText(ITextProperties.plain("Leaving this screen will discard them without saving. Are you sure?")) + .withAction(success -> { + if (!success) + return; + + changes.clear(); + super.onClose(); + }) + .open(this); + } } diff --git a/src/main/java/com/simibubi/create/foundation/config/ui/entries/BooleanEntry.java b/src/main/java/com/simibubi/create/foundation/config/ui/entries/BooleanEntry.java index 6aa6693d6..2c9e37338 100644 --- a/src/main/java/com/simibubi/create/foundation/config/ui/entries/BooleanEntry.java +++ b/src/main/java/com/simibubi/create/foundation/config/ui/entries/BooleanEntry.java @@ -25,10 +25,8 @@ public class BooleanEntry extends ValueEntry { .centered(true, true) .withElementRenderer((ms, width, height) -> UIRenderHelper.angledGradient(ms, 0, 0, height/2, height, width, 0xff_f78888, 0xff_cc2020)); - button = new PonderButton(0, 0, () -> { - value.set(!value.get()); - onValueChange(); - }).showingUnscaled(enabled); + button = new PonderButton(0, 0, () -> setValue(!getValue())) + .showingUnscaled(enabled); button.fade(1); listeners.add(button); @@ -58,14 +56,9 @@ public class BooleanEntry extends ValueEntry { } @Override - protected void onValueChange() { - super.onValueChange(); - button.showingUnscaled(value.get() ? enabled : disabled); - bumpCog(value.get() ? 15f : -16f); + public void onValueChange(Boolean newValue) { + super.onValueChange(newValue); + button.showingUnscaled(newValue ? enabled : disabled); + bumpCog(newValue ? 15f : -16f); } - - /*@Override - public boolean mouseClicked(double mX, double mY, int button) { - return this.button.mouseClicked(mX, mY, button) || super.mouseClicked(mX, mY, button); - }*/ } diff --git a/src/main/java/com/simibubi/create/foundation/config/ui/entries/EnumEntry.java b/src/main/java/com/simibubi/create/foundation/config/ui/entries/EnumEntry.java index 0c9eddec7..e4df547e3 100644 --- a/src/main/java/com/simibubi/create/foundation/config/ui/entries/EnumEntry.java +++ b/src/main/java/com/simibubi/create/foundation/config/ui/entries/EnumEntry.java @@ -35,12 +35,11 @@ public class EnumEntry extends ValueEntry> { } protected void cycleValue(int direction) { - Enum e = value.get(); + Enum e = getValue(); Enum[] options = e.getDeclaringClass().getEnumConstants(); e = options[Math.floorMod(e.ordinal() + direction, options.length)]; - value.set(e); + setValue(e); bumpCog(direction * 15f); - onValueChange(); } @Override @@ -79,8 +78,8 @@ public class EnumEntry extends ValueEntry> { } @Override - protected void onValueChange() { - super.onValueChange(); - valueText.withText(value.get().name()); + public void onValueChange(Enum newValue) { + super.onValueChange(newValue); + valueText.withText(newValue.name()); } } diff --git a/src/main/java/com/simibubi/create/foundation/config/ui/entries/NumberEntry.java b/src/main/java/com/simibubi/create/foundation/config/ui/entries/NumberEntry.java index 1d8c95537..f4a1b6115 100644 --- a/src/main/java/com/simibubi/create/foundation/config/ui/entries/NumberEntry.java +++ b/src/main/java/com/simibubi/create/foundation/config/ui/entries/NumberEntry.java @@ -39,7 +39,7 @@ public abstract class NumberEntry extends ValueEntry { public NumberEntry(String label, ForgeConfigSpec.ConfigValue value, ForgeConfigSpec.ValueSpec spec) { super(label, value, spec); textField = new ConfigTextField(Minecraft.getInstance().fontRenderer, 0, 0, 200, 30, unit); - textField.setText(String.valueOf(value.get())); + textField.setText(String.valueOf(getValue())); Object range = spec.getRange(); try { @@ -74,8 +74,7 @@ public abstract class NumberEntry extends ValueEntry { throw new IllegalArgumentException(); textField.setTextColor(0xff_20cc20); - value.set(number); - onValueChange(); + setValue(number); } catch (IllegalArgumentException ignored) { textField.setTextColor(0xff_cc2020); @@ -105,9 +104,13 @@ public abstract class NumberEntry extends ValueEntry { } @Override - protected void onReset() { - super.onReset(); - textField.setText(String.valueOf(value.get())); + public void onValueChange(T newValue) { + super.onValueChange(newValue); + String newText = String.valueOf(newValue); + if (textField.getText().equals(newText)) + return; + + textField.setText(newText); } @Override diff --git a/src/main/java/com/simibubi/create/foundation/config/ui/entries/SubMenuEntry.java b/src/main/java/com/simibubi/create/foundation/config/ui/entries/SubMenuEntry.java index 68f315f3a..131ce5c48 100644 --- a/src/main/java/com/simibubi/create/foundation/config/ui/entries/SubMenuEntry.java +++ b/src/main/java/com/simibubi/create/foundation/config/ui/entries/SubMenuEntry.java @@ -4,7 +4,6 @@ import com.electronwill.nightconfig.core.UnmodifiableConfig; import com.mojang.blaze3d.matrix.MatrixStack; import com.simibubi.create.foundation.config.ui.ConfigButton; import com.simibubi.create.foundation.config.ui.ConfigScreenList; -import com.simibubi.create.foundation.config.ui.ServerSubMenuConfigScreen; import com.simibubi.create.foundation.config.ui.SubMenuConfigScreen; import com.simibubi.create.foundation.gui.ScreenOpener; import com.simibubi.create.foundation.gui.TextStencilElement; @@ -12,21 +11,18 @@ import com.simibubi.create.foundation.gui.UIRenderHelper; import com.simibubi.create.foundation.ponder.ui.PonderButton; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screen.Screen; import net.minecraftforge.common.ForgeConfigSpec; public class SubMenuEntry extends ConfigScreenList.LabeledEntry { protected PonderButton button; - public SubMenuEntry(Screen parent, String label, ForgeConfigSpec spec, UnmodifiableConfig config) { + public SubMenuEntry(SubMenuConfigScreen parent, String label, ForgeConfigSpec spec, UnmodifiableConfig config) { super(label); TextStencilElement text = new TextStencilElement(Minecraft.getInstance().fontRenderer, "Click to open").centered(true, true); text.withElementRenderer((ms, width, height) -> UIRenderHelper.angledGradient(ms, 0 ,0, height/2, height, width, ConfigButton.Palette.button_idle_1, ConfigButton.Palette.button_idle_2)); - button = new PonderButton(0, 0, () -> ScreenOpener.transitionTo(isForServer() ? - new ServerSubMenuConfigScreen(parent, spec, config) : - new SubMenuConfigScreen(parent, spec, config)) - ).showingUnscaled(text); + button = new PonderButton(0, 0, () -> ScreenOpener.open(new SubMenuConfigScreen(parent, label, parent.type, spec, config))) + .showingUnscaled(text); button.fade(1); listeners.add(button); diff --git a/src/main/java/com/simibubi/create/foundation/config/ui/entries/ValueEntry.java b/src/main/java/com/simibubi/create/foundation/config/ui/entries/ValueEntry.java index 998b7f98c..ff015c702 100644 --- a/src/main/java/com/simibubi/create/foundation/config/ui/entries/ValueEntry.java +++ b/src/main/java/com/simibubi/create/foundation/config/ui/entries/ValueEntry.java @@ -6,44 +6,49 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import javax.annotation.Nonnull; + import org.apache.commons.lang3.ArrayUtils; import com.mojang.blaze3d.matrix.MatrixStack; -import com.simibubi.create.foundation.config.ui.CConfigureConfigPacket; import com.simibubi.create.foundation.config.ui.ConfigButton; +import com.simibubi.create.foundation.config.ui.ConfigScreen; import com.simibubi.create.foundation.config.ui.ConfigScreenList; -import com.simibubi.create.foundation.gui.TextStencilElement; +import com.simibubi.create.foundation.gui.AllIcons; +import com.simibubi.create.foundation.gui.DelegatedStencilElement; import com.simibubi.create.foundation.gui.UIRenderHelper; -import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.ponder.ui.PonderButton; -import net.minecraft.client.Minecraft; +import net.minecraft.util.text.IFormattableTextComponent; import net.minecraft.util.text.StringTextComponent; import net.minecraft.util.text.TextFormatting; import net.minecraftforge.common.ForgeConfigSpec; public class ValueEntry extends ConfigScreenList.LabeledEntry { + protected static final IFormattableTextComponent modComponent = new StringTextComponent("* ").formatted(TextFormatting.BOLD, TextFormatting.DARK_BLUE).append(StringTextComponent.EMPTY.copy().formatted(TextFormatting.RESET)); protected static final int resetWidth = 28;//including 6px offset on either side public static final Pattern unitPattern = Pattern.compile("\\[(in .*)]"); + //public static DelegatedStencilElement.ElementRenderer idle = (ms, w, h) -> UIRenderHelper.angledGradient(ms, 0, 0, h / 2, h, w, ConfigButton.Palette.button_idle_1, ConfigButton.Palette.button_idle_2); protected ForgeConfigSpec.ConfigValue value; protected ForgeConfigSpec.ValueSpec spec; protected PonderButton resetButton; protected boolean editable = true; protected String unit = null; + protected String path; public ValueEntry(String label, ForgeConfigSpec.ConfigValue value, ForgeConfigSpec.ValueSpec spec) { super(label); this.value = value; this.spec = spec; + this.path = String.join(".", value.getPath()); - TextStencilElement text = new TextStencilElement(Minecraft.getInstance().fontRenderer, "R").centered(true, true); - text.withElementRenderer((ms, width, height) -> UIRenderHelper.angledGradient(ms, 0 ,0, height/2, height, width, ConfigButton.Palette.button_idle_1, ConfigButton.Palette.button_idle_2)); resetButton = new PonderButton(0, 0, (_$, _$$) -> { - value.set((T) spec.getDefault()); + setValue((T) spec.getDefault()); this.onReset(); - }, resetWidth - 12, 16).showingUnscaled(text); + }, resetWidth - 12, 16) + .showing(AllIcons.I_CONFIG_RESET.asStencil()/*.withElementRenderer(idle)*/); resetButton.fade(1); listeners.add(resetButton); @@ -82,12 +87,20 @@ public class ValueEntry extends ConfigScreenList.LabeledEntry { @Override protected void setEditable(boolean b) { editable = b; - resetButton.active = editable && !value.get().equals(spec.getDefault()); + resetButton.active = editable && !isCurrentValueDefault(); } @Override public void render(MatrixStack ms, int index, int y, int x, int width, int height, int mouseX, int mouseY, boolean p_230432_9_, float partialTicks) { - super.render(ms, index, y, x, width, height, mouseX, mouseY, p_230432_9_, partialTicks); + if (isCurrentValueChanged()) { + IFormattableTextComponent original = label.getComponent(); + IFormattableTextComponent changed = modComponent.copy().append(original); + label.withText(changed); + super.render(ms, index, y, x, width, height, mouseX, mouseY, p_230432_9_, partialTicks); + label.withText(original); + } else { + super.render(ms, index, y, x, width, height, mouseX, mouseY, p_230432_9_, partialTicks); + } resetButton.x = x + width - resetWidth + 6; resetButton.y = y + 15; @@ -99,18 +112,40 @@ public class ValueEntry extends ConfigScreenList.LabeledEntry { return (int) (totalWidth * labelWidthMult); } - protected void onReset() { - onValueChange(); + public void setValue(@Nonnull T value) { + if (value.equals(this.value.get())) { + ConfigScreen.changes.remove(path); + onValueChange(value); + return; + } + + ConfigScreen.changes.put(path, value); + onValueChange(value); } - protected void onValueChange() { - resetButton.active = editable && !value.get().equals(spec.getDefault()); + @Nonnull + public T getValue() { + //noinspection unchecked + return (T) ConfigScreen.changes.getOrDefault(path, this.value.get()); + } - if (!isForServer()) - return; + protected boolean isCurrentValueChanged() { + return ConfigScreen.changes.containsKey(path); + } - String path = String.join(".", value.getPath()); - AllPackets.channel.sendToServer(new CConfigureConfigPacket<>(path, value.get())); + protected boolean isCurrentValueDefault() { + return spec.getDefault().equals(getValue()); + } + + public void onReset() { + onValueChange(getValue()); + } + + public void onValueChange() { + onValueChange(getValue()); + } + public void onValueChange(T newValue) { + resetButton.active = editable && !isCurrentValueDefault(); } protected void bumpCog() {bumpCog(10f);} diff --git a/src/main/java/com/simibubi/create/foundation/gui/AllIcons.java b/src/main/java/com/simibubi/create/foundation/gui/AllIcons.java index fc489f449..f81cceb59 100644 --- a/src/main/java/com/simibubi/create/foundation/gui/AllIcons.java +++ b/src/main/java/com/simibubi/create/foundation/gui/AllIcons.java @@ -124,8 +124,15 @@ public class AllIcons implements IScreenRenderable { I_MTD_SCAN = next(), I_MTD_REPLAY = next(), I_MTD_USER_MODE = next(), - I_MTD_SLOW_MODE = next(); - + I_MTD_SLOW_MODE = next(), + + I_CONFIG_UNLOCKED = newRow(), + I_CONFIG_LOCKED = next(), + I_CONFIG_DISCARD = next(), + I_CONFIG_SAVE = next(), + I_CONFIG_RESET = next(), + I_CONFIG_BACK = next(); + public AllIcons(int x, int y) { iconX = x * 16; iconY = y * 16; @@ -179,6 +186,11 @@ public class AllIcons implements IScreenRenderable { vertex(peek, builder, j, k, rgb, vec4, u1, v2); } + @OnlyIn(Dist.CLIENT) + public DelegatedStencilElement asStencil() { + return new DelegatedStencilElement().withStencilRenderer((ms, w, h) -> this.draw(ms, 0, 0)).withBounds(16, 16); + } + @OnlyIn(Dist.CLIENT) private void vertex(Entry peek, IVertexBuilder builder, int j, int k, Vector3d rgb, Vector3d vec, float u, float v) { builder.vertex(peek.getModel(), (float) vec.x, (float) vec.y, (float) vec.z) diff --git a/src/main/java/com/simibubi/create/foundation/gui/ConfirmationScreen.java b/src/main/java/com/simibubi/create/foundation/gui/ConfirmationScreen.java new file mode 100644 index 000000000..807dc91a0 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/gui/ConfirmationScreen.java @@ -0,0 +1,186 @@ +package com.simibubi.create.foundation.gui; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import javax.annotation.Nonnull; + +import org.lwjgl.opengl.GL11; + +import com.mojang.blaze3d.matrix.MatrixStack; +import com.mojang.blaze3d.systems.RenderSystem; +import com.simibubi.create.foundation.ponder.PonderUI; +import com.simibubi.create.foundation.ponder.ui.PonderButton; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.util.text.ITextProperties; +import net.minecraft.util.text.Style; + +public class ConfirmationScreen extends AbstractSimiScreen { + + private Screen source; + private Consumer action = _success -> {}; + private List text = new ArrayList<>(); + private boolean centered = false; + private int x; + private int y; + private int textWidth; + private int textHeight; + + private PonderButton confirm; + private PonderButton cancel; + + /* + * Removes text lines from the back of the list + * */ + public ConfirmationScreen removeTextLines(int amount) { + if (amount > text.size()) + return clearText(); + + text.subList(text.size() - amount, text.size()).clear(); + return this; + } + + public ConfirmationScreen clearText() { + this.text.clear(); + return this; + } + + public ConfirmationScreen addText(ITextProperties text) { + this.text.add(text); + return this; + } + + public ConfirmationScreen withText(ITextProperties text) { + return clearText().addText(text); + } + + public ConfirmationScreen at(int x, int y) { + this.x = Math.max(x, 0); + this.y = Math.max(y, 0); + this.centered = false; + return this; + } + + public ConfirmationScreen centered() { + this.centered = true; + return this; + } + + public ConfirmationScreen withAction(Consumer action) { + this.action = action; + return this; + } + + public void open(@Nonnull Screen source) { + this.source = source; + Minecraft client = source.getMinecraft(); + this.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight()); + this.client.currentScreen = this; + } + + @Override + protected void init() { + widgets.clear(); + + ArrayList copy = new ArrayList<>(text); + text.clear(); + copy.forEach(t -> text.addAll(client.fontRenderer.getTextHandler().wrapLines(t, 300, Style.EMPTY))); + + textHeight = text.size() * (client.fontRenderer.FONT_HEIGHT + 1) + 4; + textWidth = 300; + + if (x + textWidth > width) { + x = width - textWidth; + } + + if (y + textHeight + 30 > height) { + y = height - textHeight - 30; + } + + if (centered) { + x = width/2 - textWidth/2 - 2; + y = height/2 - textHeight/2 - 16; + } + + TextStencilElement confirmText = new TextStencilElement(client.fontRenderer, "Confirm").centered(true, true); + confirm = new PonderButton(x + 4, y + textHeight + 2, (_$, _$$) -> accept(true), textWidth/2 - 10, 20) + .showingUnscaled(confirmText); + confirm.fade(1); + + TextStencilElement cancelText = new TextStencilElement(client.fontRenderer, "Cancel").centered(true, true); + cancel = new PonderButton(x + textWidth/2 + 6, y + textHeight + 2, (_$, _$$) -> accept(false), textWidth/2 - 10, 20) + .showingUnscaled(cancelText); + cancel.fade(1); + + widgets.add(confirm); + widgets.add(cancel); + + } + + @Override + public void onClose() { + accept(false); + } + + private void accept(boolean success) { + client.currentScreen = source; + action.accept(success); + } + + @Override + protected void renderWindow(MatrixStack ms, int mouseX, int mouseY, float partialTicks) { + + PonderUI.renderBox(ms, x, y, textWidth, textHeight, false); + int offset = client.fontRenderer.FONT_HEIGHT + 1; + int lineY = y - offset; + + ms.push(); + ms.translate(0, 0, 200); + + for (ITextProperties line : text) { + lineY = lineY + offset; + + if (line == null) + continue; + + client.fontRenderer.draw(ms, line.getString(), x, lineY, 0xeaeaea); + } + + ms.pop(); + + } + + @Override + protected void renderWindowBackground(MatrixStack ms, int mouseX, int mouseY, float partialTicks) { + + UIRenderHelper.framebuffer.framebufferClear(Minecraft.IS_RUNNING_ON_MAC); + UIRenderHelper.prepFramebufferSize(); + + ms.push(); + //ms.translate(0, 0, -50); + //ms.scale(1, 1, 0.01f); + UIRenderHelper.framebuffer.bindFramebuffer(true); + source.render(ms, mouseX, mouseY, partialTicks); + UIRenderHelper.framebuffer.unbindFramebuffer(); + Minecraft.getInstance().getFramebuffer().bindFramebuffer(true); + ms.pop(); + + //RenderSystem.disableAlphaTest(); + RenderSystem.disableBlend(); + UIRenderHelper.drawFramebuffer(1); + RenderSystem.enableBlend(); + RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + this.fillGradient(ms, 0, 0, this.width, this.height, 0x70101010, 0x80101010); + //RenderSystem.enableAlphaTest(); + } + + @Override + public void resize(@Nonnull Minecraft client, int width, int height) { + super.resize(client, width, height); + source.resize(client, width, height); + } +} diff --git a/src/main/java/com/simibubi/create/foundation/gui/DelegatedStencilElement.java b/src/main/java/com/simibubi/create/foundation/gui/DelegatedStencilElement.java index 9feb61f84..2d0397065 100644 --- a/src/main/java/com/simibubi/create/foundation/gui/DelegatedStencilElement.java +++ b/src/main/java/com/simibubi/create/foundation/gui/DelegatedStencilElement.java @@ -7,7 +7,6 @@ public class DelegatedStencilElement extends StencilElement { protected static final ElementRenderer EMPTY_RENDERER = (ms, width, height) -> {}; protected static final ElementRenderer DEFAULT_ELEMENT = (ms, width, height) -> UIRenderHelper.angledGradient(ms, 0, -3, 5, height+4, width+6, 0xff_10dd10, 0xff_1010dd); - protected ElementRenderer stencil; protected ElementRenderer element; diff --git a/src/main/java/com/simibubi/create/foundation/gui/ScreenOpener.java b/src/main/java/com/simibubi/create/foundation/gui/ScreenOpener.java index 9687042ab..cfe1aa1d1 100644 --- a/src/main/java/com/simibubi/create/foundation/gui/ScreenOpener.java +++ b/src/main/java/com/simibubi/create/foundation/gui/ScreenOpener.java @@ -65,7 +65,7 @@ public class ScreenOpener { if (screenHistory.isEmpty()) return false; Screen previouslyRenderedScreen = screenHistory.get(0); - if (!(previouslyRenderedScreen instanceof AbstractSimiScreen)) + if (!(previouslyRenderedScreen instanceof NavigatableSimiScreen)) return false; if (!screen.isEquivalentTo((NavigatableSimiScreen) previouslyRenderedScreen)) return false; diff --git a/src/main/java/com/simibubi/create/foundation/gui/UIRenderHelper.java b/src/main/java/com/simibubi/create/foundation/gui/UIRenderHelper.java index 28b5e77ac..e89d49797 100644 --- a/src/main/java/com/simibubi/create/foundation/gui/UIRenderHelper.java +++ b/src/main/java/com/simibubi/create/foundation/gui/UIRenderHelper.java @@ -4,6 +4,8 @@ import com.mojang.blaze3d.matrix.MatrixStack; import net.minecraft.util.math.vector.Matrix4f; import net.minecraft.util.math.vector.Vector3f; import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL30; +import org.lwjgl.opengl.KHRDebug; import com.mojang.blaze3d.systems.RenderSystem; import com.simibubi.create.foundation.utility.ColorHelper; @@ -22,8 +24,6 @@ public class UIRenderHelper { public static void enableStencil() { RenderSystem.recordRenderCall(() -> Minecraft.getInstance().getFramebuffer().enableStencil()); - if (framebuffer != null) - RenderSystem.recordRenderCall(() -> framebuffer.enableStencil()); } public static Framebuffer framebuffer; @@ -33,11 +33,13 @@ public class UIRenderHelper { MainWindow mainWindow = Minecraft.getInstance().getWindow(); framebuffer = new Framebuffer(mainWindow.getFramebufferWidth(), mainWindow.getFramebufferHeight(), true, Minecraft.IS_RUNNING_ON_MAC); framebuffer.setFramebufferColor(0, 0, 0, 0); + KHRDebug.glObjectLabel(GL30.GL_FRAMEBUFFER, framebuffer.framebufferObject, "UIBuffer"); + framebuffer.enableStencil(); // framebuffer.deleteFramebuffer(); }); } - public static void prepFramebufferSize() { + public static void prepFramebufferSize() {//TODO move this to a mixin MainWindow window = Minecraft.getInstance().getWindow(); if (framebuffer.framebufferWidth != window.getFramebufferWidth() || framebuffer.framebufferHeight != window.getFramebufferHeight()) { framebuffer.func_216491_a(window.getFramebufferWidth(), window.getFramebufferHeight(), Minecraft.IS_RUNNING_ON_MAC); @@ -54,10 +56,6 @@ public class UIRenderHelper { float ty = (float) framebuffer.framebufferHeight / (float) framebuffer.framebufferTextureHeight; RenderSystem.enableTexture(); - RenderSystem.enableBlend(); - RenderSystem.disableLighting(); - RenderSystem.disableAlphaTest(); - RenderSystem.defaultBlendFunc(); RenderSystem.enableDepthTest(); framebuffer.bindFramebufferTexture(); @@ -73,8 +71,6 @@ public class UIRenderHelper { tessellator.draw(); framebuffer.unbindFramebufferTexture(); - RenderSystem.disableBlend(); - RenderSystem.enableAlphaTest(); } //angle in degrees; 0° -> fading to the right diff --git a/src/main/java/com/simibubi/create/foundation/ponder/NavigatableSimiScreen.java b/src/main/java/com/simibubi/create/foundation/ponder/NavigatableSimiScreen.java index 124a4967e..73f354c44 100644 --- a/src/main/java/com/simibubi/create/foundation/ponder/NavigatableSimiScreen.java +++ b/src/main/java/com/simibubi/create/foundation/ponder/NavigatableSimiScreen.java @@ -1,6 +1,7 @@ package com.simibubi.create.foundation.ponder; import com.mojang.blaze3d.matrix.MatrixStack; +import com.mojang.blaze3d.systems.RenderSystem; import com.simibubi.create.foundation.gui.AbstractSimiScreen; import com.simibubi.create.foundation.gui.IScreenRenderable; import com.simibubi.create.foundation.gui.ScreenOpener; @@ -106,7 +107,7 @@ public abstract class NavigatableSimiScreen extends AbstractSimiScreen { @Override protected void renderWindowBackground(MatrixStack ms, int mouseX, int mouseY, float partialTicks) { - if (transition.getChaseTarget() == 0) { + if (transition.getChaseTarget() == 0 || transition.settled()) { renderBackground(ms); return; } @@ -125,7 +126,7 @@ public abstract class NavigatableSimiScreen extends AbstractSimiScreen { ms.push();// 2 ms.translate(0, 0, -1000); UIRenderHelper.framebuffer.bindFramebuffer(true); - lastScreen.render(ms, mouseX, mouseY, 10); + lastScreen.render(ms, mouseX, mouseY, partialTicks); ms.pop();// 2 // use the buffer texture @@ -147,7 +148,12 @@ public abstract class NavigatableSimiScreen extends AbstractSimiScreen { ms.translate(dpx, dpy, 0); ms.scale((float) scale, (float) scale, 1); ms.translate(-dpx, -dpy, 0); + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + RenderSystem.disableAlphaTest(); UIRenderHelper.drawFramebuffer(1f - Math.abs(transitionValue)); + RenderSystem.disableBlend(); + RenderSystem.enableAlphaTest(); ms.pop();// 1 } diff --git a/src/main/resources/assets/create/textures/gui/icons.png b/src/main/resources/assets/create/textures/gui/icons.png index 1dff79b367fb92985f19643b0fe3032bca2a9dae..74fe5caee4e8cf10a66eec7699e8e47d4e6346f4 100644 GIT binary patch literal 17866 zcmV)EK)}C=P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U>ntmgTsSZTrtwR1*k*cr^zPa*W%9+x$BxJTjFk zNmVK3xqnBY43F^e%}oMk%hob8*uLKX_y75>|N5{03QsNNdU9>0mvZyJr$0Ro{?P89 z|Ml~GxS#X;Z~qIwe*e4gU%&q`@-^`_{rp26zg{)?e*W7FetVBF|Ni?|`}Hre{kqU^ z@BHh6$#;(YVgK@Nd6< zciQ_88=Kg{e3rTKfa9J&mzX*H&9>6n=fuxtzW%sSxa$6O0hWmO&fE+J|Af1QltT%3 zjj@KnIyN?FaOW{Sak2v;=eW7a_@q?gRbz|3H?OJT{A|pzM86F-u@N~H{Hd`+gUx!L zjQ!N7VM8OQ{N$WVuDM;g=TTC5N-m|;BFK%J>Qi$qwboX99oXp8ax1O2)_NN~_5=pz zUV81V_dZ6v25&UD*5Ky{-`Bb}gQr^5^RByT*5~ zT7SDl5Kgjt#*W1&u;bMoz@VdhcDL|6(VcV8?hdl09J$Ht-dsDlV{GB`hM2$b``-O+ z=l;~bUAg_KeT)Cw&RuTZ|B0Qu*t&n*w|}y0+c>w*j{SI{YkDWVk6r%UwedA9lfQ;{ z>~gn0Y8fHEea8Jh?9AFb&*xrihS*EqtJe4CVZ-y*{`Otk7g(d8tK4rHoBR3BRYK`A z~e;g)LygYkQ|x@m9vXe!EJ!-#*&=x2x2&*B*O5@2{(HFR7I0 z=PIdlm3Z!jt7M_pC#vuJovZxzL}2L5W3To8dLr({tNc7sVkNoCopJ>P-Y|Ax`)iBO zd^ejE+43f4q};!+WLM`YAoiWxngH_}`I)H$=I?{i)%jK^P21l}`5N2=CSB_uV zmeyNhp6T~F@op4RZE z+P}4kLTL=F?*3S)#$U@K?6LG$SnS#p#_LWmSDK}nowb?;?$o%c&XT0NmuKT&v9H+g z1Tx?{X|dyMUHBSMOYC~t=a?HNjd|beW9Jv%iPfymYU8J|EHJ&kq4ltO4r>JOilydi zJbAfV#aUSViGwD-fwArZA_bP1Z{8!~oweUjWSyS&;uYc=8*X>Hx?iTj3K-Rw7(J;oL`V(zQgT|NRe&rq=E zS+ScvdC1&TJZ!HChdn+Wrzs$4nRA4v#4zWzbA51ujlE7b;sXrWth>Lf?_5@f<$b+0 zwlp^Y5bHF05ds0ag)MJjc(?Ujsq~S#wK_*%AX(U7p4|HL-T9r|ZyPM*m%=UyG7{IL zX+xHpA>Tju@xI(q=&iip!>(bVnSWR>9y3Lg{?@r`$rEdO`E5=%iLl>WYI3O0ZhKzT;tandvAtPr(LK$vZXSb+2|ELM3H79G^P!Tam!FZAf= zN`>(Aw6b2W^u3@#DQqs6dYUN6wG!8YfCW4?V7`r?WfevRA>aI94)^*i8(9QaSR`Fw06%ce!OCqM1%#xR$&>&#OFijCYrx^tf6Iop?qG0N6VeC`D`LQ zNYeAfl8M138KH&$`&3!e%`$;i9oG z1q)&S*1(7pmQ7szK1fMj6NF{kJ0xIkE%;^w%dvPu#|5Q$-}(Vm&?5C=2b5YrsVZ~P(RRHpy0}H!Gzx*6Rw&tH0J)wSzlO)p&J#( zJ_8BF*A`!10RRH8-$fW>VR)K%2MnLfJs>lF^n^a1XMM!i6j+Dw;$^Afi-g^uYY{In z0^k(DIqR%Pa2< zg0PEw6{tffkj_Pr<@>W8Obez7won1YXK4-Q$0x#VS~%dLI<1^ zK%NAc+b|I3-f~jnMCSGJ;+?@d3{2cE?YT4FSa#4NA1c~KZ4D|ZeUGs!ws-y3@TPa(gqcX zQt&1O4upir-#@$?FweZ=!XMakcC#O?VTe7RN37%J6lx@qpSup23;_aGkDyG#}n0>@i>>VlS z@)V3Q0ed|VWX5XdbTVYmOxT6i`r>&ie!;)P2cI*wnFvyqcmP#>gNnOVHQ)l>JE7pj zT;&=l6=6|VQJLVlMD)6qml1j7-_-;3E`yH7{*=;Ns0zTqD@?TuswW3ihGhye-@eusKglMu+# zstSe#^?3LmPWz@DRn5;yV2i|QA5n88H;TmN18X{W3!m+kXpNwc8I80z#1uRn#%?g+ z2H^?M_aOyfKV1D%h5|ed{Rv&m;Nl40#23*_xBqSKzos%PuF1_IiIJ=biPi52K>&2j z%v?MxmO75$Ss(t+ji4V{dfX2D5d&Fb9w6Xm5S-+nSt)+Nx&Xmu#5T+xPvpy70jUKj z2n=mz;yUD-m{D@Tta};(PJps_%*3$6u}Szlx_H6(5O&vOhX|wz9|e#fyc_yVs742m zi7z1BcE#WfT7^IqK_^J_DkYh!0-2yugwX3g_SCq8JfON*O$V?aI1VB$B0B*(@F2$L zzy!1?boBqDcz7Bv0+RFpn~=>L%>g)U)C_QrwZJ4W1*CTN&-lcudIMB20-vgb8@lNQC)FIRslyEaX~49_w^t&#aw)mCISn37-cUiMaf^WK<8tFaPh+uq!tZ9(T9uwBd@$SwxxFEjTjr3BW-+ z^}}{yI&VDD$Y23(?sr-25Ytx!IbGfHWVR9(iF(Cd!WGc%5F^O2e!fji|0~C5{ESPF3tQ^i~|X!PJEUfLoP4Wy53QOK|LPd>`C3fk?o(&LvQ(?|l-* zt>E-<(+FiDj>2x1#vCT(f5ugs+3NfV-8B*YACSL%aI}kX#$(KvFfR{lKqGfwI-)jq z950bH=8-V@IHC4xEaEX*n35xBeS_nP(m1yf#3|29thQ1xrg*nB^_u#qZ zBr=IJR!&iU-O?#~95@A5<2TtR;7iWpRw4OwfMy56U->SORBHgH0wIIj%GgO(0)AEB zaH;V629|7y6gz;+66SA(Uint|5Yb@uMQ~9ca|N8V;r6Tx-!X;Mx`7()Vh*#VpBzGl zaVMc8wK0d$8H<6uKx=p%ETt@Df{65D-FfDZFwKREd>{~x8+Ve3Wl5C{aIk-`Nlgt_ zwQvyxKpZ(MSQvJD0Y2>r{ElYj+h(zNAc!gfc)+Ehl>BR9L4Z=!GzI~NU7t$q&F_2i zE7&VG71+_rFYIiHj8B6c);njbeTleGh^H*t4X<3Fk5nCm%o7waHwyJ6j=}g|D5Kdq z^P~vY2WL@jBSHaJt}@sibOMY9-P}|Dsf+O144e}JLE3Gao$chl?lQrWdD0k&qa-_n zLJ%l!FZYFRV&bHgptj+Hka-;*n~;5&Wj)Q^^;k`^U+@4 zwPxM0D(?n>P6jnl8vFDO;O#ATR((#F_E|a`#1iyiLtK8gFrWfX^M2z+=r)^RF~>aaiJrx}*CT)qY#ai9 za@-gXn3aQUju`z}El@iHS{T;_Ql^dkJLHNb_e zL|z1q*E$ho_1%`NfS%Fc^u|x%A!ZTT+M*EPS#ZJd!9t-z;1LuJ8b8s`EL6hxE&dRvu_;0S zdSg_hBukR*q%|M3XxlsuUJPl3uHQWb#0v5b9SA1gtK_6qdojq^+Xtt<@FvjVbDP8S z4R&W5J0LJGu54}(iuaXyST`YeB2f3Kx!sa^z&wC8DO_eC8vJARQ(&1APh>2@5&ivS zObaGL7(f8bM;wSsM#` z!gPBuzx}?WT<~Ft*%$L`YH3@koaN+pQVAxACu^pD;-(RAM5O_;{P2+lF*@=Qoe1+s zzgn&+W{L^7usPz?;=(tp00FE06GIwMU;{w3iqPKKU-Sa{K*_^qLq?Y=3khZVOmVNF zwVCCBp-4}GJ%adpzhz+o29T|YA=yvPLGQ#M!(|0xK~M|VRfNuE$q@E668Id%ikI8D zQ2=&bppJHeeca&R3f8Z1YEUX%Y)ITk=7aqsn+8~kW$!FE;-gsugo|*`;2rwx2CyLd zSm%Zh%}r28ZhrZSY*dyWuo;Br7Gdx8t!T4Ez(zF7q5|~qtw`~hOm}0e;YYAegglHJ zD71SaRr^jpH3t&KG(@Fw7dD4o>5tMnRLF{LN5fsETxX=Op3C_R8k}ex@ z=PyEPaJ6jL!KwBx;w6jscgu9KPXF#Q!3$7u+q_q*CY0Ive6cxTNH7r!#UcX27VCyc z?g0>$sJJ<+Lj)%%_Jvw{0JUJ2$jS=;hJxQ0?15Y3esKKVYycbhcP^(!6)m$8zcJ0{ z#z-HOJ&LtkqZ0CoV~ZcfLdzvt!}yc(@;0Yu2a9Q@pkT$8anxnb)5Z+2N1~8%e|AIh zZ9~uoSRoMzIcQ&rAMcXw$zewb%LUiZkGbQhB@!JzRvAeJsUd^_1nAsiO?k6HeO?{A zz>#pVN!U?nAhAy3uD^Cu5G^60{yly!)bq!M`NO(ge`#UM6ow;HpyzkTg2)H++CoXB9 z(Zm(FrP_`YnZpWNZ-PQbfOnf>zduE7p5FGqnwv8v{pQh@4 z7(dit@L1b?Z>gu{1tXjdb&a!`RaxvBi%h@<^=cg8$*u1onNf7HOhCEGb&v`GSDMz) zpNUx3J$8lw!F^cwc6AFbqyA|9gwGivlem$kxW*3SNhvJ~DR>#Sa=*twUcq=W{!z25JG?m%&N;8H#d7JT`cpcwiE2&Y!z^*c!iHj{xN04?z6c6|hkWAg)=Z2u#EX&#I4P|T)I zARa-CP;@D(nayZBi$dUy7`GMmoKJNfK(_WFQoSa9gM8Qi%ja;flH&d5^x8qBU9t(m z;-@v9bl?Uef@OazrPHW`AMi|*5LV4;ROmF2`n?q${92X{2EzHgi9l!>4G;xsc<2Zp zur6ZF6O*#_nA;%4o8BC5Wl4(s15r*tk#H?6Yp!Jxa4D z!m^p7#v0*)GnX|!~iu@6W(+=O*PaRrlk+7ogfm)svg z>%pKNmU!VuX47GuP=Bxo(3_GQ*xMj2gDA_ZHSVW8urx%PlFB^4emD$G>@? zu450_g{k5f7rg*w2+(tHU;3F>M{fZYEc;_Y59c*MR6QGHLq9CFp^>5F0epZj5XX>h zpJ^X;5Kjt)wOe-~d6ow95$Y2Q30>qLanQcZr{foJGXRE=8q~GP*8AtOEx@LOdAdcy z+c76Qqq(oiz5hP<{{|yQ7Oo0$= z`*=w9*z*-e4slEA+0e8|ph@pzQ$)D3^&QmLf;1vWZ3?V;+}x7{ehU%jS2fj#@ZveP z2L*7yrJ>;rt3MAM5;EBAlSlR+Sy=E#7=F{2;noD;(v%7~d$(n`R|GoOn-Lz{9^lqi zqH0?;ktN737Ezh!JPFjXSTUr&HBxgj!>R}I*8~?BFslWcWb6V55hL-^m%c8wP^Rqa zVLu202oKXXPlIvA%pR4W{@!k9ldTyu*dSqnnZVazBkwCe!jABQ7h?Tvpc#lcM-Ni` zZ1vehH=z}v!1kW88w&>8(z0uPKTzem3q}GMVL8bLy(tfw(2MYTPA3;rG#K2>@u>X7 zGp*>ch7ZzzI&oQv1c~?-bd48TKzg%~+fR)ZOSjaITtp*UbOVydfiknNns52oE{o`2 z`w9q&GD+3`%i>9(&)C4~7`UGTl`hKffbg3Yw}6^pl#<~3<{PYT7Q&-zgoUC{GZ29l zLLCWk_2EzXKCRi&dfa2G)%=vyCdh5d6U_(LyNfJ)Th0)RAtJYp9T`VXR!r*&E5V{< zw=-0#xx5=WB@*x0@g{HSd%TQ%m?v%8-^)XFC!|2{-v6UONi`4=u@o-e-W-=jWQq9J$QXt~x z1=iS8(2p25yFHKCG9n7{%AGW$lg*;nO<1#Yel$H4g*=4CfGeRuLN?sJCS$8iBLeh2 zSVrX`J?a$OIgro?TBIdjwfble@3Lxz-N!yo!nL5?^20h?T>W|hc7jmSAU>#QG%0E) z^F}Poh2FDAO|g?bFoFkhKP>d}?-n>%jb=NjG@#g81U65I7lhmdSoSOgYXm_x?VtH_ znybE&5ed!Av&;@j0A6v4VB-n~WGC#FI-AubhE_Xz38UACpaoyb=~)6Mx`}Pz!5-Su zpxE#Sgz_|OeD(-ppX}ZQCZWI6Hmk~3rfag!)}?Imdo9Kc8{wgS#I>jh=2j3krl9*; zgH6Wpyy26IMGQ5Yz}|7~>iG@;J+lYHESpI>0yi`<+FaGqMKIi_;IC|hB_Y-X*oTJ` z_{U4onJ|A~3`+z&vPlm!7n23IrJs1^IWdp`3JN*oim%R{asKA zHp_lTC?UTOVjp*|SEp!GAHER?%lS^LFW?;}*dE*wdiZ>&0_ZX$MXTqgo}YD2%Y@7* z@9R1Xnd>wmz)8+JcVaf-D6!5JODDM5*xSXHYap>PMA;1C)Z5`ek2J5^oNQny#HLz;H7N)}L5O&W(!}mdO6yoxVU>HG%C0@Qh|!!Pqk9-fi^e75xiXatJz*t)&O$Pv(P9_oOHS{H?_NtXC#vfzteAS{Nv zfzNEejt18+o|kEvxC0gFp3Napp!+-;Up%(|+E@HfEa=C8uD@KHQSed^q%=On+=5Gsg{lm&EPwIGfp^asubicO+&T9W$j`1Ad`|Jw~gK(1x zz=gOALYlfrh}JU}L^XY<20t*XYK0a4P1dDikQKRJ;<5?+ z!_v&0ghyuRBDHE$u|ns6`I@p-(XHekC8x%7aN!Erz+;$7J?%6GS6)lX&kWnAp z!s*~=36`bJh8+P1aG7dB-M%Np#%aRk4!;-uiK?&M*@sPxWS)D-q!BV>uZp@qa>M5Z z{0njrC3l<8cv?na6-n|PJqildH(>;hqC55}DcBKrTC3sFNDj}ap<9V-x9r&B$@6@~ zMw1Zr2u=h!T$ebQzYfmLJ0JmJv7isC^UVIh^1x7wxYqzsbL76JI$;ugYxq1*iKaHw zYsDWy&BogYU7oaU4r`;GApzY}(=`(tF9g;~Uzv#XjZoTlpoqGNmTw@8Q4$^vus`#y zQSs>CY?Xaa$@a7Sn%;IHj1a>F{c{jI!BGW0^Fqz$WRC+l*n^nznYe}}07~2JVMDH< zFbhivA-}4(GHi)w*E{-@9Zu)a1L4cS&C-@-N^V28n~}}4ZPQMtlO+As+;RPf-5sA* zMD#fVjVQ9e_u~1i$f`~(r+ehs{@0Gk{*Z2_5UfF$Mg;2cF18>n?ojdLd0^~?wPpE< zV$&=+C}QxjlyyJj0z3dPID@Ag2tv26OGHj)bkoPagSgG~V)cymM|k!?Y0;wzcr2$U z1!BH{lx9x|)WVXw<)G6s=@FZoKxi*wIW3L(bUd&gyG{F$y14}wCf$g+ba~hd+qcc3 z?}mdl3GIGMx3c3=Ga`;9xCue`ubDL(%5Y)@I8i)@p#-hsFS=RxpvQ-=F;ky=8>I4Nr zpLe}4nz&(%s5730W%WeQZr2lD%ok#ePrYs0$coyBMrUlB;PaKZ1IpG;>*g2)ThUGr zIkx3-k$R?_$VTAfw-ugLC3$w8J12Y__Qu<_2a(K+ZGv?obvm z3QFCyn{NYg<89xg^naeKIiSPi)cj__vd<1N^2j384i_b8v(C2}j4rtd=kx$L+4zIqbRpG$mN`B#I2E z@CSZ9NWX1O<~@fdVZ!BZ;t*6nc*ZfFe4VfroIbOOH$;NDY;GFRBue}&kLX{&M(J!bQdt~NoQuADho55_Lhi49kr`d!6bMiczDSu(} zwk-(Xw&`w}t%g2(;NEjVFA{>w&)rRH@MtX)CkWHgR=vmY6N5u;WLm5lnxM$8eHO4!W{ z;rrcI51=mnwe8Txj8&RbG%OJFP>~nfl9ud<4XwSy?h6A;dyQgg-SrBvPT1fqXhPK4 z^tdN+1in|XWxI(X2KX$34hlGeM`uD#*(~h=WQimK{d(s)n1`$$7Qu=$6k7AJ?PGVh zQy_>^%T86t)!gB9Vy^VxY2fu|c_I`Rj@44|2)Bi1hYi?9njJ&)pt6sevD3|SlPKS^ z*y5k+?3*kr1P^uG82;Ca!)?q3KDM)11cmI_W4p?QK(H=l(cXDP;S>UOS4^{@gCUCd ze9jSyeJWrOkfdNGuzV@iu^a%^VgA9<8utmGB0w#W*Vbpw z-=jR5(>1E=679?Ra2X8vCP+K7VBT)c zH^JWtyE6iyLDoy4z#dsajQ@LK1H~y&R{?&H+1f<=d6dJUHBKesCEN@WViBls;2y>U z0bcu?=aBvlfyZ08Ls;6levQOvrVXBm_(z1Nbo#*I&4P*#QI@BW+Wq3pATM#HcMuk_ zwu9$xzi@@b%G}OBL5j7Gw3+j@T?W26S3Lo-d`G46#}4iE{aI3&i|v6;nuPaWN9{>Y zUW|a~S7Wrgu+IrM*1esBf44_Lx}B@=&+u-~1z38w(3(27j;0OgIB2%_c&XRc8%t@J zzULPRj26-sL=3EkYvH=DZONFr2Xqr&#yvEfE3m@=ry+no8R-ebwZ-5Ll{g!?0?B85oZPIA8k9^3-QHVOUD|skd~9M)&ZUL(g8wAy9J6w3({Thv4fd#EAH-LD&3k zgRGu$1_X~RSINbe!`o2?z&A4H)eY_cAOtkk{stB{WQFG{_L5zMi(w-M3J>Z5-2eu8 z)J>>tYIrAcoU>2XDgTEfj@O?#1VI6_IIel*zC~pBs{bMNlzqw5a93xtC`jQFMHK1PjEr(KoyYI>j3nV!5A2>t`gyD428x47% zlYlWnaNbThnqae2*v2{g0=ckxr=P@Ta7hXEHk|=L04+`zY!l+5 z0j@u{0)Mvv&uPwZs@ypzCX&r)q#aIi+Gf<*IlTEPFHVnT+G6~5+mcj%@C)UB)=3)o z*gTL7qhd_~mg{jA6;JfyF!x5z;X4ccM%Zl*V6t@`$VPjXJFVgM|QkO^;jY({>=UCYqgqgve3vk2Bb&oEdfXsvbt%K+sG=kYfqc zE~<^?cn~Yp-7K1NX)4XC=e5qMmilw0ZbARK%+7UBOD(%;0DF`lS8|*TyTBFgAG5*j z=C&QSVHI@RHUZIjl2ZZ9zY_aDm{cqtJXuhuo9z$7jsv{PqOUTN?OdTx&)+(*ExSlI z-ZqmnPKCN#1G9TNgBQ$CSgoiw-U86U2`|q%DM~E+h_Oxtgw}aBDcMTl89Qe?-3~sw zY<#I6nfp9#WM%iwx19uum&55nb}QF20;CuZXw6pdBBI5q^K-sCep4w zRA(hxEFb2mk-C$ReWu4a$cC*w=ZbGM4N=*lH;+=X)XW|>rxtlg7D|w@GNO&m#;IuU zPXr-7Itv7{2zac7QAcRwRAA4UpHo0(wLRf%#i%|{mlieCqbN(1P>oI*UZ$6#4UFqF z^uC?DaO}nEXWYZU>7qT|-29hHXmH~O6KmDnD>jTyB4pb|<}rw30}izQwuLaOvOO;1 z`5`;yzT)u%zD!RD9?XNzg-s0D!hX1%ZZL;~D(2iKw=A8l2OS~O76M?`zxS+w<>{q8 z=X#t?gvyn)nb`bK@#vp?{pT057azM5XFX}^$XZCjFhHG-hu3Yu$lwijI}JSX$`JhI zaLxq>M`j~-!i=_ENI#}!vRuMF$klFT3>GA|wc#Dy_-MI~Wrn3^NP~5gl5p zy(tg4%53jN2%HG|d^@znmf7zN%5~Cd9lbuz`NVme1-JK%TE3ep{D7nhDMZYu+GTh- zSE>tFXs9rCD(KmR5qV6X&?mRBQPbHxka@5iXITUy=M8Q^DcYDC($&T}828h%`J92H zC|(_#6^`d56UyebVRK~zN^(NCwmy{!_Lq9i`^xk1f)BKU{NNvizGuQI9vvAW$8OuZ zZu2~m;enN)Sv(;ZJe@&!yFE_f_;k2S0*`;=(|g_K{I@p zy1JcVvejlq=gk$L0DJV5_2>Ga+lCx&4?%KaNM3K>2p<6iR($b-)*HQmeyP6KiG zw26GuV_?Bku%0-4?60(q%?UOUnHG`j+i4KVR7ik2;hNl$(-0nKWfcpVgNKt5Ijjc8 zaL;q_Bs#6yK1wK8a*fV3p5p0T9?%p#8e`-$BO&4!Yg*-p#IcU1XNWDZJA|(X;)vY# z@pIAZoUk0fPFUXgKmL0Ck3SGLA`O_itIfd>jWbC)nvio|tc#94bfojXI-**M+d#D= zvH*2hdx*|z1PMQ@9PTN+fK?u+KIlxO1=Gf+ac)T39WJX{CXj9?&>kB6*sj~Y+tJ8G zr1~sq!AFoNj*oPzWVehg!nF(&+^2K&Z#hXV}vn|D&(hfBHhZ3et)WYnmH5brnacVtV#f*p-Q| zVJ45=XJEF?wad|4rFzrloI2$q=onYf8H3ZoLhrgxKH9TIGMb)Gwq^H*0jBEPlG$@kf{W7>SN_SBKz&lp27xrd z@Go{C7~8qtq&P@ZgMlFQQ%GFr$lQGfHoOgGWqIK-*&+@hX$YcbusTb-D$#4?E1$9~ zHvUzo?XkL!SHyIkzQ((1#?}|0IjC!sHq-p0X+O*^ZR>0|Li-AqmK=Y|s!TjW<6@=E zESvI~der?P1{zxHbs%SQt|X7gPhrgxBB1Go9_L3oHLP&YS!4U@OHMbQLsTf|XaYC) zIgo7|mZO~}R?1EiDyof2Mv6v9B-&NKPVS@~jmg!%GkoTZ`?mCWx5uL(9#Nm zDQ;j2{q=l5;KDm$&sMtq>;W-?bp>(K@DmEyu`z#y7$mRfc+WG-@(Y z1091(%<@wlB0B5soTdehWCX=1>OhZEJiEIe%VG7{6Ee4>q}J6kiZOmpz+26X3`k_^ z!!f%s2ZtaI!&!G+u)Q*Ncg(LJUX(5IfFs{qE-1!*rq~ii62RaHv6J2NP(%VY?4QXS z24h4F+bJ=K8kQ|N6Tz`T4tY20=QQx5R(BZZ&I(SvVR&pGTqBTrPRbb+B{`c0@ZyOa zGo!%1?W%KH-Gw7qKHzr#((8|GCDmb$(>#w7Pd1Dp>a@P$AKq-rH zo#B8m{#l!Z3mA5;7@Jb;is4z%iDIphuqEOUc8>bz0$kSFTYf|k8+2KKd}RB$lNFu! z<)*A@16DjtRJd1<>N-X=J65A()Cj)Mi~>&H)!yBy=Y`#?cBobC#qMWEC!aZhyaEo8 z6@5;v!1M-w^EfFynf^g9W+%WoeC^_2JbzPdriR!ikF~?;lr^X+{!ozK<6h_dv*Qlm zim3)%$C;<96bA_*vmOs|Iq}RPfZ4_hM;+SXZ2vYC<#YO?jm;k31JdVfv$De_CFl#W z``diTzr-EP9@YigUuRZw-sedVx$+~}8MrbwzdvEEeqI2@_Ext6y0CE)$6;IGvXRJ%o+rp?24h^Xiz3genz0V1 zmNaB}7V`5`BVuu$kH=De2JyJ9&N_OEf#E*I&^rC>jD$0_*({$Bs#~)xw)S zE`zqMuQRxtCyd~&LCpS=>6z06cp@k6?%-mF2Byz~qc!)p!FImfm#MXLcI;X9j>Z^F z^@q5>E7>#}Pi5L}MxeHMvdxwWjO6wp?`QJ|G{xq5q`s#=39fJP38QnrJ(UHAlnQ~f zY!tNYbLzH}3jHJt)QO)U<$%DPgdnHwZ1fTW$3b8Bbg0E?N_#Kd$l-VM_#f7MSPN$z-h zP~K6)XsD@jUv`#M=N))V>~Sd41_} zOcH)z70kuLm@LosGa=tOjAz`d@qa&Fv0D%@!aY`cfY)u`NL=A^+_?A(KOW7`$beA# zsV4{`{CRpFPomVrA&naWWw+G$9@E*6Qz$JF_wTYh2s7y?k?Co}$l57TP%K4)UksR! z318yrp{2&Bl^i-%?QgLAY?RBvt1RNOKes>T+t)+}1#@neK@ii`k;>6AOu~X6>Vl`B zKpgq-ncLei=<9WUp7GSV{f>H& z&|;8dojk0^45Q~dF-;_&pMfAnlCd=T((P#_qOE6`!8W^LJQnJ~@9Wr|y$bL#JClji z=EP#@c9oiZLM-Lm^pib4h{4pIM?NIHAGE7ERIlZ0Oti^qLokroT9_R87yngANXHPvy3f`~@5Fr**nBFYtda^nWk(|Hp;C&)$#! z4|KQgU05uCp#T5@g=s@WP)S2WAaHVTW@&6?004NLeUUv#!$2IxU(-rOsv;H?QOHo8 zEQpFYN)?M>p|llRbuhW~3z{?}DK3tJYr(;f#j1mgv#t)Vf*|+-;^yY0=prTlFDbN$ z_29T4@9sVB-T^|b#8lHa0jQc~B%%>9ommzAukc|2J?O)T#7sSwTuhlq->gs)ceU_G%OiWC%v9V50PJn=b0001KoH)n; z0004WQchCiKa?JEDZ{eBYn^Gs5uB$e*>y6bbf?E100jZun3 zlph10NpMRLpC|Xz{v3!16P^;*1wrk+z24n`0G+_V5s1Hl+2yP;dmW`5z2Jp|;bXo4 z%YXI)SpJ7pZ~(x9V?mg?>8tOT$e6I)=x_K&}gTeRTh&jp~gNzQ7^Cn?~H=+PKWc*0gVOkD5#1jA{;}49?Xw(>)mgN9| z=mYnSfijLEBi1Hyx5;N5qK3b%41fpaCDLS3&FXV#YG5@09xxLgftXIbVQ&CRpE5!2 zz@t%wfIH*e#SIdvyS(_f8{J@I6kZ zHn-M~IlM5E1e43<+(hf|&o%(80Z4xLD^wzpeC~&`#hhZ|#g&tTUtQU>8q8;xu<{DE z(5Xm9Z4S!WYw>kcwmfr&U|wE&0GG4VfVsH#pM>PMk<%qDEjZ;GfIZx~vMr{X1}N9SninnM>KXcI3Zl*I z2hi5Q>4nhMC^!y4VSI=Btl5`|2284fV*cH3K29`{23$&?P*#56_^AR|W|Ji+v~}C% zujQ@dg7>L`WB^P7z;pu;6Onao(K=~#nYArSJKj3Z`6&P}N8f z9Xe$c*kQwk{sMqML)I-*!A;5&09_`h#9cNQEGgyrd0L%+^bB0Nal)ynbaV%Bs*6y1 z5`@PW)V2k%%GL5kXxhx$F)t8CJRU$e#%>c2WffTrMdln%!2>xcTpx07dNK_S(1sIHEZ4W{9dO3UvH@{0041e<&JZc9cpccz z`}6>OJYCSz#JK=eo+%k#grqTD5W8aRD4=Ik2i~IrB_*Zv&bK@qZLIH}lB09g!72b` z7yXbz+yD)fk9J|BLbF|)0{}iQc4iMW%P}F@>S2vDv^QN2aJwmR;3%-RCjbzir3RFg z&QG6@25|G^Ph$bx{`TkC9{A<&SAPxdfp31lxf>||i`(x$y!w8i3Vwd``sVf5zm8MD zyMO<M+Eo0c?Y z?yP{x@tGLOoIe>wyjHt@6uhqjY|AJ4hXu)dh}=d-3hx2voHk#s)`;oC*C)c;b?gU| zMc@!3^<7Ju!#26l*OQEm*#RVL8`Hdeyz>D1j?|!WoMk7&>|yM7sclTNmv7&!)OQ$& z+-YKCo4g#h^~wF%e|Gt}?Wh6=U^m;JAUTLLyIm%4S`PaHFr|Qlh<8Tmsk!6P>r=9m zDSNt}`n%n>0w8^ySHcpE?}!MR9q;q}@cs+$`FzgjI@dY(59j`z>$>mr2y1SH;1uHo003cPtZxYb zAae-<><}h+mf2i6VE&dy7@(p{e2z(2-E_@#0iY^@YtI?VnqU6&zXLG%G}6vI;qW!S z=Fbc$KA514)pQ~Na8{b=>s|?RTzs)$D^tg9fE26G6CM3?*C_3TUD85QRFq(%wY(gT zeD6nQ{_}+O$vyh+<4s=k!Nt)hK@B}kVRR^$z>OpbPaEAxT)2508}oh@3(McDX~ zK586lXy$&{8--@SL^{`Rc~tD#JTqsz@pGmz54x@=nmsyvzZ9i+qldc zyJ2{5BQE1v%yAJB)aL@H2T@)@65b79QUeRhX}sBzaBC_#`(9Xdy*I&5JWePYLEPoA zZMlIr>{B)$vz@cemou|G`g7E<>Cn`Tq{|(VTe$CCApBvnJvBZ@vNj&%-@iW~S9S^` z5c+||!F0ZN#P6I~sV7n@ourRZvK<%u%6&Wx>c!FS;1V3pL)!keG5G6;eWXjGVcgU{ zIV-!Ki^tw+K^6j&U}?o%lml2%%~8JKZfoPbd53}zRU^|X9@a`9H)iyO#J;>u9DEc{&8)o-Wl z!VM<*=U`LA0dxePyQ~{7!u0NX$+yy{{qa`08ndxw&@@h@ZINAd>2M5-3xS&}FkvG- zHq6;fuwCshOhe??3XNH>B500^o$b+%i%(GJPUXddBTn92HN0+Osx=D1&e4-VxPRqkVu!P8_{!MTH`e^0h?Dt05 z;2pa0LQOH6k852|E?)SlDLEroYi1~$#ENG>`M1tzw(C+>GHzQlMbf|HC%I{e9pTJ# z-DA`kG2hU&JLaR~C4ZNHs9^+(`q~tNv97^h5ZWOfE!Y(`h)oP%OQiPPtCohv-KQO7 z21k3-RaW+h;YhEf}yIXQ#fQbY;MWnF<7m+4RQVUS*@|u zO1WS8o@)1YN?E!;-ISW#!!3$>PQeiA9E2Wi=OIqdRm-XK-ieLtCw}_i&zJ9Q>}3jk z7Yu)Pv|xLxbJ^|hPk87NrIN&Lk$3QWKXVlxEa~M(WpTXbxaR*wCEf>1Z)Nj(U(`KQ zBfrn-9gwIL>kJ7}an^M6 zsOTwmZmsE;BdX}>xF6k|-1|7X>_#fh-7(Eb7T!^WoxTAy<)y2fDOj^`JXYcjKZmoS zx2FpPWSv4QW!QZYjEVXs1eY?7mROq0d=)<^kx9Wk`|CY?7Ujn^l%jvuyL>U<@0()B z796XP^_nNvs|u%Fdh7>*VhZ8y^b>$=W%C+^L@>!IBgx|Z&qdUa?iZ&q*7)-651T0( zErgWe?M8BRMX?&L#D2DZV65L723r}5D=o}RE(4c)9i|MHbAK8*Zg3Ad*w>f6i4)B| zWao6VAv2>F&Py()SvRe4Z5DPf`pl!=-X_F^HHp{`zbve2wp&20UmR#Ph#ZX$|9s+o zc5a>3tfIonlZ{VF${QTxDXz|}X}Xtt(r#1>g(>d|5f3Z1h2;mSS>=&$PUc#zoc72n z*}QWzH%Mzo0Npu1kJ}GW7bk*nwkv{-g>OAnfJkq!M(B17${FqOZUln+1 zX6(^R2VB)M+sIQDcZHC{;e*wAk1xZD$>HILUcGkNSn6H$WG&t0I|GIP8_64G^V6h*7$_W$TNgR@@k%vBezw-H#`IpU(QKBu@H2~WL9WK z5C9>?*!Z!VtBqeuhe@TniV=cN8U@GTMMZoWdclDh`!ywt{Wwux?x?Zh^46wyQDaGM zFAHz)?`+i6(DB8AbH#syo0h9oyG{`U(6I zXnP=4(9riGLblHbQqAmef!0|d-a5(fTgN-a)Z*(dIiDonRCAZ_(hxn#J?onh0WZM4 zCCex-WhrrWk7JIxJatG0s)u(%aU=zyx0Y#C{4OuV)y3=bwI?s7{w&3ZvftCM<1dIX z3EN#&10J*0LfjAq%MDa`PZ0k^We3I`DREyMLXy~gT?syP79-fCx&t?^wE`hW7PxNr zpW$Oo+)Q9*Zk?E`~lHi#<}!NWVXUT0z5lE^R=~dw5zhz(r1B{bTz! zq!pncIBA6=2>wg{pYBk7;H4w5DJReTO;=0h(pay0#11*j8NDmBH7QydDY28M1JYTp ztxw1#_y&B@2DxCY#Sx}tdYfN|HCjYGSQfG&KbnwyCb4NHVHJ@2i*OGrn^AZrLP&C1 zy7r-DfYP|MBxGIS-`gM5$I}o;-cAtW==f)E|F!$+>z{^mw$sApB(M<2(NP^oCns(F zJ`6l4?`=~P=_3O*3m{jv7rPg7c>E?d%d;UapT@#{_Sl1_+t6BUVZFbd7@?uz@;urM zeDWUaeIjJ}@J8tlJJAi$KwA(pW)49H9}Gk|h3mql*hV?S_MvCS-_EaQf_D$ZQ-G}e zEkA0sv%9SPjj&?mZ5Hah$E9v%&UjrLCnuEW^eTzN{*v(~U5Q@x}Uma)K`nf3n{JQWrxkF9N>92d; z?YgzDUs3fHaT|8!zp6SA+re}FYqEdT_y4z1Pz&bd{DJsR+w{#&N(VDPw1A0$xqb!a Hdc=PKERk3x