From d590b23aa731f919d06eb49bf9368ed62d0017c2 Mon Sep 17 00:00:00 2001 From: zelophed Date: Thu, 4 Mar 2021 12:53:24 +0100 Subject: [PATCH] tagging along - added tags and chapters to the ponder registry - slightly changed how scenes are registered - added a back stack to the screen opener and some animations to go along with it - added a interface for icons drawn into screens --- .../com/simibubi/create/CreateClient.java | 15 +- .../simibubi/create/events/ClientEvents.java | 10 +- .../foundation/command/AllCommands.java | 1 + .../command/ConfigureConfigPacket.java | 7 + .../foundation/command/PonderCommand.java | 25 ++ .../foundation/gui/AbstractSimiScreen.java | 144 ++++++++++- .../create/foundation/gui/AllGuiTextures.java | 12 +- .../create/foundation/gui/AllIcons.java | 11 +- .../foundation/gui/IScreenRenderable.java | 18 ++ .../create/foundation/gui/ScreenOpener.java | 76 ++++-- .../create/foundation/gui/UIRenderHelper.java | 174 +++++++++++++ .../foundation/ponder/PonderRegistry.java | 123 +++++++--- .../create/foundation/ponder/PonderScene.java | 41 +--- .../ponder/PonderStoryBoardEntry.java | 26 +- .../ponder/PonderTooltipHandler.java | 16 +- .../create/foundation/ponder/PonderUI.java | 154 ++++++++++-- .../ponder/content/DebugScenes.java | 17 +- .../ponder/content/PonderChapter.java | 52 ++++ .../ponder/content/PonderChapterRegistry.java | 49 ++++ .../ponder/content/PonderIndex.java | 33 ++- .../ponder/content/PonderIndexScreen.java | 170 +++++++++++++ .../foundation/ponder/content/PonderTag.java | 88 +++++++ .../ponder/content/PonderTagRegistry.java | 94 +++++++ .../ponder/content/PonderTagScreen.java | 231 ++++++++++++++++++ .../foundation/ponder/ui/ChapterLabel.java | 39 +++ .../foundation/ponder/ui/LayoutHelper.java | 126 ++++++++++ .../foundation/ponder/ui/PonderButton.java | 34 ++- .../foundation/utility/LerpedFloat.java | 4 + .../ponder/chapter/basic_kinetics.png | Bin 0 -> 7389 bytes .../textures/ponder/tag/item_transfer.png | Bin 0 -> 7389 bytes 30 files changed, 1619 insertions(+), 171 deletions(-) create mode 100644 src/main/java/com/simibubi/create/foundation/command/PonderCommand.java create mode 100644 src/main/java/com/simibubi/create/foundation/gui/IScreenRenderable.java create mode 100644 src/main/java/com/simibubi/create/foundation/gui/UIRenderHelper.java create mode 100644 src/main/java/com/simibubi/create/foundation/ponder/content/PonderChapter.java create mode 100644 src/main/java/com/simibubi/create/foundation/ponder/content/PonderChapterRegistry.java create mode 100644 src/main/java/com/simibubi/create/foundation/ponder/content/PonderIndexScreen.java create mode 100644 src/main/java/com/simibubi/create/foundation/ponder/content/PonderTag.java create mode 100644 src/main/java/com/simibubi/create/foundation/ponder/content/PonderTagRegistry.java create mode 100644 src/main/java/com/simibubi/create/foundation/ponder/content/PonderTagScreen.java create mode 100644 src/main/java/com/simibubi/create/foundation/ponder/ui/ChapterLabel.java create mode 100644 src/main/java/com/simibubi/create/foundation/ponder/ui/LayoutHelper.java create mode 100644 src/main/resources/assets/create/textures/ponder/chapter/basic_kinetics.png create mode 100644 src/main/resources/assets/create/textures/ponder/tag/item_transfer.png diff --git a/src/main/java/com/simibubi/create/CreateClient.java b/src/main/java/com/simibubi/create/CreateClient.java index 482cf089b..c411a9e26 100644 --- a/src/main/java/com/simibubi/create/CreateClient.java +++ b/src/main/java/com/simibubi/create/CreateClient.java @@ -1,10 +1,5 @@ package com.simibubi.create; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.function.Function; - import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer; import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher; import com.simibubi.create.content.contraptions.relays.encased.CasingConnectivity; @@ -14,6 +9,7 @@ import com.simibubi.create.content.schematics.client.SchematicHandler; import com.simibubi.create.foundation.ResourceReloadHandler; import com.simibubi.create.foundation.block.render.CustomBlockModels; import com.simibubi.create.foundation.block.render.SpriteShifter; +import com.simibubi.create.foundation.gui.UIRenderHelper; import com.simibubi.create.foundation.item.CustomItemModels; import com.simibubi.create.foundation.item.CustomRenderedItems; import com.simibubi.create.foundation.ponder.content.PonderIndex; @@ -24,7 +20,6 @@ import com.simibubi.create.foundation.render.backend.Backend; import com.simibubi.create.foundation.render.backend.OptifineHandler; import com.simibubi.create.foundation.utility.ghost.GhostBlocks; import com.simibubi.create.foundation.utility.outliner.Outliner; - import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.BlockModelShapes; @@ -42,6 +37,11 @@ import net.minecraftforge.client.model.ModelLoader; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + public class CreateClient { public static ClientSchematicLoader schematicSender; @@ -90,6 +90,9 @@ public class CreateClient { getColorHandler().init(); AllFluids.assignRenderLayers(); PonderIndex.register(); + PonderIndex.registerTags(); + + UIRenderHelper.init(); IResourceManager resourceManager = Minecraft.getInstance() .getResourceManager(); diff --git a/src/main/java/com/simibubi/create/events/ClientEvents.java b/src/main/java/com/simibubi/create/events/ClientEvents.java index 9a2811ab0..f03338c00 100644 --- a/src/main/java/com/simibubi/create/events/ClientEvents.java +++ b/src/main/java/com/simibubi/create/events/ClientEvents.java @@ -1,8 +1,5 @@ package com.simibubi.create.events; -import java.util.ArrayList; -import java.util.List; - import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.systems.RenderSystem; import com.simibubi.create.AllFluids; @@ -25,7 +22,6 @@ import com.simibubi.create.content.curiosities.zapper.blockzapper.BlockzapperRen import com.simibubi.create.content.curiosities.zapper.terrainzapper.WorldshaperRenderHandler; import com.simibubi.create.content.logistics.block.mechanicalArm.ArmInteractionPointHandler; import com.simibubi.create.foundation.config.AllConfigs; -import com.simibubi.create.foundation.gui.ScreenOpener; import com.simibubi.create.foundation.item.TooltipHelper; import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.networking.LeftClickPacket; @@ -40,7 +36,6 @@ import com.simibubi.create.foundation.tileEntity.behaviour.scrollvalue.ScrollVal import com.simibubi.create.foundation.utility.AnimationTickHolder; import com.simibubi.create.foundation.utility.ServerSpeedProvider; import com.simibubi.create.foundation.utility.placement.PlacementHelpers; - import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.ActiveRenderInfo; import net.minecraft.client.renderer.IRenderTypeBuffer; @@ -68,6 +63,9 @@ import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod.EventBusSubscriber; +import java.util.ArrayList; +import java.util.List; + @EventBusSubscriber(value = Dist.CLIENT) public class ClientEvents { @@ -95,7 +93,7 @@ public class ClientEvents { CouplingPhysics.tick(world); PonderTooltipHandler.tick(); - ScreenOpener.tick(); + //ScreenOpener.tick(); ServerSpeedProvider.clientTick(); BeltConnectorHandler.tick(); FilteringRenderer.tick(); diff --git a/src/main/java/com/simibubi/create/foundation/command/AllCommands.java b/src/main/java/com/simibubi/create/foundation/command/AllCommands.java index 760fe23c3..66d1ea8e8 100644 --- a/src/main/java/com/simibubi/create/foundation/command/AllCommands.java +++ b/src/main/java/com/simibubi/create/foundation/command/AllCommands.java @@ -28,6 +28,7 @@ public class AllCommands { .then(FixLightingCommand.register()) .then(HighlightCommand.register()) .then(CouplingCommand.register()) + .then(PonderCommand.register()) //utility .then(util) diff --git a/src/main/java/com/simibubi/create/foundation/command/ConfigureConfigPacket.java b/src/main/java/com/simibubi/create/foundation/command/ConfigureConfigPacket.java index 692933155..bb54b2db4 100644 --- a/src/main/java/com/simibubi/create/foundation/command/ConfigureConfigPacket.java +++ b/src/main/java/com/simibubi/create/foundation/command/ConfigureConfigPacket.java @@ -4,6 +4,7 @@ import com.simibubi.create.content.contraptions.goggles.GoggleConfigScreen; import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.gui.ScreenOpener; import com.simibubi.create.foundation.networking.SimplePacketBase; +import com.simibubi.create.foundation.ponder.content.PonderIndexScreen; import com.simibubi.create.foundation.render.backend.FastRenderDispatcher; import net.minecraft.client.Minecraft; import net.minecraft.client.entity.player.ClientPlayerEntity; @@ -65,6 +66,7 @@ public class ConfigureConfigPacket extends SimplePacketBase { fixLighting(() -> Actions::experimentalLighting), overlayReset(() -> Actions::overlayReset), experimentalRendering(() -> Actions::experimentalRendering), + ponderIndex(() -> Actions::ponderIndex), ; @@ -130,6 +132,11 @@ public class ConfigureConfigPacket extends SimplePacketBase { Minecraft.getInstance().worldRenderer.loadRenderers(); } + @OnlyIn(Dist.CLIENT) + private static void ponderIndex(String value) { + ScreenOpener.transitionTo(new PonderIndexScreen()); + } + private static ITextComponent boolToText(boolean b) { return b ? new StringTextComponent("enabled").applyTextStyle(TextFormatting.DARK_GREEN) diff --git a/src/main/java/com/simibubi/create/foundation/command/PonderCommand.java b/src/main/java/com/simibubi/create/foundation/command/PonderCommand.java new file mode 100644 index 000000000..b69b5664b --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/command/PonderCommand.java @@ -0,0 +1,25 @@ +package com.simibubi.create.foundation.command; + +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.simibubi.create.foundation.networking.AllPackets; +import net.minecraft.command.CommandSource; +import net.minecraft.command.Commands; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraftforge.fml.network.PacketDistributor; + +public class PonderCommand { + + static ArgumentBuilder register() { + return Commands.literal("ponder") + .requires(cs -> cs.hasPermissionLevel(0)) + .executes(ctx -> { + ServerPlayerEntity player = ctx.getSource().asPlayer(); + + AllPackets.channel.send( + PacketDistributor.PLAYER.with(() -> player), + new ConfigureConfigPacket(ConfigureConfigPacket.Actions.ponderIndex.name(), "")); + + return 1; + }); + } +} diff --git a/src/main/java/com/simibubi/create/foundation/gui/AbstractSimiScreen.java b/src/main/java/com/simibubi/create/foundation/gui/AbstractSimiScreen.java index 2b1902376..25ebdcac4 100644 --- a/src/main/java/com/simibubi/create/foundation/gui/AbstractSimiScreen.java +++ b/src/main/java/com/simibubi/create/foundation/gui/AbstractSimiScreen.java @@ -1,16 +1,23 @@ package com.simibubi.create.foundation.gui; -import java.util.ArrayList; -import java.util.List; - +import com.mojang.blaze3d.systems.RenderSystem; import com.simibubi.create.foundation.gui.widgets.AbstractSimiWidget; - +import com.simibubi.create.foundation.utility.LerpedFloat; +import net.minecraft.client.MainWindow; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.widget.Widget; import net.minecraft.util.text.StringTextComponent; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; +import org.apache.commons.lang3.mutable.MutableBoolean; +import org.apache.commons.lang3.mutable.MutableInt; +import org.apache.logging.log4j.LogManager; +import org.lwjgl.glfw.GLFW; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; @OnlyIn(Dist.CLIENT) public abstract class AbstractSimiScreen extends Screen { @@ -18,6 +25,7 @@ public abstract class AbstractSimiScreen extends Screen { protected int sWidth, sHeight; protected int guiLeft, guiTop; protected List widgets; + public final LerpedFloat transition = LerpedFloat.linear().startWithValue(0).chase(0, .1f, LerpedFloat.Chaser.LINEAR); protected AbstractSimiScreen() { super(new StringTextComponent("")); @@ -31,17 +39,86 @@ public abstract class AbstractSimiScreen extends Screen { guiTop = (this.height - sHeight) / 2; } + @Override + public void tick() { + super.tick(); + + transition.tickChaser(); + + if (transition.getValue() < -0.9995f) { + transition.updateChaseTarget(0); + transition.setValue(0); + ScreenOpener.openLastScreen(); + } else if (transition.getValue() > 0.9995f) { + transition.updateChaseTarget(0); + transition.setValue(0); + } + } + @Override public void render(int mouseX, int mouseY, float partialTicks) { + + RenderSystem.pushMatrix(); + renderTransition(mouseX, mouseY, partialTicks); + partialTicks = Minecraft.getInstance() .getRenderPartialTicks(); renderBackground(); renderWindow(mouseX, mouseY, partialTicks); for (Widget widget : widgets) widget.render(mouseX, mouseY, partialTicks); + + renderBreadcrumbs(mouseX, mouseY, partialTicks); + renderWindowForeground(mouseX, mouseY, partialTicks); for (Widget widget : widgets) widget.renderToolTip(mouseX, mouseY); + + RenderSystem.popMatrix(); + } + + private void renderTransition(int mouseX, int mouseY, float partialTicks) { + if (transition.getChaseTarget() != 0) { + if (ScreenOpener.getLastScreen() == null) { + return; + } else if (ScreenOpener.getLastScreen() == this) { + LogManager.getLogger().warn("Tired to render last screen recursively during transition"); + return; + } + //draw last screen into buffer + RenderSystem.pushMatrix();//1 + UIRenderHelper.framebuffer.framebufferClear(Minecraft.IS_RUNNING_ON_MAC); + UIRenderHelper.prepFramebufferSize(); + RenderSystem.pushMatrix();//2 + RenderSystem.translated(0, 0, -1000); + UIRenderHelper.framebuffer.bindFramebuffer(true); + ScreenOpener.getLastScreen().render(mouseX, mouseY, partialTicks); + RenderSystem.popMatrix();//2 + Minecraft.getInstance().getFramebuffer().bindFramebuffer(true); + + //use the buffer texture + float transitionValue = transition.getValue(partialTicks); + if (transition.getChaseTarget() < 0) + transitionValue += 1; + //transitionV is ~1 when the older screen is hidden + //transitionV is ~0 when the older screen is still fully visible + double scale = 1 - 0.25 * transitionValue; + MainWindow window = Minecraft.getInstance().getWindow(); + int sw = window.getScaledWidth(); + int sh = window.getScaledHeight(); + RenderSystem.translated(sw * 0.5, sh * 0.5, -10); + RenderSystem.scaled(scale, scale, 1); + RenderSystem.translated(sw * -0.5, sh * -0.5, 0); + + UIRenderHelper.drawFramebuffer(sw, sh, 1f - transitionValue); + RenderSystem.popMatrix();//1 + + //modify current screen as well + scale = 1 + 0.02 * (1 - transitionValue); + RenderSystem.translated(sw * 0.5, sh * 0.5, 0); + RenderSystem.scaled(scale, scale, 1); + RenderSystem.translated(sw * -0.5, sh * -0.5, 0); + } } @Override @@ -60,6 +137,12 @@ public abstract class AbstractSimiScreen extends Screen { if (widget.keyPressed(code, p_keyPressed_2_, p_keyPressed_3_)) return true; } + + if (code == GLFW.GLFW_KEY_BACKSPACE) { + ScreenOpener.transitionToLast(); + return true; + } + return super.keyPressed(code, p_keyPressed_2_, p_keyPressed_3_); } @@ -98,6 +181,12 @@ public abstract class AbstractSimiScreen extends Screen { return true; } + @Override + public void onClose() { + ScreenOpener.clearStack(); + super.onClose(); + } + @Override public boolean isPauseScreen() { return false; @@ -105,6 +194,53 @@ public abstract class AbstractSimiScreen extends Screen { protected abstract void renderWindow(int mouseX, int mouseY, float partialTicks); + protected void renderBreadcrumbs(int mouseX, int mouseY, float partialTicks) { + List history = ScreenOpener.getScreenHistory(); + if (history.isEmpty()) + return; + + history.add(0, Minecraft.getInstance().currentScreen); + int spacing = 20; + + List names = history + .stream() + .map(AbstractSimiScreen::screenTitle) + .collect(Collectors.toList()); + + int bWidth = names + .stream() + .mapToInt(s -> font.getStringWidth(s) + spacing) + .sum(); + + MutableInt x = new MutableInt(width - bWidth); + MutableInt y = new MutableInt(height - 18); + MutableBoolean first = new MutableBoolean(true); + + if (x.getValue() < 25) + x.setValue(25); + + names.forEach(s -> { + int sWidth = font.getStringWidth(s); + //UIRenderHelper.breadcrumbArrow(x.getValue(), y.getValue(), sWidth + spacing, 14, spacing/2, 0xbbababab, 0x22ababab); + UIRenderHelper.breadcrumbArrow(x.getValue(), y.getValue(), sWidth + spacing, 14, spacing/2, 0xdd101010, 0x44101010); + drawString(font, s, x.getValue() + 5, y.getValue() + 3, first.getValue() ? 0xffeeffee : 0xffddeeff); + first.setFalse(); + + x.add(sWidth + spacing); + }); + } + + private static String screenTitle(Screen screen) { + if (screen instanceof AbstractSimiScreen) + return ((AbstractSimiScreen) screen).getBreadcrumbTitle(); + + return screen.getClass().getSimpleName(); + } + + protected String getBreadcrumbTitle() { + return this.getClass().getSimpleName(); + } + protected void renderWindowForeground(int mouseX, int mouseY, float partialTicks) { for (Widget widget : widgets) { if (!widget.isHovered()) 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 efe59e30a..b69069be6 100644 --- a/src/main/java/com/simibubi/create/foundation/gui/AllGuiTextures.java +++ b/src/main/java/com/simibubi/create/foundation/gui/AllGuiTextures.java @@ -1,15 +1,13 @@ package com.simibubi.create.foundation.gui; import com.simibubi.create.Create; - import net.minecraft.client.Minecraft; import net.minecraft.client.gui.AbstractGui; -import net.minecraft.client.gui.screen.Screen; import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -public enum AllGuiTextures { +public enum AllGuiTextures implements IScreenRenderable { // Inventories PLAYER_INVENTORY("player_inventory.png", 176, 108), @@ -117,16 +115,10 @@ public enum AllGuiTextures { .bindTexture(location); } + @Override @OnlyIn(Dist.CLIENT) public void draw(AbstractGui screen, int x, int y) { bind(); screen.blit(x, y, startX, startY, width, height); } - - @OnlyIn(Dist.CLIENT) - public void draw(int x, int y) { - draw(new Screen(null) { - }, x, y); - } - } 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 7d85393c7..17806fb70 100644 --- a/src/main/java/com/simibubi/create/foundation/gui/AllIcons.java +++ b/src/main/java/com/simibubi/create/foundation/gui/AllIcons.java @@ -5,10 +5,8 @@ import com.mojang.blaze3d.matrix.MatrixStack.Entry; import com.mojang.blaze3d.vertex.IVertexBuilder; import com.simibubi.create.Create; import com.simibubi.create.foundation.utility.ColorHelper; - import net.minecraft.client.Minecraft; import net.minecraft.client.gui.AbstractGui; -import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.client.renderer.RenderType; import net.minecraft.util.ResourceLocation; @@ -16,7 +14,7 @@ import net.minecraft.util.math.Vec3d; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -public class AllIcons { +public class AllIcons implements IScreenRenderable { public static final ResourceLocation ICON_ATLAS = Create.asResource("textures/gui/icons.png"); private static int x = 0, y = -1; @@ -146,18 +144,13 @@ public class AllIcons { .bindTexture(ICON_ATLAS); } + @Override @OnlyIn(Dist.CLIENT) public void draw(AbstractGui screen, int x, int y) { bind(); screen.blit(x, y, iconX, iconY, 16, 16); } - @OnlyIn(Dist.CLIENT) - public void draw(int x, int y) { - draw(new Screen(null) { - }, x, y); - } - @OnlyIn(Dist.CLIENT) public void draw(MatrixStack ms, IRenderTypeBuffer buffer, int color) { IVertexBuilder builder = buffer.getBuffer(RenderType.getTextSeeThrough(ICON_ATLAS)); diff --git a/src/main/java/com/simibubi/create/foundation/gui/IScreenRenderable.java b/src/main/java/com/simibubi/create/foundation/gui/IScreenRenderable.java new file mode 100644 index 000000000..d4e16c6aa --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/gui/IScreenRenderable.java @@ -0,0 +1,18 @@ +package com.simibubi.create.foundation.gui; + +import net.minecraft.client.gui.AbstractGui; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.util.text.StringTextComponent; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +public interface IScreenRenderable { + + @OnlyIn(Dist.CLIENT) + void draw(AbstractGui screen, int x, int y); + + @OnlyIn(Dist.CLIENT) + default void draw(int x, int y) { + draw(new Screen(new StringTextComponent("")) {}, x, y); + } +} 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 bef5042de..59a08da3a 100644 --- a/src/main/java/com/simibubi/create/foundation/gui/ScreenOpener.java +++ b/src/main/java/com/simibubi/create/foundation/gui/ScreenOpener.java @@ -2,28 +2,72 @@ package com.simibubi.create.foundation.gui; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screen.Screen; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; -import net.minecraftforge.fml.DistExecutor; + +import javax.annotation.Nullable; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; public class ScreenOpener { - @OnlyIn(Dist.CLIENT) - private static Screen openedGuiNextTick; + private static final Deque backStack = new ArrayDeque<>(); - public static void tick() { - DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> { - if (openedGuiNextTick != null) { - Minecraft.getInstance().displayGuiScreen(openedGuiNextTick); - openedGuiNextTick = null; - } - }); + public static void open(Screen screen) { + open(Minecraft.getInstance().currentScreen, screen); } - public static void open(Screen gui) { - DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> { - openedGuiNextTick = gui; - }); + public static void open(@Nullable Screen current, Screen toOpen) { + if (current != null) { + if (backStack.size() >= 15) //don't go deeper than 15 steps + backStack.pollLast(); + + backStack.push(current); + } else + backStack.clear(); + + openScreen(toOpen); + } + + public static void openLastScreen() { + if (backStack.isEmpty()) + return; + + openScreen(backStack.pop()); + } + + //transitions are only supported in simiScreens atm. they take care of all the rendering for it + public static void transitionTo(AbstractSimiScreen screen) { + screen.transition.updateChaseTarget(1); + open(screen); + } + + public static void transitionToLast() { + if (backStack.isEmpty()) + return; + + Screen currentScreen = Minecraft.getInstance().currentScreen; + if (currentScreen instanceof AbstractSimiScreen) + ((AbstractSimiScreen) currentScreen).transition.updateChaseTarget(-1); + else + openLastScreen(); + } + + public static void clearStack() { + backStack.clear(); + } + + public static List getScreenHistory() { + return new ArrayList<>(backStack); + } + + @Nullable + public static Screen getLastScreen() { + return backStack.peek(); + } + + private static void openScreen(Screen screen) { + Minecraft.getInstance().enqueue(() -> Minecraft.getInstance().displayGuiScreen(screen)); } } diff --git a/src/main/java/com/simibubi/create/foundation/gui/UIRenderHelper.java b/src/main/java/com/simibubi/create/foundation/gui/UIRenderHelper.java new file mode 100644 index 000000000..87816728e --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/gui/UIRenderHelper.java @@ -0,0 +1,174 @@ +package com.simibubi.create.foundation.gui; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.simibubi.create.foundation.utility.ColorHelper; +import net.minecraft.client.MainWindow; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.shader.Framebuffer; +import net.minecraftforge.fml.client.gui.GuiUtils; +import org.lwjgl.opengl.GL11; + +public class UIRenderHelper { + + public static Framebuffer framebuffer; + + public static void init() { + RenderSystem.recordRenderCall(() -> { + MainWindow mainWindow = Minecraft.getInstance().getWindow(); + framebuffer = new Framebuffer(mainWindow.getFramebufferWidth(), mainWindow.getFramebufferHeight(), true, Minecraft.IS_RUNNING_ON_MAC); + framebuffer.deleteFramebuffer(); + }); + } + + public static void prepFramebufferSize() { + 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); + } + } + + public static void drawFramebuffer(int width, int height, float alpha) { + float vx = (float) width; + float vy = (float) height; + float tx = (float) framebuffer.framebufferWidth / (float) framebuffer.framebufferTextureWidth; + float ty = (float) framebuffer.framebufferHeight / (float) framebuffer.framebufferTextureHeight; + + + RenderSystem.enableTexture(); + RenderSystem.enableBlend(); + RenderSystem.disableLighting(); + RenderSystem.disableAlphaTest(); + RenderSystem.defaultBlendFunc(); + RenderSystem.enableDepthTest(); + + framebuffer.bindFramebufferTexture(); + + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder bufferbuilder = tessellator.getBuffer(); + bufferbuilder.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR_TEXTURE); + + bufferbuilder.vertex(0, vy, 0).color(1, 1, 1, alpha).texture(0,0).endVertex(); + bufferbuilder.vertex(vx, vy, 0).color(1, 1, 1, alpha).texture(tx,0).endVertex(); + bufferbuilder.vertex(vx, 0, 0).color(1, 1, 1, alpha).texture(tx, ty).endVertex(); + bufferbuilder.vertex(0, 0, 0).color(1, 1, 1, alpha).texture(0, ty).endVertex(); + + tessellator.draw(); + framebuffer.unbindFramebufferTexture(); + RenderSystem.disableBlend(); + RenderSystem.enableAlphaTest(); + } + + //angle in degrees; 0° -> fading to the right + //x and y specify the middle point of the starting edge + //width is the total width of the streak + public static void streak(float angle, int x, int y, int width, int length, int color) { + int a1 = 0xa0 << 24; + int a2 = 0x80 << 24; + int a3 = 0x10 << 24; + int a4 = 0x00 << 24; + + color = color & 0x00FFFFFF; + int c1 = a1 | color; + int c2 = a2 | color; + int c3 = a3 | color; + int c4 = a4 | color; + + RenderSystem.pushMatrix(); + RenderSystem.translated(x, y, 0); + RenderSystem.rotatef(angle - 90, 0, 0, 1); + + streak(width/2, length, c1, c2, c3, c4); + + RenderSystem.popMatrix(); + } + + private static void streak(int width, int height, int c1, int c2, int c3, int c4) { + double split1 = .5; + double split2 = .75; + GuiUtils.drawGradientRect(0, -width, 0, width, (int) (split1 * height), c1, c2); + GuiUtils.drawGradientRect(0, -width, (int) (split1 * height), width, (int) (split2 * height), c2, c3); + GuiUtils.drawGradientRect(0, -width, (int) (split2 * height), width, height, c3, c4); + } + + //draws a wide chevron-style breadcrumb arrow pointing left + public static void breadcrumbArrow(int x, int y, int width, int height, int indent, int startColor, int endColor) { + RenderSystem.pushMatrix(); + RenderSystem.translated(x - indent, y, 0); + + breadcrumbArrow(width, height, indent, startColor, endColor); + + RenderSystem.popMatrix(); + } + + private static void breadcrumbArrow(int width, int height, int indent, int c1, int c2) { + + /* + * 0,0 x1,y1 ********************* x4,y4 ***** x7,y7 + * **** **** + * **** **** + * x0,y0 x2,y2 x5,y5 + * **** **** + * **** **** + * x3,y3 ********************* x6,y6 ***** x8,y8 + * + * */ + + double x0 = 0, y0 = height / 2d; + double x1 = indent, y1 = 0; + double x2 = indent, y2 = height / 2d; + double x3 = indent, y3 = height; + double x4 = width, y4 = 0; + double x5 = width, y5 = height / 2d; + double x6 = width, y6 = height; + double x7 = indent + width, y7 = 0; + double x8 = indent + width, y8 = height; + + int fc1 = ColorHelper.mixAlphaColors(c1, c2, 0); + int fc2 = ColorHelper.mixAlphaColors(c1, c2, (indent)/(width + 2f * indent)); + int fc3 = ColorHelper.mixAlphaColors(c1, c2, (indent + width)/(width + 2f * indent)); + int fc4 = ColorHelper.mixAlphaColors(c1, c2, 1); + + RenderSystem.disableTexture(); + RenderSystem.enableBlend(); + RenderSystem.disableAlphaTest(); + RenderSystem.defaultBlendFunc(); + RenderSystem.shadeModel(GL11.GL_SMOOTH); + + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder bufferbuilder = tessellator.getBuffer(); + bufferbuilder.begin(GL11.GL_TRIANGLES, DefaultVertexFormats.POSITION_COLOR); + + bufferbuilder.vertex(x0, y0, 0).color(fc1 >> 16 & 0xFF, fc1 >> 8 & 0xFF, fc1 & 0xFF, fc1 >> 24 & 0xFF).endVertex(); + bufferbuilder.vertex(x1, y1, 0).color(fc2 >> 16 & 0xFF, fc2 >> 8 & 0xFF, fc2 & 0xFF, fc2 >> 24 & 0xFF).endVertex(); + bufferbuilder.vertex(x2, y2, 0).color(fc2 >> 16 & 0xFF, fc2 >> 8 & 0xFF, fc2 & 0xFF, fc2 >> 24 & 0xFF).endVertex(); + + bufferbuilder.vertex(x0, y0, 0).color(fc1 >> 16 & 0xFF, fc1 >> 8 & 0xFF, fc1 & 0xFF, fc1 >> 24 & 0xFF).endVertex(); + bufferbuilder.vertex(x2, y2, 0).color(fc2 >> 16 & 0xFF, fc2 >> 8 & 0xFF, fc2 & 0xFF, fc2 >> 24 & 0xFF).endVertex(); + bufferbuilder.vertex(x3, y3, 0).color(fc2 >> 16 & 0xFF, fc2 >> 8 & 0xFF, fc2 & 0xFF, fc2 >> 24 & 0xFF).endVertex(); + + bufferbuilder.vertex(x3, y3, 0).color(fc2 >> 16 & 0xFF, fc2 >> 8 & 0xFF, fc2 & 0xFF, fc2 >> 24 & 0xFF).endVertex(); + bufferbuilder.vertex(x1, y1, 0).color(fc2 >> 16 & 0xFF, fc2 >> 8 & 0xFF, fc2 & 0xFF, fc2 >> 24 & 0xFF).endVertex(); + bufferbuilder.vertex(x4, y4, 0).color(fc3 >> 16 & 0xFF, fc3 >> 8 & 0xFF, fc3 & 0xFF, fc3 >> 24 & 0xFF).endVertex(); + + bufferbuilder.vertex(x3, y3, 0).color(fc2 >> 16 & 0xFF, fc2 >> 8 & 0xFF, fc2 & 0xFF, fc2 >> 24 & 0xFF).endVertex(); + bufferbuilder.vertex(x4, y4, 0).color(fc3 >> 16 & 0xFF, fc3 >> 8 & 0xFF, fc3 & 0xFF, fc3 >> 24 & 0xFF).endVertex(); + bufferbuilder.vertex(x6, y6, 0).color(fc3 >> 16 & 0xFF, fc3 >> 8 & 0xFF, fc3 & 0xFF, fc3 >> 24 & 0xFF).endVertex(); + + bufferbuilder.vertex(x5, y5, 0).color(fc3 >> 16 & 0xFF, fc3 >> 8 & 0xFF, fc3 & 0xFF, fc3 >> 24 & 0xFF).endVertex(); + bufferbuilder.vertex(x4, y4, 0).color(fc3 >> 16 & 0xFF, fc3 >> 8 & 0xFF, fc3 & 0xFF, fc3 >> 24 & 0xFF).endVertex(); + bufferbuilder.vertex(x7, y7, 0).color(fc4 >> 16 & 0xFF, fc4 >> 8 & 0xFF, fc4 & 0xFF, fc4 >> 24 & 0xFF).endVertex(); + + bufferbuilder.vertex(x6, y6, 0).color(fc3 >> 16 & 0xFF, fc3 >> 8 & 0xFF, fc3 & 0xFF, fc3 >> 24 & 0xFF).endVertex(); + bufferbuilder.vertex(x5, y5, 0).color(fc3 >> 16 & 0xFF, fc3 >> 8 & 0xFF, fc3 & 0xFF, fc3 >> 24 & 0xFF).endVertex(); + bufferbuilder.vertex(x8, y8, 0).color(fc4 >> 16 & 0xFF, fc4 >> 8 & 0xFF, fc4 & 0xFF, fc4 >> 24 & 0xFF).endVertex(); + + tessellator.draw(); + RenderSystem.shadeModel(GL11.GL_FLAT); + RenderSystem.disableBlend(); + RenderSystem.enableAlphaTest(); + RenderSystem.enableTexture(); + } +} diff --git a/src/main/java/com/simibubi/create/foundation/ponder/PonderRegistry.java b/src/main/java/com/simibubi/create/foundation/ponder/PonderRegistry.java index 39a69fa25..0b93608c9 100644 --- a/src/main/java/com/simibubi/create/foundation/ponder/PonderRegistry.java +++ b/src/main/java/com/simibubi/create/foundation/ponder/PonderRegistry.java @@ -1,22 +1,10 @@ package com.simibubi.create.foundation.ponder; -import java.io.BufferedInputStream; -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.zip.GZIPInputStream; - import com.google.gson.JsonElement; import com.simibubi.create.Create; import com.simibubi.create.foundation.ponder.PonderStoryBoardEntry.PonderStoryBoard; -import com.simibubi.create.foundation.ponder.content.PonderIndex; -import com.simibubi.create.foundation.ponder.content.SharedText; +import com.simibubi.create.foundation.ponder.content.*; import com.tterrag.registrate.util.entry.ItemProviderEntry; - import net.minecraft.client.Minecraft; import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.CompressedStreamTools; @@ -26,38 +14,66 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.gen.feature.template.PlacementSettings; import net.minecraft.world.gen.feature.template.Template; +import java.io.BufferedInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; +import java.util.function.Consumer; +import java.util.zip.GZIPInputStream; + public class PonderRegistry { - static Map> all = new HashMap<>(); + public static final PonderTagRegistry tags = new PonderTagRegistry(); + public static final PonderChapterRegistry chapters = new PonderChapterRegistry(); + public static Map> all = new HashMap<>(); - public static void addStoryBoard(ItemProviderEntry component, String schematic, PonderStoryBoard storyBoard) { + public static PonderSceneBuilder addStoryBoard(ItemProviderEntry component, String schematic, PonderStoryBoard storyBoard) { ResourceLocation id = component.getId(); - all.computeIfAbsent(id, $ -> new ArrayList<>()) - .add(new PonderStoryBoardEntry(storyBoard, schematic)); + PonderStoryBoardEntry entry = new PonderStoryBoardEntry(storyBoard, schematic, id); + PonderSceneBuilder builder = new PonderSceneBuilder(entry); + all.computeIfAbsent(id, _$ -> new ArrayList<>()).add(entry); + return builder; } - public static MultiSceneBuilder forComponents(ItemProviderEntry component, ItemProviderEntry... additional) { - return new MultiSceneBuilder(component, additional); + public static PonderSceneBuilder addStoryBoard(PonderChapter chapter, ResourceLocation component, String schematic, PonderStoryBoard storyBoard) { + if (component == null) + component = new ResourceLocation("minecraft", "stick"); + + PonderStoryBoardEntry entry = new PonderStoryBoardEntry(storyBoard, schematic, component); + PonderSceneBuilder builder = new PonderSceneBuilder(entry); + chapters.addStoriesToChapter(chapter, entry); + return builder; + } + + public static MultiSceneBuilder forComponents(ItemProviderEntry... components) { + return new MultiSceneBuilder(Arrays.asList(components)); } public static List compile(ResourceLocation id) { + return compile(all.get(id)); + } + public static List compile(PonderChapter chapter) { + return compile(chapters.getStories(chapter)); + } + + public static List compile(List entries) { if (PonderIndex.EDITOR_MODE) { - PonderLocalization.shared.clear(); - PonderLocalization.specific.clear(); - SharedText.gatherText(); + //PonderLocalization.shared.clear(); + //PonderLocalization.specific.clear(); + //SharedText.gatherText(); } - List list = all.get(id); List scenes = new ArrayList<>(); - for (int i = 0; i < list.size(); i++) { - PonderStoryBoardEntry sb = list.get(i); + for (int i = 0; i < entries.size(); i++) { + PonderStoryBoardEntry sb = entries.get(i); Template activeTemplate = loadSchematic(sb.getSchematicName()); PonderWorld world = new PonderWorld(BlockPos.ZERO, Minecraft.getInstance().world); activeTemplate.addBlocksToWorld(world, BlockPos.ZERO, new PlacementSettings()); world.createBackup(); - PonderScene scene = compileScene(id, i, sb, world); + PonderScene scene = compileScene(i, sb, world); scene.begin(); scenes.add(scene); } @@ -65,11 +81,10 @@ public class PonderRegistry { return scenes; } - public static PonderScene compileScene(ResourceLocation id, int i, PonderStoryBoardEntry sb, PonderWorld world) { - PonderScene scene = new PonderScene(world, id, i); + public static PonderScene compileScene(int i, PonderStoryBoardEntry sb, PonderWorld world) { + PonderScene scene = new PonderScene(world, sb.getComponent(), i, sb.getTags()); SceneBuilder builder = scene.builder(); - sb.getBoard() - .program(builder, scene.getSceneBuildingUtil()); + sb.getBoard().program(builder, scene.getSceneBuildingUtil()); return scene; } @@ -95,28 +110,58 @@ public class PonderRegistry { SharedText.gatherText(); all.forEach((id, list) -> { for (int i = 0; i < list.size(); i++) - compileScene(id, i, list.get(i), null); + compileScene(i, list.get(i), null); }); return PonderLocalization.record(); } public static class MultiSceneBuilder { - private ItemProviderEntry component; - private ItemProviderEntry[] additional; + private final Collection> components; - MultiSceneBuilder(ItemProviderEntry component, ItemProviderEntry[] additional) { - this.component = component; - this.additional = additional; + MultiSceneBuilder(Collection> components) { + this.components = components; } public MultiSceneBuilder addStoryBoard(String schematicPath, PonderStoryBoard storyBoard) { - PonderRegistry.addStoryBoard(component, schematicPath, storyBoard); - for (ItemProviderEntry itemProviderEntry : additional) - PonderRegistry.addStoryBoard(itemProviderEntry, schematicPath, storyBoard); + return addStoryBoard(schematicPath, storyBoard, PonderSceneBuilder::highlightAllTags); + } + + public MultiSceneBuilder addStoryBoard(String schematicPath, PonderStoryBoard storyBoard, Consumer extras) { + components.forEach(c -> extras.accept(PonderRegistry.addStoryBoard(c, schematicPath, storyBoard))); return this; } } + public static class PonderSceneBuilder { + + private final PonderStoryBoardEntry entry; + + PonderSceneBuilder(PonderStoryBoardEntry entry) { + this.entry = entry; + } + + public PonderSceneBuilder highlightAllTags() { + entry.getTags().add(PonderTag.Highlight.ALL); + return this; + } + + public PonderSceneBuilder highlightTags(PonderTag... tags) { + entry.getTags().addAll(Arrays.asList(tags)); + return this; + } + + public PonderSceneBuilder chapter(PonderChapter chapter) { + PonderRegistry.chapters.addStoriesToChapter(chapter, entry); + return this; + } + + public PonderSceneBuilder chapters(PonderChapter... chapters) { + for (PonderChapter c : chapters) + chapter(c); + return this; + } + } + } diff --git a/src/main/java/com/simibubi/create/foundation/ponder/PonderScene.java b/src/main/java/com/simibubi/create/foundation/ponder/PonderScene.java index 8806eac71..6578ab8b6 100644 --- a/src/main/java/com/simibubi/create/foundation/ponder/PonderScene.java +++ b/src/main/java/com/simibubi/create/foundation/ponder/PonderScene.java @@ -1,34 +1,15 @@ package com.simibubi.create.foundation.ponder; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.apache.commons.lang3.mutable.MutableDouble; -import org.apache.commons.lang3.mutable.MutableObject; - import com.mojang.blaze3d.matrix.MatrixStack; import com.simibubi.create.foundation.ponder.content.PonderIndex; +import com.simibubi.create.foundation.ponder.content.PonderTag; import com.simibubi.create.foundation.ponder.elements.PonderOverlayElement; import com.simibubi.create.foundation.ponder.elements.PonderSceneElement; import com.simibubi.create.foundation.ponder.elements.WorldSectionElement; import com.simibubi.create.foundation.ponder.instructions.HideAllInstruction; import com.simibubi.create.foundation.renderState.SuperRenderTypeBuffer; -import com.simibubi.create.foundation.utility.AnimationTickHolder; -import com.simibubi.create.foundation.utility.LerpedFloat; -import com.simibubi.create.foundation.utility.MatrixStacker; -import com.simibubi.create.foundation.utility.Pair; -import com.simibubi.create.foundation.utility.VecHelper; +import com.simibubi.create.foundation.utility.*; import com.simibubi.create.foundation.utility.outliner.Outliner; - import net.minecraft.block.BlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.ActiveRenderInfo; @@ -41,12 +22,14 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.Direction; import net.minecraft.util.Direction.Axis; import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.BlockRayTraceResult; -import net.minecraft.util.math.MutableBoundingBox; -import net.minecraft.util.math.Vec2f; -import net.minecraft.util.math.Vec3d; -import net.minecraft.util.math.Vec3i; +import net.minecraft.util.math.*; +import org.apache.commons.lang3.mutable.MutableDouble; +import org.apache.commons.lang3.mutable.MutableObject; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; public class PonderScene { @@ -59,6 +42,7 @@ public class PonderScene { List schedule, activeSchedule; Map linkedElements; Set elements; + List tags; PonderWorld world; ResourceLocation component; @@ -79,7 +63,7 @@ public class PonderScene { int totalTime; int currentTime; - public PonderScene(PonderWorld world, ResourceLocation component, int sceneIndex) { + public PonderScene(PonderWorld world, ResourceLocation component, int sceneIndex, Collection tags) { pointOfInterest = Vec3d.ZERO; textIndex = 1; @@ -90,6 +74,7 @@ public class PonderScene { outliner = new Outliner(); elements = new HashSet<>(); linkedElements = new HashMap<>(); + this.tags = new ArrayList<>(tags); schedule = new ArrayList<>(); activeSchedule = new ArrayList<>(); transform = new SceneTransform(); diff --git a/src/main/java/com/simibubi/create/foundation/ponder/PonderStoryBoardEntry.java b/src/main/java/com/simibubi/create/foundation/ponder/PonderStoryBoardEntry.java index aa397e763..7de4ccf5e 100644 --- a/src/main/java/com/simibubi/create/foundation/ponder/PonderStoryBoardEntry.java +++ b/src/main/java/com/simibubi/create/foundation/ponder/PonderStoryBoardEntry.java @@ -1,17 +1,27 @@ package com.simibubi.create.foundation.ponder; +import com.simibubi.create.foundation.ponder.content.PonderTag; +import net.minecraft.util.ResourceLocation; + +import java.util.ArrayList; +import java.util.List; + public class PonderStoryBoardEntry { - private String schematicName; - private PonderStoryBoard board; + private final String schematicName; + private final PonderStoryBoard board; + private final List tags; + private final ResourceLocation component; - public PonderStoryBoardEntry(PonderStoryBoard board, String schematicName) { + public PonderStoryBoardEntry(PonderStoryBoard board, String schematicName, ResourceLocation component) { this.board = board; this.schematicName = schematicName; + this.tags = new ArrayList<>(); + this.component = component; } public interface PonderStoryBoard { - public abstract void program(SceneBuilder scene, SceneBuildingUtil util); + void program(SceneBuilder scene, SceneBuildingUtil util); } public String getSchematicName() { @@ -22,4 +32,12 @@ public class PonderStoryBoardEntry { return board; } + public List getTags() { + return tags; + } + + public ResourceLocation getComponent() { + return component; + } + } diff --git a/src/main/java/com/simibubi/create/foundation/ponder/PonderTooltipHandler.java b/src/main/java/com/simibubi/create/foundation/ponder/PonderTooltipHandler.java index 6bf190ba3..8421df99e 100644 --- a/src/main/java/com/simibubi/create/foundation/ponder/PonderTooltipHandler.java +++ b/src/main/java/com/simibubi/create/foundation/ponder/PonderTooltipHandler.java @@ -1,14 +1,13 @@ package com.simibubi.create.foundation.ponder; -import java.util.List; - import com.google.common.base.Strings; import com.simibubi.create.foundation.gui.ScreenOpener; +import com.simibubi.create.foundation.ponder.content.PonderIndexScreen; +import com.simibubi.create.foundation.ponder.content.PonderTagScreen; import com.simibubi.create.foundation.utility.AnimationTickHolder; import com.simibubi.create.foundation.utility.ColorHelper; import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.foundation.utility.LerpedFloat; - import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.screen.Screen; @@ -22,6 +21,8 @@ import net.minecraft.util.text.StringTextComponent; import net.minecraft.util.text.TextFormatting; import net.minecraftforge.client.event.RenderTooltipEvent; +import java.util.List; + public class PonderTooltipHandler { static LerpedFloat holdWProgress = LerpedFloat.linear() @@ -51,6 +52,12 @@ public class PonderTooltipHandler { stack = ponderUI.getHoveredTooltipItem(); if (stack.isItemEqual(ponderUI.getSubject())) subject = true; + } else if (currentScreen instanceof PonderTagScreen) { + PonderTagScreen tagScreen = (PonderTagScreen) currentScreen; + stack = tagScreen.getHoveredTooltipItem(); + } else if (currentScreen instanceof PonderIndexScreen) { + PonderIndexScreen indexScreen = (PonderIndexScreen) currentScreen; + stack = indexScreen.getHoveredTooltipItem(); } else return; @@ -71,8 +78,7 @@ public class PonderTooltipHandler { if (!subject && InputMappings.isKeyDown(window, keyCode)) { if (value >= 1) { - ScreenOpener.open(new PonderUI(PonderRegistry.compile(stack.getItem() - .getRegistryName()))); + ScreenOpener.transitionTo(PonderUI.of(stack)); holdWProgress.startWithValue(0); return; } diff --git a/src/main/java/com/simibubi/create/foundation/ponder/PonderUI.java b/src/main/java/com/simibubi/create/foundation/ponder/PonderUI.java index 609e79525..6b30e3cfc 100644 --- a/src/main/java/com/simibubi/create/foundation/ponder/PonderUI.java +++ b/src/main/java/com/simibubi/create/foundation/ponder/PonderUI.java @@ -1,27 +1,14 @@ package com.simibubi.create.foundation.ponder; -import java.util.List; - -import org.apache.commons.lang3.mutable.MutableBoolean; - 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.AllGuiTextures; -import com.simibubi.create.foundation.gui.AllIcons; +import com.simibubi.create.foundation.gui.*; import com.simibubi.create.foundation.ponder.PonderScene.SceneTransform; -import com.simibubi.create.foundation.ponder.content.PonderIndex; +import com.simibubi.create.foundation.ponder.content.*; import com.simibubi.create.foundation.ponder.ui.PonderButton; import com.simibubi.create.foundation.renderState.SuperRenderTypeBuffer; -import com.simibubi.create.foundation.utility.AnimationTickHolder; -import com.simibubi.create.foundation.utility.ColorHelper; -import com.simibubi.create.foundation.utility.Iterate; -import com.simibubi.create.foundation.utility.Lang; -import com.simibubi.create.foundation.utility.LerpedFloat; +import com.simibubi.create.foundation.utility.*; import com.simibubi.create.foundation.utility.LerpedFloat.Chaser; -import com.simibubi.create.foundation.utility.Pair; -import com.simibubi.create.foundation.utility.Pointing; - import net.minecraft.client.ClipboardHelper; import net.minecraft.client.GameSettings; import net.minecraft.client.MainWindow; @@ -41,16 +28,28 @@ import net.minecraft.world.gen.feature.template.PlacementSettings; import net.minecraft.world.gen.feature.template.Template; import net.minecraftforge.fml.client.gui.GuiUtils; import net.minecraftforge.registries.ForgeRegistries; +import org.apache.commons.lang3.mutable.MutableBoolean; +import org.lwjgl.opengl.GL11; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.IntStream; public class PonderUI extends AbstractSimiScreen { public static final String PONDERING = PonderLocalization.LANG_PREFIX + "pondering"; public static final String IDENTIFY_MODE = PonderLocalization.LANG_PREFIX + "identify_mode"; + public static final String IN_CHAPTER = PonderLocalization.LANG_PREFIX + "in_chapter"; private List scenes; + private List tags; + private List tagButtons; + private List tagFades; private LerpedFloat fadeIn; private LerpedFloat sceneProgress; ItemStack stack; + PonderChapter chapter = null; private boolean identifyMode; private ItemStack hoveredTooltipItem; @@ -62,10 +61,24 @@ public class PonderUI extends AbstractSimiScreen { private LerpedFloat lazyIndex; private int index = 0; - private PonderButton left, right, icon, scan; + private PonderButton left, right, icon, scan, chap; + + public static PonderUI of(ItemStack item) { + return new PonderUI(PonderRegistry.compile(item.getItem().getRegistryName())); + } + + public static PonderUI of(PonderChapter chapter) { + PonderUI ui = new PonderUI(PonderRegistry.compile(chapter)); + ui.chapter = chapter; + return ui; + } public PonderUI(List scenes) { this.scenes = scenes; + if (scenes.isEmpty()) { + List l = Collections.singletonList(new PonderStoryBoardEntry(DebugScenes::empty, "debug/scene_1", new ResourceLocation("minecraft", "stick"))); + scenes.addAll(PonderRegistry.compile(l)); + } lazyIndex = LerpedFloat.linear() .startWithValue(index); sceneProgress = LerpedFloat.linear() @@ -87,14 +100,43 @@ public class PonderUI extends AbstractSimiScreen { else stack = new ItemStack(ForgeRegistries.BLOCKS.getValue(component)); - int bY = height - 20 - 31; + tags = new ArrayList<>(PonderRegistry.tags.getTags(component)); + tagButtons = new ArrayList<>(); + tagFades = new ArrayList<>(); + + + tags.forEach(t -> { + int i = tagButtons.size(); + int x = 31; + int y = 91 + i * 40; + PonderButton b = new PonderButton(x, y, () -> { + ScreenOpener.transitionTo(new PonderTagScreen(t)); + }) + .showing(t) + .fade(0, -1); + + widgets.add(b); + tagButtons.add(b); + + LerpedFloat chase = LerpedFloat.linear() + .startWithValue(0) + .chase(0, .05f, Chaser.exp(.1)); + tagFades.add(chase); + + }); + widgets.add(icon = new PonderButton(31, 31, () -> { - }).showing(stack) - .fade(0, -1)); + }).showing(stack).fade(0, -1)); + + if (chapter != null) { + widgets.add(chap = new PonderButton(width - 31 - 24, 31, () -> { + }).showing(chapter).fade(0, -1)); + } GameSettings bindings = minecraft.gameSettings; int spacing = 8; int bX = (width - 20) / 2 - (70 + 2 * spacing); + int bY = height - 20 - 31; widgets.add(scan = new PonderButton(bX, bY, () -> { identifyMode = !identifyMode; @@ -129,6 +171,7 @@ public class PonderUI extends AbstractSimiScreen { @Override public void tick() { + super.tick(); PonderScene activeScene = scenes.get(index); if (!identifyMode) activeScene.tick(); @@ -182,7 +225,7 @@ public class PonderUI extends AbstractSimiScreen { PonderWorld world = new PonderWorld(BlockPos.ZERO, Minecraft.getInstance().world); activeTemplate.addBlocksToWorld(world, BlockPos.ZERO, new PlacementSettings()); world.createBackup(); - scene = PonderRegistry.compileScene(scene.component, index, sb, world); + scene = PonderRegistry.compileScene(index, sb, world); scene.begin(); scenes.set(index, scene); } @@ -309,13 +352,30 @@ public class PonderUI extends AbstractSimiScreen { RenderSystem.translated(0, 0, 800); int x = icon.x + icon.getWidth() + 8; int y = icon.y; + + UIRenderHelper.streak(0, x - 4, y + 10, 26, (int) (150 * fade), 0x101010); + drawString(font, Lang.translate(PONDERING), x, y, 0xffa3a3a3); y += 12; x += 0; - RenderSystem.translated(0, 3 * (indexDiff), 0); - font.drawSplitString(activeScene.getTitle(), x, y, left.x - x, - ColorHelper.applyAlpha(textColor, 1 - indexDiff)); + //RenderSystem.translated(0, 3 * (indexDiff), 0); + RenderSystem.translated(x, y, 0); + RenderSystem.rotatef(indexDiff * -75, 1, 0, 0); + RenderSystem.translated(0, 0, 5); + font.drawSplitString(activeScene.getTitle(), 0, 0, left.x, ColorHelper.applyAlpha(textColor, 1 - indexDiff)); RenderSystem.popMatrix(); + + if (chapter != null) { + RenderSystem.pushMatrix(); + + RenderSystem.translated(chap.x - 4 - 4, chap.y, 0); + UIRenderHelper.streak(180, 4, 10, 26, (int) (150 * fade), 0x101010); + + drawRightAlignedString(font, Lang.translate(IN_CHAPTER), 0, 0, 0xffa3a3a3); + drawRightAlignedString(font, Lang.translate(PonderLocalization.LANG_PREFIX + "chapter." + chapter.getId()), 0, 12, 0xffeeeeee); + + RenderSystem.popMatrix(); + } } if (identifyMode) { @@ -387,6 +447,44 @@ public class PonderUI extends AbstractSimiScreen { GuiUtils.drawGradientRect(200, 0, 3, 1, 4, 0x60ffeedd, 0x60ffeedd); RenderSystem.popMatrix(); } + + //Tags + List sceneTags = activeScene.tags; + boolean highlightAll = sceneTags.contains(PonderTag.Highlight.ALL); + double s = Minecraft.getInstance().getWindow().getGuiScaleFactor(); + IntStream.range(0, tagButtons.size()).forEach(i -> { + RenderSystem.pushMatrix(); + LerpedFloat chase = tagFades.get(i); + PonderButton button = tagButtons.get(i); + if (button.isMouseOver(mouseX, mouseY)) { + chase.updateChaseTarget(1); + } else + chase.updateChaseTarget(0); + + chase.tickChaser(); + + if (highlightAll || sceneTags.contains(this.tags.get(i))) + button.flash(); + else + button.dim(); + + int x = button.x + button.getWidth() + 4; + int y = button.y - 2; + RenderSystem.translated(x, y + 5 * (1 - fade), 0); + + float fadedWidth = 200 * chase.getValue(partialTicks); + UIRenderHelper.streak(0, 0, 12, 26, (int) fadedWidth, 0x101010); + + GL11.glScissor((int) (x * s), 0, (int) (fadedWidth * s), (int) (height * s)); + GL11.glEnable(GL11.GL_SCISSOR_TEST); + + String tagName = Lang.translate("ponder.tag." + this.tags.get(i).getId()); + drawString(tagName, 3, 8, 0xffeedd); + + GL11.glDisable(GL11.GL_SCISSOR_TEST); + + RenderSystem.popMatrix(); + }); } protected void lowerButtonGroup(int index, int mouseX, int mouseY, float fade, AllIcons icon, KeyBinding key) { @@ -484,6 +582,14 @@ public class PonderUI extends AbstractSimiScreen { return super.keyPressed(code, p_keyPressed_2_, p_keyPressed_3_); } + @Override + protected String getBreadcrumbTitle() { + if (chapter != null) + return Lang.translate(PonderLocalization.LANG_PREFIX + "chapter." + chapter.getId()); + + return stack.getItem().getName().getFormattedText(); + } + public FontRenderer getFontRenderer() { return font; } diff --git a/src/main/java/com/simibubi/create/foundation/ponder/content/DebugScenes.java b/src/main/java/com/simibubi/create/foundation/ponder/content/DebugScenes.java index 714fde22f..e0a254a4a 100644 --- a/src/main/java/com/simibubi/create/foundation/ponder/content/DebugScenes.java +++ b/src/main/java/com/simibubi/create/foundation/ponder/content/DebugScenes.java @@ -4,19 +4,14 @@ import com.simibubi.create.AllBlocks; import com.simibubi.create.AllItems; import com.simibubi.create.content.contraptions.base.IRotate.SpeedLevel; import com.simibubi.create.content.contraptions.particle.RotationIndicatorParticleData; -import com.simibubi.create.foundation.ponder.ElementLink; -import com.simibubi.create.foundation.ponder.PonderRegistry; +import com.simibubi.create.foundation.ponder.*; import com.simibubi.create.foundation.ponder.PonderStoryBoardEntry.PonderStoryBoard; -import com.simibubi.create.foundation.ponder.SceneBuilder; -import com.simibubi.create.foundation.ponder.SceneBuildingUtil; -import com.simibubi.create.foundation.ponder.Selection; import com.simibubi.create.foundation.ponder.elements.BeltItemElement; import com.simibubi.create.foundation.ponder.elements.InputWindowElement; import com.simibubi.create.foundation.ponder.elements.WorldSectionElement; import com.simibubi.create.foundation.ponder.instructions.EmitParticlesInstruction.Emitter; import com.simibubi.create.foundation.utility.Pointing; import com.tterrag.registrate.util.entry.ItemEntry; - import net.minecraft.block.Blocks; import net.minecraft.entity.Entity; import net.minecraft.entity.item.ItemEntity; @@ -48,10 +43,18 @@ public class DebugScenes { private static void add(PonderStoryBoard sb) { ItemEntry item = AllItems.BRASS_HAND; String schematicPath = "debug/scene_" + index; - PonderRegistry.addStoryBoard(item, schematicPath, sb); + PonderRegistry.addStoryBoard(item, schematicPath, sb) + .highlightAllTags() + .chapter(PonderChapter.of("debug")); index++; } + public static void empty(SceneBuilder scene, SceneBuildingUtil util) { + scene.title("Missing Content"); + scene.showBasePlate(); + scene.idle(5); + } + public static void coordinateScene(SceneBuilder scene, SceneBuildingUtil util) { scene.title("Coordinate Space"); scene.showBasePlate(); diff --git a/src/main/java/com/simibubi/create/foundation/ponder/content/PonderChapter.java b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderChapter.java new file mode 100644 index 000000000..a128c6bfb --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderChapter.java @@ -0,0 +1,52 @@ +package com.simibubi.create.foundation.ponder.content; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.simibubi.create.Create; +import com.simibubi.create.foundation.gui.IScreenRenderable; +import com.simibubi.create.foundation.ponder.PonderRegistry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.AbstractGui; +import net.minecraft.util.ResourceLocation; + +import javax.annotation.Nonnull; + +public class PonderChapter implements IScreenRenderable { + + private final String id; + private final ResourceLocation icon; + + private PonderChapter(String id) { + this.id = id; + icon = new ResourceLocation(Create.ID, "textures/ponder/chapter/" + id + ".png"); + } + + @Override + public void draw(AbstractGui screen, int x, int y) { + RenderSystem.pushMatrix(); + Minecraft.getInstance().getTextureManager().bindTexture(icon); + RenderSystem.scaled(0.25, 0.25, 1); + //x and y offset, blit z offset, tex x and y, tex width and height, entire tex sheet width and height + AbstractGui.blit(x, y, 0, 0, 0, 64, 64, 64, 64); + RenderSystem.popMatrix(); + } + + @Nonnull + public static PonderChapter of(String id) { + PonderChapter chapter = PonderRegistry.chapters.getChapter(id); + if (chapter == null) { + chapter = PonderRegistry.chapters.addChapter(new PonderChapter(id)); + } + + return chapter; + } + + public PonderChapter addTagsToChapter(PonderTag... tags) { + for (PonderTag t : tags) + PonderRegistry.tags.add(t, this); + return this; + } + + public String getId() { + return id; + } +} diff --git a/src/main/java/com/simibubi/create/foundation/ponder/content/PonderChapterRegistry.java b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderChapterRegistry.java new file mode 100644 index 000000000..66da66763 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderChapterRegistry.java @@ -0,0 +1,49 @@ +package com.simibubi.create.foundation.ponder.content; + +import com.simibubi.create.foundation.ponder.PonderStoryBoardEntry; +import com.simibubi.create.foundation.utility.Pair; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; +import java.util.stream.Collectors; + +public class PonderChapterRegistry { + + private final Map>> chapters; + + public PonderChapterRegistry() { + chapters = new HashMap<>(); + } + + public void addStoriesToChapter(@Nonnull PonderChapter chapter, PonderStoryBoardEntry... entries) { + chapters.get(chapter.getId()).getSecond().addAll(Arrays.asList(entries)); + } + + PonderChapter addChapter(@Nonnull PonderChapter chapter) { + chapters.put(chapter.getId(), Pair.of(chapter, new ArrayList<>())); + return chapter; + } + + @Nullable + PonderChapter getChapter(String id) { + Pair> pair = chapters.get(id); + if (pair == null) + return null; + + return pair.getFirst(); + } + + public List getAllChapters() { + return chapters + .values() + .stream() + .map(Pair::getFirst) + .collect(Collectors.toList()); + } + + public List getStories(PonderChapter chapter) { + return chapters.get(chapter.getId()).getSecond(); + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/ponder/content/PonderIndex.java b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderIndex.java index f635b1e40..0e6b00894 100644 --- a/src/main/java/com/simibubi/create/foundation/ponder/content/PonderIndex.java +++ b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderIndex.java @@ -1,7 +1,10 @@ package com.simibubi.create.foundation.ponder.content; import com.simibubi.create.AllBlocks; +import com.simibubi.create.AllItems; import com.simibubi.create.foundation.ponder.PonderRegistry; +import net.minecraft.block.Blocks; +import net.minecraft.item.Items; public class PonderIndex { @@ -13,8 +16,8 @@ public class PonderIndex { // (!) Modifications inside storyboard methods only require re-opening the ui PonderRegistry.forComponents(AllBlocks.SHAFT) - .addStoryBoard("shaft/relay", KineticsScenes::shaftAsRelay) - .addStoryBoard("shaft/encasing", KineticsScenes::shaftsCanBeEncased); + .addStoryBoard("shaft/relay", KineticsScenes::shaftAsRelay, b -> b.highlightAllTags().chapter(PonderChapter.of("basic_kinetics"))) + .addStoryBoard("shaft/encasing", KineticsScenes::shaftsCanBeEncased, b -> b.chapter(PonderChapter.of("encasing"))); // Funnels PonderRegistry.addStoryBoard(AllBlocks.BRASS_FUNNEL, "funnels/brass", FunnelScenes::brass); @@ -39,4 +42,30 @@ public class PonderIndex { DebugScenes.registerAll(); } + public static void registerTags() { + + PonderRegistry.tags.forItems(AllBlocks.SHAFT.getId()) + .add(PonderTag.Create.KINETICS); + + PonderRegistry.tags.forItems(AllBlocks.ANDESITE_FUNNEL.getId(), AllBlocks.BRASS_FUNNEL.getId()) + .add(PonderTag.Create.ARM_ACCESS) + .add(PonderTag.Vanilla.ITEM_TRANSFER) + .add(PonderTag.Vanilla.REDSTONE_CONTROL); + + PonderRegistry.tags.forTag(PonderTag.Vanilla.REDSTONE_CONTROL) + .add(Items.REDSTONE.getRegistryName()) + .add(Blocks.LEVER.getRegistryName()); + + PonderRegistry.tags.forTag(PonderTag.Create.KINETICS) + .add(AllBlocks.COGWHEEL.getId()) + .add(AllBlocks.LARGE_COGWHEEL.getId()) + .add(AllItems.BELT_CONNECTOR.getId()) + .add(AllBlocks.ENCASED_CHAIN_DRIVE.getId()); + + PonderChapter.of("basic_kinetics").addTagsToChapter( + PonderTag.Create.KINETICS + ); + + } + } diff --git a/src/main/java/com/simibubi/create/foundation/ponder/content/PonderIndexScreen.java b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderIndexScreen.java new file mode 100644 index 000000000..542148e96 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderIndexScreen.java @@ -0,0 +1,170 @@ +package com.simibubi.create.foundation.ponder.content; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.simibubi.create.foundation.gui.AbstractSimiScreen; +import com.simibubi.create.foundation.gui.ScreenOpener; +import com.simibubi.create.foundation.gui.UIRenderHelper; +import com.simibubi.create.foundation.ponder.PonderRegistry; +import com.simibubi.create.foundation.ponder.PonderUI; +import com.simibubi.create.foundation.ponder.ui.ChapterLabel; +import com.simibubi.create.foundation.ponder.ui.LayoutHelper; +import com.simibubi.create.foundation.ponder.ui.PonderButton; +import net.minecraft.block.Block; +import net.minecraft.client.MainWindow; +import net.minecraft.client.gui.widget.Widget; +import net.minecraft.client.renderer.Rectangle2d; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.MathHelper; +import net.minecraftforge.registries.ForgeRegistries; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class PonderIndexScreen extends AbstractSimiScreen { + + protected final List chapters; + private final double chapterXmult = 0.5; + private final double chapterYmult = 0.3; + protected Rectangle2d chapterArea; + + protected final List items; + private final double itemXmult = 0.5; + private final double itemYmult = 0.75; + protected Rectangle2d itemArea; + + private ItemStack hoveredItem = ItemStack.EMPTY; + + public PonderIndexScreen() { + chapters = new ArrayList<>(); + items = new ArrayList<>(); + } + + @Override + protected void init() { + super.init(); + + widgets.clear(); + + chapters.clear(); + chapters.addAll(PonderRegistry.chapters.getAllChapters()); + + LayoutHelper layout = LayoutHelper.centeredHorizontal( + chapters.size(), + MathHelper.clamp((int) Math.ceil(chapters.size() / 4f), 1, 4), + 200, + 38, + 16 + ); + chapterArea = layout.getArea(); + int chapterCenterX = (int) (width * chapterXmult); + int chapterCenterY = (int) (height * chapterYmult); + + //todo at some point pagination or horizontal scrolling may be needed for chapters/items + for (PonderChapter chapter : chapters) { + ChapterLabel label = new ChapterLabel(chapter, chapterCenterX + layout.getX(), chapterCenterY + layout.getY(), () -> { + ScreenOpener.transitionTo(PonderUI.of(chapter)); + }); + + widgets.add(label); + layout.next(); + } + + items.clear(); + PonderRegistry.all.keySet() + .stream() + .map(key -> { + Item item = ForgeRegistries.ITEMS.getValue(key); + if (item == null) { + Block b = ForgeRegistries.BLOCKS.getValue(key); + if (b != null) + item = b.asItem(); + } + return item; + }) + .filter(Objects::nonNull) + .forEach(items::add); + + layout = LayoutHelper.centeredHorizontal( + items.size(), + MathHelper.clamp((int) Math.ceil(items.size() / 11f), 1, 4), + 28, + 28, + 8 + ); + itemArea = layout.getArea(); + int itemCenterX = (int) (width * itemXmult); + int itemCenterY = (int) (height * itemYmult); + + for (Item item : items) { + PonderButton button = new PonderButton(itemCenterX + layout.getX() + 4, itemCenterY + layout.getY() + 4, () -> {}) + .showing(new ItemStack(item)); + + button.fade(1); + widgets.add(button); + layout.next(); + } + + + } + + @Override + public void tick() { + super.tick(); + + hoveredItem = ItemStack.EMPTY; + MainWindow w = minecraft.getWindow(); + double mouseX = minecraft.mouseHelper.getMouseX() * w.getScaledWidth() / w.getWidth(); + double mouseY = minecraft.mouseHelper.getMouseY() * w.getScaledHeight() / w.getHeight(); + for (Widget widget : widgets) { + if (widget instanceof PonderButton) + if (widget.isMouseOver(mouseX, mouseY)) { + hoveredItem = ((PonderButton) widget).getItem(); + } + } + } + + @Override + protected void renderWindow(int mouseX, int mouseY, float partialTicks) { + int x = (int) (width * chapterXmult); + int y = (int) (height * chapterYmult); + + RenderSystem.pushMatrix(); + RenderSystem.translated(x, y, 0); + + UIRenderHelper.streak(0, chapterArea.getX() - 10, chapterArea.getY() - 20, 20, 220, 0x101010); + drawString(font, "Topics to Ponder about", chapterArea.getX() - 5, chapterArea.getY() - 25, 0xffddeeff); + + RenderSystem.popMatrix(); + + + x = (int) (width * itemXmult); + y = (int) (height * itemYmult); + + RenderSystem.pushMatrix(); + RenderSystem.translated(x, y, 0); + + UIRenderHelper.streak(0, itemArea.getX() - 10, itemArea.getY() - 20, 20, 220, 0x101010); + drawString(font, "Items to inspect", itemArea.getX() - 5, itemArea.getY() - 25, 0xffddeeff); + + RenderSystem.popMatrix(); + } + + @Override + protected void renderWindowForeground(int mouseX, int mouseY, float partialTicks) { + if (hoveredItem.isEmpty()) + return; + + RenderSystem.pushMatrix(); + RenderSystem.translated(0, 0, 200); + + renderTooltip(hoveredItem, mouseX, mouseY); + + RenderSystem.popMatrix(); + } + + public ItemStack getHoveredTooltipItem() { + return hoveredItem; + } +} diff --git a/src/main/java/com/simibubi/create/foundation/ponder/content/PonderTag.java b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderTag.java new file mode 100644 index 000000000..c960b8f01 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderTag.java @@ -0,0 +1,88 @@ +package com.simibubi.create.foundation.ponder.content; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.simibubi.create.AllBlocks; +import com.simibubi.create.foundation.gui.GuiGameElement; +import com.simibubi.create.foundation.gui.IScreenRenderable; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.AbstractGui; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +public class PonderTag implements IScreenRenderable { + + private final String id; + private ResourceLocation icon; + private ItemStack itemIcon = ItemStack.EMPTY; + private ItemStack mainItem = ItemStack.EMPTY; + + public PonderTag(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public ItemStack getMainItem() { + return mainItem; + } + + public PonderTag idAsIcon() { + return icon(id); + } + + public PonderTag icon(String location) { + this.icon = new ResourceLocation(com.simibubi.create.Create.ID, "textures/ponder/tag/" + location + ".png"); + return this; + } + + public PonderTag item(Item item) { + return this.item(item, true, true); + } + + public PonderTag item(Item item, boolean useAsIcon, boolean useAsMainItem) { + if (useAsIcon) this.itemIcon = new ItemStack(item); + if (useAsMainItem) this.mainItem = new ItemStack(item); + return this; + } + + @Override + @OnlyIn(Dist.CLIENT) + public void draw(AbstractGui screen, int x, int y) { + RenderSystem.pushMatrix(); + RenderSystem.translated(x, y, 0); + if (icon != null) { + Minecraft.getInstance().getTextureManager().bindTexture(icon); + RenderSystem.scaled(0.25, 0.25, 1); + //x and y offset, blit z offset, tex x and y, tex width and height, entire tex sheet width and height + AbstractGui.blit(0, 0, 0, 0, 0, 64, 64, 64, 64); + } else if (!itemIcon.isEmpty()) { + RenderSystem.translated(-4, -4, 0); + RenderSystem.scaled(1.5, 1.5, 1); + GuiGameElement.of(itemIcon).render(); + } + RenderSystem.popMatrix(); + } + + public static class Create { + public static final PonderTag KINETICS = new PonderTag("kinetics").item(AllBlocks.COGWHEEL.get().asItem(), true, false); + public static final PonderTag FLUID_TRANSFER = new PonderTag("fluid_transfer").idAsIcon(); + + public static final PonderTag OPEN_INVENTORY = new PonderTag("open_inventory").item(AllBlocks.BASIN.get().asItem()); + public static final PonderTag ARM_ACCESS = new PonderTag("arm_access").item(AllBlocks.MECHANICAL_ARM.get().asItem()); + } + + public static class Vanilla { + public static final PonderTag REDSTONE_CONTROL = new PonderTag("redstone_control").item(Items.REDSTONE, true, false); + public static final PonderTag ITEM_TRANSFER = new PonderTag("item_transfer").idAsIcon(); + } + + public static class Highlight { + public static final PonderTag ALL = new PonderTag("_all"); + } +} diff --git a/src/main/java/com/simibubi/create/foundation/ponder/content/PonderTagRegistry.java b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderTagRegistry.java new file mode 100644 index 000000000..98f897a8c --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderTagRegistry.java @@ -0,0 +1,94 @@ +package com.simibubi.create.foundation.ponder.content; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; +import com.simibubi.create.foundation.ponder.PonderRegistry; +import net.minecraft.util.ResourceLocation; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +public class PonderTagRegistry { + + private final Multimap tags; + private final Multimap chapterTags; + + public PonderTagRegistry() { + tags = LinkedHashMultimap.create(); + chapterTags = LinkedHashMultimap.create(); + } + + public Set getTags(ResourceLocation item) { + return ImmutableSet.copyOf(tags.get(item)); + } + + public Set getTags(PonderChapter chapter) { + return ImmutableSet.copyOf(chapterTags.get(chapter)); + } + + public Set getItems(PonderTag tag) { + return tags + .entries() + .stream() + .filter(e -> e.getValue() == tag) + .map(Map.Entry::getKey) + .collect(ImmutableSet.toImmutableSet()); + } + + public Set getChapters(PonderTag tag) { + return chapterTags + .entries() + .stream() + .filter(e -> e.getValue() == tag) + .map(Map.Entry::getKey) + .collect(ImmutableSet.toImmutableSet()); + } + + public void add(PonderTag tag, ResourceLocation item) { + tags.put(item, tag); + } + + public void add(PonderTag tag, PonderChapter chapter) { + chapterTags.put(chapter, tag); + } + + public ItemBuilder forItems(ResourceLocation... items) { + return new ItemBuilder(items); + } + + public TagBuilder forTag(PonderTag tag) { + return new TagBuilder(tag); + } + + public static class ItemBuilder { + + private final Collection items; + + private ItemBuilder(ResourceLocation... items) { + this.items = Arrays.asList(items); + } + + public ItemBuilder add(PonderTag tag) { + items.forEach(i -> PonderRegistry.tags.add(tag, i)); + return this; + } + + } + + public static class TagBuilder { + + private final PonderTag tag; + + private TagBuilder(PonderTag tag) { + this.tag = tag; + } + + public TagBuilder add(ResourceLocation item) { + PonderRegistry.tags.add(tag, item); + return this; + } + } +} diff --git a/src/main/java/com/simibubi/create/foundation/ponder/content/PonderTagScreen.java b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderTagScreen.java new file mode 100644 index 000000000..1281a1ba0 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderTagScreen.java @@ -0,0 +1,231 @@ +package com.simibubi.create.foundation.ponder.content; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.simibubi.create.foundation.gui.AbstractSimiScreen; +import com.simibubi.create.foundation.gui.GuiGameElement; +import com.simibubi.create.foundation.gui.ScreenOpener; +import com.simibubi.create.foundation.gui.UIRenderHelper; +import com.simibubi.create.foundation.ponder.PonderRegistry; +import com.simibubi.create.foundation.ponder.PonderUI; +import com.simibubi.create.foundation.ponder.ui.ChapterLabel; +import com.simibubi.create.foundation.ponder.ui.LayoutHelper; +import com.simibubi.create.foundation.ponder.ui.PonderButton; +import com.simibubi.create.foundation.utility.Lang; +import net.minecraft.block.Block; +import net.minecraft.client.MainWindow; +import net.minecraft.client.gui.widget.Widget; +import net.minecraft.client.renderer.Rectangle2d; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.MathHelper; +import net.minecraftforge.registries.ForgeRegistries; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class PonderTagScreen extends AbstractSimiScreen { + + protected final PonderTag tag; + protected final List items; + private final double itemXmult = 0.5; + private final double itemYmult = 0.4; + protected Rectangle2d itemArea; + protected final List chapters; + private final double chapterXmult = 0.5; + private final double chapterYmult = 0.75; + protected Rectangle2d chapterArea; + private final double mainXmult = 0.5; + private final double mainYmult = 0.15; + + private ItemStack hoveredItem = ItemStack.EMPTY; + + + public PonderTagScreen(PonderTag tag) { + this.tag = tag; + items = new ArrayList<>(); + chapters = new ArrayList<>(); + } + + @Override + protected void init() { + super.init(); + widgets.clear(); + + //items + items.clear(); + PonderRegistry.tags.getItems(tag) + .stream() + .map(key -> { + Item item = ForgeRegistries.ITEMS.getValue(key); + if (item == null) { + Block b = ForgeRegistries.BLOCKS.getValue(key); + if (b != null) + item = b.asItem(); + } + return item; + }) + .filter(Objects::nonNull) + .forEach(items::add); + + int rowCount = MathHelper.clamp((int) Math.ceil(items.size() / 11d), 1, 3); + LayoutHelper layout = LayoutHelper.centeredHorizontal(items.size(), rowCount, 28, 28, 8); + itemArea = layout.getArea(); + int itemCenterX = (int) (width * itemXmult); + int itemCenterY = (int) (height * itemYmult); + + for (Item i : items) { + PonderButton button = new PonderButton(itemCenterX + layout.getX() + 4, itemCenterY + layout.getY() + 4, () -> {}) + .showing(new ItemStack(i)); + + button.fade(1); + widgets.add(button); + layout.next(); + } + + //chapters + chapters.clear(); + chapters.addAll(PonderRegistry.tags.getChapters(tag)); + + rowCount = MathHelper.clamp((int) Math.ceil(chapters.size() / 3f), 1, 3); + layout = LayoutHelper.centeredHorizontal(chapters.size(), rowCount, 200, 38, 16); + chapterArea = layout.getArea(); + int chapterCenterX = (int) (width * chapterXmult); + int chapterCenterY = (int) (height * chapterYmult); + + for (PonderChapter chapter : chapters) { + ChapterLabel label = new ChapterLabel(chapter, chapterCenterX + layout.getX(), chapterCenterY + layout.getY(), () -> { + ScreenOpener.transitionTo(PonderUI.of(chapter)); + }); + + widgets.add(label); + layout.next(); + } + + } + + @Override + public void tick() { + super.tick(); + + hoveredItem = ItemStack.EMPTY; + MainWindow w = minecraft.getWindow(); + double mouseX = minecraft.mouseHelper.getMouseX() * w.getScaledWidth() / w.getWidth(); + double mouseY = minecraft.mouseHelper.getMouseY() * w.getScaledHeight() / w.getHeight(); + for (Widget widget : widgets) { + if (widget instanceof PonderButton) + if (widget.isMouseOver(mouseX, mouseY)) { + hoveredItem = ((PonderButton) widget).getItem(); + } + } + } + + @Override + protected void renderWindow(int mouseX, int mouseY, float partialTicks) { + renderItems(mouseX, mouseY, partialTicks); + + renderChapters(mouseX, mouseY, partialTicks); + + // + int x = (int) (width * mainXmult); + int y = (int) (height * mainYmult); + + RenderSystem.pushMatrix(); + RenderSystem.translated(x, y, 0); + RenderSystem.translated(-150, 0, 0); + + if (!tag.getMainItem().isEmpty()) { + RenderSystem.translated(-25, 0, 0); + PonderUI.renderBox(0, -10, 20, 20, false); + RenderSystem.pushMatrix(); + RenderSystem.translated(-2, -12, 0); + RenderSystem.scaled(1.5, 1.5, 1); + GuiGameElement.of(tag.getMainItem()).render(); + + RenderSystem.popMatrix(); + + RenderSystem.translated(75, 0, 0); + + } + + RenderSystem.pushMatrix(); + RenderSystem.scaled(1.5, 1.5, 1); + + + //render icon & box + PonderUI.renderBox(0, -10, 20, 20, true); + RenderSystem.translated(2, 2 - 10, 100); + tag.draw(this, 0, 0); + + RenderSystem.popMatrix(); + + //tag name & description + UIRenderHelper.streak(0, 36, 0, 39, 350, 0x101010); + drawString(font, Lang.translate("ponder.tag." + tag.getId()), 41, -16, 0xffff_ffff); + drawString(font, Lang.translate("ponder.tag." + tag.getId() + ".desc"), 41, -4, 0xffff_ffff); + + RenderSystem.popMatrix(); + + } + + protected void renderItems(int mouseX, int mouseY, float partialTicks) { + if (items.isEmpty()) + return; + + int x = (int) (width * itemXmult); + int y = (int) (height * itemYmult); + + RenderSystem.pushMatrix(); + RenderSystem.translated(x, y, 0); + + UIRenderHelper.streak(0, itemArea.getX() - 10, itemArea.getY() - 20, 20, 180, 0x101010); + drawString(font, "Related Items", itemArea.getX() - 5, itemArea.getY() - 25, 0xffddeeff); + + UIRenderHelper.streak(0, 0, 0, itemArea.getHeight() + 10, itemArea.getWidth()/2 + 75, 0x101010); + UIRenderHelper.streak(180, 0, 0, itemArea.getHeight() + 10, itemArea.getWidth()/2 + 75, 0x101010); + + RenderSystem.popMatrix(); + + } + + protected void renderChapters(int mouseX, int mouseY, float partialTicks) { + if (chapters.isEmpty()) + return; + + int chapterX = (int) (width * chapterXmult); + int chapterY = (int) (height * chapterYmult); + + RenderSystem.pushMatrix(); + RenderSystem.translated(chapterX, chapterY, 0); + + UIRenderHelper.streak(0, chapterArea.getX() - 10, chapterArea.getY() - 20, 20, 220, 0x101010); + drawString(font, "More Topics to Ponder about", chapterArea.getX() - 5, chapterArea.getY() - 25, 0xffddeeff); + + RenderSystem.popMatrix(); + } + + @Override + protected void renderWindowForeground(int mouseX, int mouseY, float partialTicks) { + RenderSystem.pushMatrix(); + RenderSystem.disableRescaleNormal(); + RenderSystem.disableDepthTest(); + + RenderSystem.translated(0, 0, 200); + if (!hoveredItem.isEmpty()) { + renderTooltip(hoveredItem, mouseX, mouseY); + } + RenderSystem.enableDepthTest(); + RenderSystem.enableRescaleNormal(); + RenderSystem.popMatrix(); + } + + @Override + protected String getBreadcrumbTitle() { + return Lang.translate("ponder.tag." + tag.getId()); + } + + public ItemStack getHoveredTooltipItem() { + return hoveredItem; + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/ponder/ui/ChapterLabel.java b/src/main/java/com/simibubi/create/foundation/ponder/ui/ChapterLabel.java new file mode 100644 index 000000000..a912f324a --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/ponder/ui/ChapterLabel.java @@ -0,0 +1,39 @@ +package com.simibubi.create.foundation.ponder.ui; + +import com.simibubi.create.foundation.gui.UIRenderHelper; +import com.simibubi.create.foundation.gui.widgets.AbstractSimiWidget; +import com.simibubi.create.foundation.ponder.content.PonderChapter; +import com.simibubi.create.foundation.utility.Lang; +import net.minecraft.client.Minecraft; + +public class ChapterLabel extends AbstractSimiWidget { + + private final PonderChapter chapter; + private final PonderButton button; + + public ChapterLabel(PonderChapter chapter, int x, int y, Runnable onClick) { + super(x, y, 175, 38); + + this.button = new PonderButton(x + 4, y + 4, onClick, 30, 30).showing(chapter); + this.button.fade(1); + + this.chapter = chapter; + } + + @Override + public void render(int mouseX, int mouseY, float partialTicks) { + UIRenderHelper.streak(0, x, y + height/2, height - 2, width, 0x101010); + drawString(Minecraft.getInstance().fontRenderer, Lang.translate("ponder.chapter." + chapter.getId()), x + 50, y + 20, 0xffddeeff); + + button.renderButton(mouseX, mouseY, partialTicks); + super.render(mouseX, mouseY, partialTicks); + } + + @Override + public void onClick(double x, double y) { + if (!button.isMouseOver(x, y)) + return; + + button.runCallback(); + } +} diff --git a/src/main/java/com/simibubi/create/foundation/ponder/ui/LayoutHelper.java b/src/main/java/com/simibubi/create/foundation/ponder/ui/LayoutHelper.java new file mode 100644 index 000000000..97c472072 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/ponder/ui/LayoutHelper.java @@ -0,0 +1,126 @@ +package com.simibubi.create.foundation.ponder.ui; + +import net.minecraft.client.renderer.Rectangle2d; + +public interface LayoutHelper { + + static LayoutHelper centeredHorizontal(int itemCount, int rows, int width, int height, int spacing) { + return new CenteredHorizontalLayoutHelper(itemCount, rows, width, height, spacing); + } + + int getX(); + + int getY(); + + void next(); + + int getTotalWidth(); + + int getTotalHeight(); + + default Rectangle2d getArea() { + int lWidth = getTotalWidth(); + int lHeight = getTotalHeight(); + return new Rectangle2d( + -lWidth/2, + -lHeight/2, + lWidth, + lHeight + ); + } + + class CenteredHorizontalLayoutHelper implements LayoutHelper { + + int itemCount; + int rows; + int width; + int height; + int spacing; + + int currentColumn = 0; + int currentRow = 0; + int[] rowCounts; + int x = 0, y = 0; + + CenteredHorizontalLayoutHelper(int itemCount, int rows, int width, int height, int spacing) { + this.itemCount = itemCount; + this.rows = rows; + this.width = width; + this.height = height; + this.spacing = spacing; + + rowCounts = new int[rows]; + int itemsPerRow = itemCount / rows; + int itemDiff = itemCount - itemsPerRow * rows; + for (int i = 0; i < rows; i++) { + rowCounts[i] = itemsPerRow; + if (itemDiff > 0) { + rowCounts[i]++; + itemDiff--; + } + } + + init(); + } + + @Override + public int getX() { + return x; + } + + @Override + public int getY() { + return y; + } + + @Override + public void next() { + currentColumn++; + if (currentColumn >= rowCounts[currentRow]) { + //nextRow + if (++currentRow >= rows) { + x = 0; + y = 0; + return; + } + + currentColumn = 0; + prepareX(); + y += height + spacing; + return; + } + + x += width + spacing; + } + + private void init() { + prepareX(); + prepareY(); + + } + + + private void prepareX() { + int rowWidth = rowCounts[currentRow] * width + (rowCounts[currentRow] - 1) * spacing; + x = -(rowWidth / 2); + } + + private void prepareY() { + int totalHeight = rows * height + (rows > 1 ? ((rows - 1) * spacing) : 0); + y = -(totalHeight / 2); + } + + @Override + public int getTotalWidth() { + return rowCounts[0] * width + (rowCounts[0] - 1) * spacing; + } + + @Override + public int getTotalHeight() { + return rows * height + (rows > 1 ? ((rows - 1) * spacing) : 0); + } + + + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/ponder/ui/PonderButton.java b/src/main/java/com/simibubi/create/foundation/ponder/ui/PonderButton.java index 9ff50f0e7..8ba55e22a 100644 --- a/src/main/java/com/simibubi/create/foundation/ponder/ui/PonderButton.java +++ b/src/main/java/com/simibubi/create/foundation/ponder/ui/PonderButton.java @@ -1,21 +1,20 @@ package com.simibubi.create.foundation.ponder.ui; import com.mojang.blaze3d.systems.RenderSystem; -import com.simibubi.create.foundation.gui.AllIcons; import com.simibubi.create.foundation.gui.GuiGameElement; +import com.simibubi.create.foundation.gui.IScreenRenderable; import com.simibubi.create.foundation.gui.widgets.AbstractSimiWidget; import com.simibubi.create.foundation.ponder.PonderUI; import com.simibubi.create.foundation.utility.AnimationTickHolder; import com.simibubi.create.foundation.utility.ColorHelper; import com.simibubi.create.foundation.utility.LerpedFloat; - import net.minecraft.client.Minecraft; import net.minecraft.client.settings.KeyBinding; import net.minecraft.item.ItemStack; public class PonderButton extends AbstractSimiWidget { - private AllIcons icon; + private IScreenRenderable icon; private ItemStack item; protected boolean pressed; private Runnable onClick; @@ -27,14 +26,17 @@ public class PonderButton extends AbstractSimiWidget { public static final int SIZE = 20; - public PonderButton(int x, int y, Runnable onClick) { - super(x, y, SIZE, SIZE); + public PonderButton(int x, int y, Runnable onClick, int width, int height) { + super(x, y, width, height); this.onClick = onClick; - flash = LerpedFloat.linear() - .startWithValue(0); + flash = LerpedFloat.linear().startWithValue(0); } - public PonderButton showing(AllIcons icon) { + public PonderButton(int x, int y, Runnable onClick) { + this(x, y, onClick, SIZE, SIZE); + } + + public PonderButton showing(IScreenRenderable icon) { this.icon = icon; return this; } @@ -97,17 +99,24 @@ public class PonderButton extends AbstractSimiWidget { if (icon != null) { RenderSystem.enableBlend(); RenderSystem.color4f(1, 1, 1, fade); - icon.draw(this, x + 2, y + 2); + RenderSystem.pushMatrix(); + RenderSystem.translated(x + 2, y + 2, 0); + RenderSystem.scaled((width - 4) / 16d, (height - 4) / 16d, 1); + icon.draw(this, 0, 0); + RenderSystem.popMatrix(); } if (item != null) { + RenderSystem.pushMatrix(); + RenderSystem.translated(0, 0, -800); GuiGameElement.of(item) .at(x - 2, y - 2) .scale(1.5f) .render(); + RenderSystem.popMatrix(); } if (shortcut != null) - drawCenteredString(Minecraft.getInstance().fontRenderer, shortcut.getLocalizedName(), x + SIZE / 2 + 8, - y + SIZE - 6, ColorHelper.applyAlpha(0xff606060, fade)); + drawCenteredString(Minecraft.getInstance().fontRenderer, shortcut.getLocalizedName(), x + width / 2 + 8, + y + height - 6, ColorHelper.applyAlpha(0xff606060, fade)); RenderSystem.popMatrix(); } @@ -133,4 +142,7 @@ public class PonderButton extends AbstractSimiWidget { toolTip.add(text); } + public ItemStack getItem() { + return item; + } } diff --git a/src/main/java/com/simibubi/create/foundation/utility/LerpedFloat.java b/src/main/java/com/simibubi/create/foundation/utility/LerpedFloat.java index 073f2fc07..4c7b4fec4 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/LerpedFloat.java +++ b/src/main/java/com/simibubi/create/foundation/utility/LerpedFloat.java @@ -46,6 +46,10 @@ public class LerpedFloat { return this; } + public void updateChaseTarget(float target) { + this.chaseTarget = target; + } + public boolean updateChaseSpeed(double speed) { float prevSpeed = this.chaseSpeed; this.chaseSpeed = (float) speed; diff --git a/src/main/resources/assets/create/textures/ponder/chapter/basic_kinetics.png b/src/main/resources/assets/create/textures/ponder/chapter/basic_kinetics.png new file mode 100644 index 0000000000000000000000000000000000000000..da70cc75afa6b78cb7d32483638205385ea81239 GIT binary patch literal 7389 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/create/textures/ponder/tag/item_transfer.png b/src/main/resources/assets/create/textures/ponder/tag/item_transfer.png new file mode 100644 index 0000000000000000000000000000000000000000..da70cc75afa6b78cb7d32483638205385ea81239 GIT binary patch literal 7389 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 literal 0 HcmV?d00001