From 03d25b0ab5ef2b324eac886dfb488b0559a0e5aa Mon Sep 17 00:00:00 2001 From: TechPizza <23627133+TechPizzaDev@users.noreply.github.com> Date: Tue, 22 Aug 2023 00:45:39 +0200 Subject: [PATCH] Added custom scrollable widget All vanilla widgets use this through MixinScrollableBaseWidget --- .../dev/adventurecraft/awakening/MathF.java | 24 ++ .../awakening/common/AC_CutsceneCamera.java | 35 +- .../awakening/common/AC_PlayerTorch.java | 6 +- .../awakening/common/ScrollableWidget.java | 395 ++++++++++++++++++ .../extension/client/ExMinecraft.java | 2 +- .../gui/widget/ExScrollableBaseWidget.java | 6 + .../mixin/client/MixinMinecraft.java | 63 +-- .../gui/screen/MixinSelectWorldScreen.java | 19 +- .../gui/screen/ingame/MixinStatsScreen.java | 21 + .../gui/widget/MixinScrollableBaseWidget.java | 152 +++++++ .../client/render/MixinGameRenderer.java | 6 +- .../resources/adventurecraft.accesswidener | 2 + src/main/resources/adventurecraft.mixins.json | 2 + 13 files changed, 653 insertions(+), 80 deletions(-) create mode 100644 src/main/java/dev/adventurecraft/awakening/MathF.java create mode 100644 src/main/java/dev/adventurecraft/awakening/common/ScrollableWidget.java create mode 100644 src/main/java/dev/adventurecraft/awakening/extension/client/gui/widget/ExScrollableBaseWidget.java create mode 100644 src/main/java/dev/adventurecraft/awakening/mixin/client/gui/screen/ingame/MixinStatsScreen.java create mode 100644 src/main/java/dev/adventurecraft/awakening/mixin/client/gui/widget/MixinScrollableBaseWidget.java diff --git a/src/main/java/dev/adventurecraft/awakening/MathF.java b/src/main/java/dev/adventurecraft/awakening/MathF.java new file mode 100644 index 00000000..8b0656aa --- /dev/null +++ b/src/main/java/dev/adventurecraft/awakening/MathF.java @@ -0,0 +1,24 @@ +package dev.adventurecraft.awakening; + +public final class MathF { + + public static float cubicInterpolation(float mu, float y0, float y1, float y2, float y3) { + float mu2 = mu * mu; + float a0 = -0.5F * y0 + 1.5F * y1 - 1.5F * y2 + 0.5F * y3; + float a1 = y0 - 2.5F * y1 + 2.0F * y2 - 0.5F * y3; + float a2 = -0.5F * y0 + 0.5F * y2; + return a0 * mu * mu2 + a1 * mu2 + a2 * mu + y1; + } + + public static float lerp(float amount, float start, float end) { + return (1.0F - amount) * start + amount * end; + } + + public static double lerp(double amount, double start, double end) { + return (1.0 - amount) * start + amount * end; + } + + public static double round(double value, int decimals) { + return Math.round(value * decimals) / (double) decimals; + } +} diff --git a/src/main/java/dev/adventurecraft/awakening/common/AC_CutsceneCamera.java b/src/main/java/dev/adventurecraft/awakening/common/AC_CutsceneCamera.java index 0a29f103..1a424475 100644 --- a/src/main/java/dev/adventurecraft/awakening/common/AC_CutsceneCamera.java +++ b/src/main/java/dev/adventurecraft/awakening/common/AC_CutsceneCamera.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.List; +import dev.adventurecraft.awakening.MathF; import net.minecraft.client.Minecraft; import net.minecraft.client.entity.player.AbstractClientPlayerEntity; import net.minecraft.client.render.Tessellator; @@ -65,7 +66,7 @@ public void loadCameraEntities() { if (prevPoint != null) { for (int i = 0; i < 25; ++i) { float currTime = (float) (i + 1) / 25.0F; - float nextTime = this.lerp(currTime, prevPoint.time, point.time); + float nextTime = MathF.lerp(currTime, prevPoint.time, point.time); AC_CutsceneCameraPoint nextPoint = camera.getPoint(nextTime); Vec3d linePoint = Vec3d.create(nextPoint.posX, nextPoint.posY, nextPoint.posZ); this.linePoints.add(linePoint); @@ -155,18 +156,6 @@ public void startCamera() { this.startTime = Minecraft.instance.world.getWorldTime(); } - private float cubicInterpolation(float mu, float y0, float y1, float y2, float y3) { - float mu2 = mu * mu; - float a0 = -0.5F * y0 + 1.5F * y1 - 1.5F * y2 + 0.5F * y3; - float a1 = y0 - 2.5F * y1 + 2.0F * y2 - 0.5F * y3; - float a2 = -0.5F * y0 + 0.5F * y2; - return a0 * mu * mu2 + a1 * mu2 + a2 * mu + y1; - } - - private float lerp(float amount, float start, float end) { - return (1.0F - amount) * start + amount * end; - } - public boolean isEmpty() { return this.cameraPoints.isEmpty(); } @@ -290,20 +279,20 @@ public AC_CutsceneCameraPoint getPoint(float time) { switch (this.prevPoint.blendType) { case LINEAR: - this.curPoint.posX = this.lerp(amount, this.prevPoint.posX, point.posX); - this.curPoint.posY = this.lerp(amount, this.prevPoint.posY, point.posY); - this.curPoint.posZ = this.lerp(amount, this.prevPoint.posZ, point.posZ); - this.curPoint.rotYaw = this.lerp(amount, this.prevPoint.rotYaw, point.rotYaw); - this.curPoint.rotPitch = this.lerp(amount, this.prevPoint.rotPitch, point.rotPitch); + this.curPoint.posX = MathF.lerp(amount, this.prevPoint.posX, point.posX); + this.curPoint.posY = MathF.lerp(amount, this.prevPoint.posY, point.posY); + this.curPoint.posZ = MathF.lerp(amount, this.prevPoint.posZ, point.posZ); + this.curPoint.rotYaw = MathF.lerp(amount, this.prevPoint.rotYaw, point.rotYaw); + this.curPoint.rotPitch = MathF.lerp(amount, this.prevPoint.rotPitch, point.rotPitch); break; case QUADRATIC: //noinspection SuspiciousNameCombination - this.curPoint.posX = this.cubicInterpolation(amount, this.prevPrevPoint.posX, this.prevPoint.posX, point.posX, nextPoint.posX); - this.curPoint.posY = this.cubicInterpolation(amount, this.prevPrevPoint.posY, this.prevPoint.posY, point.posY, nextPoint.posY); - this.curPoint.posZ = this.cubicInterpolation(amount, this.prevPrevPoint.posZ, this.prevPoint.posZ, point.posZ, nextPoint.posZ); - this.curPoint.rotYaw = this.cubicInterpolation(amount, this.prevPrevPoint.rotYaw, this.prevPoint.rotYaw, point.rotYaw, nextPoint.rotYaw); - this.curPoint.rotPitch = this.cubicInterpolation(amount, this.prevPrevPoint.rotPitch, this.prevPoint.rotPitch, point.rotPitch, nextPoint.rotPitch); + this.curPoint.posX = MathF.cubicInterpolation(amount, this.prevPrevPoint.posX, this.prevPoint.posX, point.posX, nextPoint.posX); + this.curPoint.posY = MathF.cubicInterpolation(amount, this.prevPrevPoint.posY, this.prevPoint.posY, point.posY, nextPoint.posY); + this.curPoint.posZ = MathF.cubicInterpolation(amount, this.prevPrevPoint.posZ, this.prevPoint.posZ, point.posZ, nextPoint.posZ); + this.curPoint.rotYaw = MathF.cubicInterpolation(amount, this.prevPrevPoint.rotYaw, this.prevPoint.rotYaw, point.rotYaw, nextPoint.rotYaw); + this.curPoint.rotPitch = MathF.cubicInterpolation(amount, this.prevPrevPoint.rotPitch, this.prevPoint.rotPitch, point.rotPitch, nextPoint.rotPitch); break; case NONE: diff --git a/src/main/java/dev/adventurecraft/awakening/common/AC_PlayerTorch.java b/src/main/java/dev/adventurecraft/awakening/common/AC_PlayerTorch.java index d179a10e..21dac42b 100644 --- a/src/main/java/dev/adventurecraft/awakening/common/AC_PlayerTorch.java +++ b/src/main/java/dev/adventurecraft/awakening/common/AC_PlayerTorch.java @@ -31,11 +31,11 @@ public static void setTorchState(World world, boolean active) { } public static void setTorchPos(World world, float x, float y, float z) { - long avgFrameTime = ((ExMinecraft) Minecraft.instance).getAvgFrameTime(); + double avgFrameTime = ((ExMinecraft) Minecraft.instance).getFrameTime(); int updateRate = 1; - if (avgFrameTime > 33333333L) { + if (avgFrameTime > 1 / 30.0) { updateRate = 3; - } else if (avgFrameTime > 16666666L) { + } else if (avgFrameTime > 1 / 60.0) { updateRate = 2; } diff --git a/src/main/java/dev/adventurecraft/awakening/common/ScrollableWidget.java b/src/main/java/dev/adventurecraft/awakening/common/ScrollableWidget.java new file mode 100644 index 00000000..18e5eae8 --- /dev/null +++ b/src/main/java/dev/adventurecraft/awakening/common/ScrollableWidget.java @@ -0,0 +1,395 @@ +package dev.adventurecraft.awakening.common; + +import dev.adventurecraft.awakening.MathF; +import dev.adventurecraft.awakening.extension.client.ExMinecraft; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiElement; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.render.Tessellator; +import org.lwjgl.input.Mouse; +import org.lwjgl.opengl.GL11; + +import java.util.List; + +public abstract class ScrollableWidget extends GuiElement { + + public final Minecraft client; + public final int width; + public final int height; + public int widgetX; + public int widgetY; + public final int contentTop; + public final int contentBot; + protected final int entryHeight; + private final int scrollBackRight; + private final int scrollBackLeft; + + private int field_1533; + private int field_1534; + private double dragDistance = -2.0; + private double scrollAmount; + private double lerpScrollY; + private double targetScrollY; + private double scrollY; + private int prevEntryIndex = -1; + private long prevClickTime = 0L; + private boolean renderSelections = true; + private int contentTopPadding; + private boolean isUsingScrollbar; + private boolean isScrolling; + private boolean firstFrame = true; + + public ScrollableWidget( + Minecraft minecraft, + int x, int y, int width, int height, + int contentTop, int contentBot, int entryHeight) { + this.client = minecraft; + this.width = width; + this.height = height; + this.widgetX = x; + this.widgetY = y; + this.contentTop = contentTop; + this.contentBot = contentBot; + this.entryHeight = entryHeight; + this.scrollBackLeft = 0; + this.scrollBackRight = width; + this.contentTopPadding = 0; + } + + protected abstract int getEntryCount(); + + protected abstract void entryClicked(int entryIndex, boolean doubleClick); + + protected abstract boolean isEntrySelected(int entryIndex); + + protected int getTotalRenderHeight() { + return this.getEntryCount() * this.entryHeight + this.contentTopPadding; + } + + protected abstract void renderBackground(); + + protected abstract void renderEntry( + int entryIndex, double entryX, double entryY, int entryHeight, Tessellator tessellator); + + protected void mouseClicked(int entryX, int entryY) { + } + + protected void beforeRender(double x, double y, Tessellator tessellator) { + } + + protected void afterRender(int mouseX, int mouseY, float tickTime, Tessellator tessellator) { + } + + public void setRenderSelections(boolean bl) { + this.renderSelections = bl; + } + + public void setContentTopPadding(int value) { + this.contentTopPadding = value; + } + + public int getEntryUnderPoint(int x, int y) { + int center = this.width / 2 + this.widgetX; + int left = center - 110; + int right = center + 110; + if (x >= left && x <= right) { + int entryY = y - this.contentTop - this.contentTopPadding + (int) this.scrollY - 4 - this.widgetY; + if (entryY >= 0) { + int entryIndex = entryY / this.entryHeight; + if (entryIndex >= 0 && entryIndex < this.getEntryCount()) { + return entryIndex; + } + } + } + return -1; + } + + public void registerButtons(List list, int i, int j) { + this.field_1533 = i; + this.field_1534 = j; + } + + public void moveContent(double amount) { + if (Math.abs(amount) == 0) { + return; + } + + if (!this.isUsingScrollbar && this.isScrolling) { + // Drag with more and more friction + double maxY = this.getTotalRenderHeight() - (this.contentBot - this.contentTop - 4); + if (maxY < 0) { + maxY /= 2; + } + if (this.targetScrollY >= maxY) { + double delta = this.targetScrollY - maxY + amount; + amount = amount / (1.0 + (delta * delta) / this.entryHeight); + } else { + double minY = Math.min(maxY, 0.0f); + if (this.targetScrollY < minY) { + double delta = minY - this.targetScrollY - amount; + amount = amount / (1.0 + (delta * delta) / this.entryHeight); + } + } + } + this.targetScrollY += amount; + } + + private double clampTargetScroll(double value, double totalHeight) { + double maxY = totalHeight - (this.contentBot - this.contentTop - 4); + if (maxY < 0) { + maxY /= 2; + } + if (value >= maxY) { + value = maxY; + } else { + double minY = Math.min(maxY, 0.0f); + if (value < minY) { + value = minY; + } + } + return value; + } + + private void clampTargetScroll(double totalHeight) { + this.targetScrollY = this.clampTargetScroll(this.targetScrollY, totalHeight); + } + + public void buttonClicked(ButtonWidget button) { + if (!button.active) { + return; + } + if (button.id == this.field_1533) { + this.moveContent(-(this.entryHeight * 2.0 / 3)); + this.dragDistance = -2.0; + } else if (button.id == this.field_1534) { + this.moveContent(this.entryHeight * 2.0 / 3); + this.dragDistance = -2.0; + } + } + + public void onMouseEvent() { + int mouseScrollYDelta = Mouse.getEventDWheel(); + this.moveContent(-(double) mouseScrollYDelta * this.entryHeight); + } + + public void render(int mouseX, int mouseY, float tickTime) { + this.renderBackground(); + int entryCount = this.getEntryCount(); + int center = this.width / 2 + this.widgetX; + int scrollBarLeft = center + 124; + int scrollBarRight = scrollBarLeft + 6; + + int scrollBackLeft = this.scrollBackLeft + this.widgetX; + int scrollBackRight = this.scrollBackRight + this.widgetX; + int contentTop = this.contentTop + this.widgetY; + int contentBot = this.contentBot + this.widgetY; + int totalHeight = this.getTotalRenderHeight(); + + if (this.firstFrame) { + // Derived classes handle element count, + // so we can only do initial layout before rendering. + this.clampTargetScroll(totalHeight); + this.lerpScrollY = this.targetScrollY; + this.scrollY = this.targetScrollY; + this.firstFrame = false; + } + + if (Mouse.isButtonDown(0)) { + if (this.dragDistance == -1.0) { + boolean bl = true; + if (mouseY >= contentTop && mouseY <= contentBot) { + int entryLeft = center - 110; + int entryRight = center + 110; + if (mouseX >= entryLeft && mouseX <= entryRight) { + int hoverY = mouseY - contentTop - this.contentTopPadding + (int) this.scrollY - 4; + int entryIndex = hoverY / this.entryHeight; + if (entryIndex >= 0 && hoverY >= 0 && entryIndex < entryCount) { + boolean doubleClick = entryIndex == this.prevEntryIndex && System.currentTimeMillis() - this.prevClickTime < 250L; + this.entryClicked(entryIndex, doubleClick); + this.prevEntryIndex = entryIndex; + this.prevClickTime = System.currentTimeMillis(); + } else if (hoverY < 0) { + this.mouseClicked(mouseX - entryLeft, mouseY - contentTop + (int) this.scrollY - 4); + bl = false; + } + } + + if (mouseX >= scrollBarLeft && mouseX <= scrollBarRight) { + this.scrollAmount = -1.0; + int n3 = totalHeight - (contentBot - contentTop - 4); + if (n3 < 1) { + n3 = 1; + } + int n2 = (int) Math.ceil((double) ((contentBot - contentTop) * (contentBot - contentTop)) / (double) totalHeight); + if (n2 < 32) { + n2 = 32; + } + if (n2 > contentBot - contentTop - 8) { + n2 = contentBot - contentTop - 8; + } + this.scrollAmount /= (double) (contentBot - contentTop - n2) / (double) n3; + this.isUsingScrollbar = true; + } else { + this.scrollAmount = 1.0; + } + this.dragDistance = bl ? (float) mouseY : -2.0; + } else { + this.dragDistance = -2.0; + } + } else if (this.dragDistance >= 0.0) { + this.moveContent(-((double) mouseY - this.dragDistance) * this.scrollAmount); + this.dragDistance = mouseY; + this.isScrolling = true; + } + } else { + this.dragDistance = -1.0; + this.isUsingScrollbar = false; + this.isScrolling = false; + } + + if (this.isUsingScrollbar) { + // Prevent bouncing + this.clampTargetScroll(totalHeight); + } + double lerpFactor = this.isUsingScrollbar ? 45 : 25; + double deltaTime = ((ExMinecraft) this.client).getFrameTime(); + this.lerpScrollY = MathF.lerp(lerpFactor * deltaTime, this.lerpScrollY, this.targetScrollY); + this.scrollY = MathF.round(this.lerpScrollY, 3); + if (!this.isScrolling) { + this.clampTargetScroll(totalHeight); + } + + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_FOG); + var ts = Tessellator.INSTANCE; + this.renderContentBackground(scrollBackLeft, scrollBackRight, contentTop, contentBot, this.scrollY, ts); + + double entryBaseX = center - 92 - 16; + double entryBaseY = contentTop + 4 - this.scrollY; + this.beforeRender(entryBaseX, entryBaseY, ts); + + for (int entryIndex = 0; entryIndex < entryCount; ++entryIndex) { + double entryY = entryBaseY + entryIndex * this.entryHeight + this.contentTopPadding; + int entryContentHeight = this.entryHeight - 4; + if (entryY > contentBot || entryY + entryContentHeight < contentTop) { + continue; + } + + if (this.renderSelections && this.isEntrySelected(entryIndex)) { + int shadowStartX = center - 110; + int shadowEndX = center + 110; + GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + GL11.glDisable(GL11.GL_TEXTURE_2D); + ts.start(); + ts.color(0x808080); + ts.vertex(shadowStartX, entryY + entryContentHeight + 2, 0.0, 0.0, 1.0); + ts.vertex(shadowEndX, entryY + entryContentHeight + 2, 0.0, 1.0, 1.0); + ts.vertex(shadowEndX, entryY - 2, 0.0, 1.0, 0.0); + ts.vertex(shadowStartX, entryY - 2, 0.0, 0.0, 0.0); + + ts.color(0); + ts.vertex(shadowStartX + 1, entryY + entryContentHeight + 1, 0.0, 0.0, 1.0); + ts.vertex(shadowEndX - 1, entryY + entryContentHeight + 1, 0.0, 1.0, 1.0); + ts.vertex(shadowEndX - 1, entryY - 1, 0.0, 1.0, 0.0); + ts.vertex(shadowStartX + 1, entryY - 1, 0.0, 0.0, 0.0); + ts.tessellate(); + GL11.glEnable(GL11.GL_TEXTURE_2D); + } + this.renderEntry(entryIndex, entryBaseX, entryY, entryContentHeight, ts); + } + GL11.glDisable(GL11.GL_DEPTH_TEST); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glShadeModel(GL11.GL_SMOOTH); + GL11.glBindTexture(GL11.GL_TEXTURE_2D, this.client.textureManager.getTextureId("/gui/background.png")); + GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + ts.start(); + this.renderBackground(this.widgetY, contentTop, 255, 255); + this.renderBackground(contentBot, this.widgetY + this.height, 255, 255); + ts.tessellate(); + GL11.glDisable(GL11.GL_ALPHA_TEST); + GL11.glDisable(GL11.GL_TEXTURE_2D); + + int n4 = 4; + ts.start(); + ts.color(0, 0); + ts.vertex(scrollBackLeft, contentTop + n4, 0.0, 0.0, 1.0); + ts.vertex(scrollBackRight, contentTop + n4, 0.0, 1.0, 1.0); + ts.color(0, 255); + ts.vertex(scrollBackRight, contentTop, 0.0, 1.0, 0.0); + ts.vertex(scrollBackLeft, contentTop, 0.0, 0.0, 0.0); + + ts.color(0, 255); + ts.vertex(scrollBackLeft, contentBot, 0.0, 0.0, 1.0); + ts.vertex(scrollBackRight, contentBot, 0.0, 1.0, 1.0); + ts.color(0, 0); + ts.vertex(scrollBackRight, contentBot - n4, 0.0, 1.0, 0.0); + ts.vertex(scrollBackLeft, contentBot - n4, 0.0, 0.0, 0.0); + ts.tessellate(); + + int n3 = totalHeight - (contentBot - contentTop - 4); + if (n3 > 0) { + // Scrollbar rendering + int n2 = (contentBot - contentTop) * (contentBot - contentTop) / totalHeight; + if (n2 < 32) { + n2 = 32; + } + if (n2 > contentBot - contentTop - 8) { + n2 = contentBot - contentTop - 8; + } + int n = (int) this.clampTargetScroll(this.targetScrollY, totalHeight) * (contentBot - contentTop - n2) / n3 + contentTop; + if (n < contentTop) { + n = contentTop; + } + + ts.start(); + ts.color(0, 255); + ts.vertex(scrollBarLeft, contentBot, 0.0, 0.0, 1.0); + ts.vertex(scrollBarRight, contentBot, 0.0, 1.0, 1.0); + ts.vertex(scrollBarRight, contentTop, 0.0, 1.0, 0.0); + ts.vertex(scrollBarLeft, contentTop, 0.0, 0.0, 0.0); + + ts.color(0x808080, 255); + ts.vertex(scrollBarLeft, n + n2, 0.0, 0.0, 1.0); + ts.vertex(scrollBarRight, n + n2, 0.0, 1.0, 1.0); + ts.vertex(scrollBarRight, n, 0.0, 1.0, 0.0); + ts.vertex(scrollBarLeft, n, 0.0, 0.0, 0.0); + + ts.color(0xC0C0C0, 255); + ts.vertex(scrollBarLeft, n + n2 - 1, 0.0, 0.0, 1.0); + ts.vertex(scrollBarRight - 1, n + n2 - 1, 0.0, 1.0, 1.0); + ts.vertex(scrollBarRight - 1, n, 0.0, 1.0, 0.0); + ts.vertex(scrollBarLeft, n, 0.0, 0.0, 0.0); + ts.tessellate(); + } + this.afterRender(mouseX, mouseY, tickTime, ts); + GL11.glEnable(GL11.GL_TEXTURE_2D); + GL11.glShadeModel(GL11.GL_FLAT); + GL11.glEnable(GL11.GL_ALPHA_TEST); + GL11.glDisable(GL11.GL_BLEND); + } + + private void renderBackground(int topY, int botY, int topAlpha, int botAlpha) { + var ts = Tessellator.INSTANCE; + float f = 32.0f; + ts.color(0x404040, topAlpha); + ts.vertex(this.widgetX + this.width, topY, 0.0, 0.0, (float) topY / f); + ts.vertex(this.widgetX, topY, 0.0, (float) this.width / f, (float) topY / f); + ts.color(0x404040, botAlpha); + ts.vertex(this.widgetX, botY, 0.0, (float) this.width / f, (float) botY / f); + ts.vertex(this.widgetX + this.width, botY, 0.0, 0.0, (float) botY / f); + } + + protected void renderContentBackground(double left, double right, double top, double bot, double scroll, Tessellator ts) { + GL11.glBindTexture(GL11.GL_TEXTURE_2D, this.client.textureManager.getTextureId("/gui/background.png")); + GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + double f2 = 32.0; + ts.start(); + ts.color(0x202020); + ts.vertex(left, bot, 0.0, left / f2, (bot + scroll) / f2); + ts.vertex(right, bot, 0.0, right / f2, (bot + scroll) / f2); + ts.vertex(right, top, 0.0, right / f2, (top + scroll) / f2); + ts.vertex(left, top, 0.0, left / f2, (top + scroll) / f2); + ts.tessellate(); + } +} \ No newline at end of file diff --git a/src/main/java/dev/adventurecraft/awakening/extension/client/ExMinecraft.java b/src/main/java/dev/adventurecraft/awakening/extension/client/ExMinecraft.java index 0c58ca48..094f8ba7 100644 --- a/src/main/java/dev/adventurecraft/awakening/extension/client/ExMinecraft.java +++ b/src/main/java/dev/adventurecraft/awakening/extension/client/ExMinecraft.java @@ -16,7 +16,7 @@ public interface ExMinecraft { void saveMapUsed(String var1, String var2); - long getAvgFrameTime(); + double getFrameTime(); void updateStoreGUI(); diff --git a/src/main/java/dev/adventurecraft/awakening/extension/client/gui/widget/ExScrollableBaseWidget.java b/src/main/java/dev/adventurecraft/awakening/extension/client/gui/widget/ExScrollableBaseWidget.java new file mode 100644 index 00000000..204e4b21 --- /dev/null +++ b/src/main/java/dev/adventurecraft/awakening/extension/client/gui/widget/ExScrollableBaseWidget.java @@ -0,0 +1,6 @@ +package dev.adventurecraft.awakening.extension.client.gui.widget; + +public interface ExScrollableBaseWidget { + + void onMouseEvent(); +} diff --git a/src/main/java/dev/adventurecraft/awakening/mixin/client/MixinMinecraft.java b/src/main/java/dev/adventurecraft/awakening/mixin/client/MixinMinecraft.java index b77c5098..734224a6 100644 --- a/src/main/java/dev/adventurecraft/awakening/mixin/client/MixinMinecraft.java +++ b/src/main/java/dev/adventurecraft/awakening/mixin/client/MixinMinecraft.java @@ -216,14 +216,10 @@ public static File getGameDirectory() { @Shadow public ProgressListenerImpl progressListener; - private static long[] updateTimes = new long[512]; - private static long updateRendererTime; - + private long previousNanoTime; + private double deltaTime; private int rightMouseTicksRan; public AC_MapList mapList; - public int nextFrameTime; - public long prevFrameTimeForAvg; - public long[] tFrameTimes = new long[60]; public AC_CutsceneCamera cutsceneCamera = new AC_CutsceneCamera(); public AC_CutsceneCamera activeCutsceneCamera; public boolean cameraActive; @@ -395,6 +391,18 @@ private void run_setup(CallbackInfo ci) { ContextFactory.initGlobal(new ContextFactory()); } + @Inject( + method = "run", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/util/math/AxixAlignedBoundingBox;method_85()V", + shift = At.Shift.BEFORE)) + private void run_updateDeltaTime(CallbackInfo ci) { + long nanoTime = System.nanoTime(); + this.deltaTime = (nanoTime - this.previousNanoTime) / (double) 1_000_000_000; + this.previousNanoTime = nanoTime; + } + @Redirect( method = "run", at = @At( @@ -408,19 +416,6 @@ private void run_setSoundListenerPos(SoundHelper instance, LivingEntity f, float } } - @Inject( - method = "run", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/client/Minecraft;printOpenGLError(Ljava/lang/String;)V", - shift = At.Shift.AFTER, - ordinal = 1)) - private void run_updateFrameTimes(CallbackInfo ci) { - this.prevFrameTimeForAvg = System.nanoTime(); - this.tFrameTimes[this.nextFrameTime] = this.prevFrameTimeForAvg; - this.nextFrameTime = (this.nextFrameTime + 1) % 60; - } - @Inject( method = "run", at = @At( @@ -497,32 +492,6 @@ private void printStackOnOutOfMem(CallbackInfo ci, @Local OutOfMemoryError error error.printStackTrace(); } - @Inject( - method = "method_2111", - at = @At( - value = "FIELD", - target = "Lnet/minecraft/client/Minecraft;lastFrameRenderTime:J", - shift = At.Shift.BEFORE, - ordinal = 2)) - private void setUpdateTime(long var1, CallbackInfo ci) { - updateTimes[frameRenderTimesAmount & updateTimes.length - 1] = updateRendererTime; - } - - @Inject( - method = "method_2111", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/client/render/Tessellator;addVertex(DDD)V", - shift = At.Shift.BEFORE, - ordinal = 12), - locals = LocalCapture.CAPTURE_FAILHARD) - private void renderUpdateTime(long var1, CallbackInfo ci, long var3, long var5, Tessellator var7, int var8, long var9, int var11, int var12, int var13, int var14, int var15, long var16, long var18) { - long updateTime = updateTimes[var12] / 200000L; - var7.color(var14 * 1); - var7.addVertex((float) var12 + 0.5F, (float) ((long) this.actualHeight - (var16 - var18)) + 0.5F, 0.0D); - var7.addVertex((float) var12 + 0.5F, (float) ((long) this.actualHeight - (var16 - var18 - updateTime)) + 0.5F, 0.0D); - } - @Redirect( method = "toggleFullscreen", at = @At( @@ -1240,8 +1209,8 @@ public void saveMapUsed(String worldName, String mapName) { } @Override - public long getAvgFrameTime() { - return this.tFrameTimes[this.nextFrameTime] != 0L ? (this.prevFrameTimeForAvg - this.tFrameTimes[this.nextFrameTime]) / 60L : 23333333L; + public double getFrameTime() { + return this.deltaTime; } @Override diff --git a/src/main/java/dev/adventurecraft/awakening/mixin/client/gui/screen/MixinSelectWorldScreen.java b/src/main/java/dev/adventurecraft/awakening/mixin/client/gui/screen/MixinSelectWorldScreen.java index ff69422d..5c09f7c1 100644 --- a/src/main/java/dev/adventurecraft/awakening/mixin/client/gui/screen/MixinSelectWorldScreen.java +++ b/src/main/java/dev/adventurecraft/awakening/mixin/client/gui/screen/MixinSelectWorldScreen.java @@ -1,13 +1,13 @@ package dev.adventurecraft.awakening.mixin.client.gui.screen; import dev.adventurecraft.awakening.common.AC_GuiMapSelect; +import dev.adventurecraft.awakening.extension.client.gui.widget.ExScrollableBaseWidget; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.SelectWorldScreen; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.resource.language.TranslationStorage; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; @@ -21,6 +21,9 @@ public abstract class MixinSelectWorldScreen extends Screen { @Shadow private ButtonWidget deleteButton; + @Shadow + private SelectWorldScreen.WorldList worldList; + //@Overwrite TODO: is this needed? public void addButtonsX() { TranslationStorage var1 = TranslationStorage.getInstance(); @@ -31,7 +34,9 @@ public void addButtonsX() { this.deleteButton.active = false; } - @Redirect(method = "buttonClicked", at = @At( + @Redirect( + method = "buttonClicked", + at = @At( value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;openScreen(Lnet/minecraft/client/gui/screen/Screen;)V", ordinal = 1)) @@ -39,10 +44,18 @@ private void openAcMapSelect(Minecraft instance, Screen screen) { instance.openScreen(new AC_GuiMapSelect(this, "")); } - @Redirect(method = "loadWorld", at = @At( + @Redirect( + method = "loadWorld", + at = @At( value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;openScreen(Lnet/minecraft/client/gui/screen/Screen;)V", ordinal = 1)) private void disableOpenScreen(Minecraft instance, Screen screen) { } + + @Override + public void onMouseEvent() { + ((ExScrollableBaseWidget) this.worldList).onMouseEvent(); + super.onMouseEvent(); + } } diff --git a/src/main/java/dev/adventurecraft/awakening/mixin/client/gui/screen/ingame/MixinStatsScreen.java b/src/main/java/dev/adventurecraft/awakening/mixin/client/gui/screen/ingame/MixinStatsScreen.java new file mode 100644 index 00000000..6cecc33b --- /dev/null +++ b/src/main/java/dev/adventurecraft/awakening/mixin/client/gui/screen/ingame/MixinStatsScreen.java @@ -0,0 +1,21 @@ +package dev.adventurecraft.awakening.mixin.client.gui.screen.ingame; + +import dev.adventurecraft.awakening.extension.client.gui.widget.ExScrollableBaseWidget; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.StatsScreen; +import net.minecraft.client.gui.widget.ScrollableBaseWidget; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(StatsScreen.class) +public abstract class MixinStatsScreen extends Screen { + + @Shadow + private ScrollableBaseWidget generalTabBase; + + @Override + public void onMouseEvent() { + ((ExScrollableBaseWidget) this.generalTabBase).onMouseEvent(); + super.onMouseEvent(); + } +} diff --git a/src/main/java/dev/adventurecraft/awakening/mixin/client/gui/widget/MixinScrollableBaseWidget.java b/src/main/java/dev/adventurecraft/awakening/mixin/client/gui/widget/MixinScrollableBaseWidget.java new file mode 100644 index 00000000..4ad0f20b --- /dev/null +++ b/src/main/java/dev/adventurecraft/awakening/mixin/client/gui/widget/MixinScrollableBaseWidget.java @@ -0,0 +1,152 @@ +package dev.adventurecraft.awakening.mixin.client.gui.widget; + +import dev.adventurecraft.awakening.common.ScrollableWidget; +import dev.adventurecraft.awakening.extension.client.gui.widget.ExScrollableBaseWidget; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.ScrollableBaseWidget; +import net.minecraft.client.render.Tessellator; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; + +@Mixin(ScrollableBaseWidget.class) +public abstract class MixinScrollableBaseWidget implements ExScrollableBaseWidget { + + @Unique + private ScrollableWidget rootWidget; + + @Unique + private boolean doRenderStatItemSlot; + + @Shadow + protected abstract void method_1255(int i, int j); + + @Inject(method = "", at = @At("TAIL")) + private void init( + Minecraft instance, int width, int height, + int contentTop, int contentBot, int entryHeight, CallbackInfo ci) { + + MixinScrollableBaseWidget self = this; + this.rootWidget = new ScrollableWidget( + instance, 0, 0, width, height, contentTop, contentBot, entryHeight) { + @Override + protected int getEntryCount() { + return self.getSize(); + } + + @Override + protected void entryClicked(int entryIndex, boolean doubleClick) { + self.entryClicked(entryIndex, doubleClick); + } + + @Override + protected void mouseClicked(int x, int y) { + self.mouseClicked(x, y); + } + + @Override + protected boolean isEntrySelected(int entryIndex) { + return self.isWorldSelected(entryIndex); + } + + @Override + protected int getTotalRenderHeight() { + return self.getTotalRenderHeight(); + } + + @Override + protected void renderBackground() { + self.renderBackground(); + } + + @Override + protected void renderEntry(int entryIndex, double entryX, double entryY, int entryHeight, Tessellator tessellator) { + int x = (int) Math.floor(entryX); + int y = (int) Math.floor(entryY); + self.renderStatEntry(entryIndex, x, y, entryHeight, tessellator); + } + + @Override + protected void beforeRender(double x, double y, Tessellator tessellator) { + if (self.doRenderStatItemSlot) { + int sX = (int) Math.floor(x); + int sY = (int) Math.floor(y); + self.renderStatItemSlot(sX, sY, tessellator); + } + } + + @Override + protected void afterRender(int mouseX, int mouseY, float tickTime, Tessellator tessellator) { + self.method_1255(mouseX, mouseY); + } + }; + } + + @Shadow + protected abstract int getSize(); + + @Shadow + protected abstract void entryClicked(int var1, boolean var2); + + @Shadow + protected abstract boolean isWorldSelected(int var1); + + @Shadow + protected abstract int getTotalRenderHeight(); + + @Shadow + protected abstract void mouseClicked(int x, int y); + + @Shadow + protected abstract void renderBackground(); + + @Shadow + protected abstract void renderStatEntry(int var1, int var2, int var3, int var4, Tessellator var5); + + @Shadow + protected abstract void renderStatItemSlot(int i, int j, Tessellator arg); + + @Overwrite + public void method_1260(boolean bl) { + this.rootWidget.setRenderSelections(bl); + } + + @Overwrite + public void method_1261(boolean bl, int i) { + this.doRenderStatItemSlot = bl; + int contentYOffset = bl ? i : 0; + this.rootWidget.setContentTopPadding(contentYOffset); + } + + @Overwrite + public int method_1262(int i, int j) { + return this.rootWidget.getEntryUnderPoint(i, j); + } + + @Overwrite + public void registerButtons(List list, int i, int j) { + this.rootWidget.registerButtons(list, i, j); + } + + @Overwrite + public void buttonClicked(ButtonWidget arg) { + this.rootWidget.buttonClicked(arg); + } + + @Overwrite + public void render(int i, int j, float f) { + this.rootWidget.render(i, j, f); + } + + @Override + public void onMouseEvent() { + this.rootWidget.onMouseEvent(); + } +} diff --git a/src/main/java/dev/adventurecraft/awakening/mixin/client/render/MixinGameRenderer.java b/src/main/java/dev/adventurecraft/awakening/mixin/client/render/MixinGameRenderer.java index d7582b56..c5a2c42f 100644 --- a/src/main/java/dev/adventurecraft/awakening/mixin/client/render/MixinGameRenderer.java +++ b/src/main/java/dev/adventurecraft/awakening/mixin/client/render/MixinGameRenderer.java @@ -954,10 +954,10 @@ public void resetZoom() { public float getFarPlane() { float dist = (float) (256 >> this.client.options.viewDistance); if (((ExGameOptions) this.client.options).isAutoFarClip()) { - long avgTime = ((ExMinecraft) this.client).getAvgFrameTime(); - if (avgTime > 33333333L) { + double avgTime = ((ExMinecraft) this.client).getFrameTime(); + if (avgTime > 0.033) { this.farClipAdjustment *= 0.99F; - } else if (avgTime < 20000000L) { + } else if (avgTime < 0.02) { this.farClipAdjustment *= 1.01F; } diff --git a/src/main/resources/adventurecraft.accesswidener b/src/main/resources/adventurecraft.accesswidener index 4f2a832a..e0165c33 100644 --- a/src/main/resources/adventurecraft.accesswidener +++ b/src/main/resources/adventurecraft.accesswidener @@ -79,6 +79,8 @@ accessible field net/minecraft/client/gui/screen/Screen buttons Ljava/util/List; accessible field net/minecraft/client/gui/screen/Screen textRenderer Lnet/minecraft/client/render/TextRenderer; accessible field net/minecraft/client/gui/screen/Screen lastClickedButton Lnet/minecraft/client/gui/widget/ButtonWidget; +accessible class net/minecraft/client/gui/screen/SelectWorldScreen$WorldList + accessible method net/minecraft/client/gui/GuiElement fillGradient (IIIIII)V accessible field net/minecraft/client/gui/widgets/SliderWidget option Lnet/minecraft/client/options/Option; diff --git a/src/main/resources/adventurecraft.mixins.json b/src/main/resources/adventurecraft.mixins.json index f804a51d..89165db8 100644 --- a/src/main/resources/adventurecraft.mixins.json +++ b/src/main/resources/adventurecraft.mixins.json @@ -20,8 +20,10 @@ "client.gui.screen.ingame.MixinChatScreen", "client.gui.screen.ingame.MixinDeathScreen", "client.gui.screen.ingame.MixinPauseScreen", + "client.gui.screen.ingame.MixinStatsScreen", "client.gui.screen.menu.MixinTitleScreen", "client.gui.screen.menu.MixinVideoSettingsScreen", + "client.gui.widget.MixinScrollableBaseWidget", "client.model.MixinCuboid", "client.options.MixinGameOptions", "client.particle.MixinParticleManager",