aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/polyfrost/chatting
diff options
context:
space:
mode:
authorWyvest <wyvestbusiness@gmail.com>2023-11-22 08:18:19 +0900
committerWyvest <wyvestbusiness@gmail.com>2023-11-22 08:18:19 +0900
commit8b373f577d9c6dde26357ef3fc86691f1efef9b4 (patch)
treea5328e995d8f4df21a9fe94ac8e384be08833c70 /src/main/java/org/polyfrost/chatting
parent64230799777473246b5f98efbc596206c5bbf42d (diff)
downloadChatting-8b373f577d9c6dde26357ef3fc86691f1efef9b4.tar.gz
Chatting-8b373f577d9c6dde26357ef3fc86691f1efef9b4.tar.bz2
Chatting-8b373f577d9c6dde26357ef3fc86691f1efef9b4.zip
update PGT and relocate to org.polyfrost
Diffstat (limited to 'src/main/java/org/polyfrost/chatting')
-rw-r--r--src/main/java/org/polyfrost/chatting/hook/ChatLineHook.java17
-rw-r--r--src/main/java/org/polyfrost/chatting/hook/GuiNewChatHook.java21
-rw-r--r--src/main/java/org/polyfrost/chatting/mixin/ChatLineMixin.java114
-rw-r--r--src/main/java/org/polyfrost/chatting/mixin/ClientCommandHandlerMixin.java33
-rw-r--r--src/main/java/org/polyfrost/chatting/mixin/EntityPlayerSPMixin.java41
-rw-r--r--src/main/java/org/polyfrost/chatting/mixin/GuiChatMixin.java143
-rw-r--r--src/main/java/org/polyfrost/chatting/mixin/GuiNewChatAccessor.java20
-rw-r--r--src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMapMixin.java78
-rw-r--r--src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin.java257
-rw-r--r--src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatHeight.java21
-rw-r--r--src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatSearching.java31
-rw-r--r--src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatTabs.java54
-rw-r--r--src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_Scrolling.java71
-rw-r--r--src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_SmoothMessages.java103
-rw-r--r--src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_TextRendering.java29
-rw-r--r--src/main/java/org/polyfrost/chatting/mixin/GuiUtilsMixin.java36
16 files changed, 1069 insertions, 0 deletions
diff --git a/src/main/java/org/polyfrost/chatting/hook/ChatLineHook.java b/src/main/java/org/polyfrost/chatting/hook/ChatLineHook.java
new file mode 100644
index 0000000..50b9ce3
--- /dev/null
+++ b/src/main/java/org/polyfrost/chatting/hook/ChatLineHook.java
@@ -0,0 +1,17 @@
+package org.polyfrost.chatting.hook;
+
+import net.minecraft.client.gui.ChatLine;
+import net.minecraft.client.network.NetworkPlayerInfo;
+
+import java.lang.ref.WeakReference;
+import java.util.HashSet;
+
+public interface ChatLineHook {
+ HashSet<WeakReference<ChatLine>> chatLines = new HashSet<>();
+ boolean hasDetected();
+ NetworkPlayerInfo getPlayerInfo();
+
+ void updatePlayerInfo();
+
+ long getUniqueId();
+}
diff --git a/src/main/java/org/polyfrost/chatting/hook/GuiNewChatHook.java b/src/main/java/org/polyfrost/chatting/hook/GuiNewChatHook.java
new file mode 100644
index 0000000..84097bd
--- /dev/null
+++ b/src/main/java/org/polyfrost/chatting/hook/GuiNewChatHook.java
@@ -0,0 +1,21 @@
+package org.polyfrost.chatting.hook;
+
+import net.minecraft.client.gui.ChatLine;
+
+import java.awt.datatransfer.Transferable;
+
+public interface GuiNewChatHook {
+ int getRight();
+
+ boolean isHovering();
+
+ ChatLine getHoveredLine(int mouseY);
+
+ Transferable getChattingChatComponent(int mouseY);
+
+ default ChatLine getFullMessage(ChatLine line) {
+ throw new AssertionError("getFullMessage not overridden on GuiNewChat");
+ }
+
+ int getTextOpacity();
+}
diff --git a/src/main/java/org/polyfrost/chatting/mixin/ChatLineMixin.java b/src/main/java/org/polyfrost/chatting/mixin/ChatLineMixin.java
new file mode 100644
index 0000000..2e5f21c
--- /dev/null
+++ b/src/main/java/org/polyfrost/chatting/mixin/ChatLineMixin.java
@@ -0,0 +1,114 @@
+/*
+ * This file is from chat_heads is licensed under MPL-2.0, which can be found at https://www.mozilla.org/en-US/MPL/2.0/
+ * See: https://github.com/dzwdz/chat_heads/blob/fabric-1.16.x/LICENSE
+ */
+
+package org.polyfrost.chatting.mixin;
+
+import org.polyfrost.chatting.config.ChattingConfig;
+import org.polyfrost.chatting.hook.ChatLineHook;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.ChatLine;
+import net.minecraft.client.network.NetHandlerPlayClient;
+import net.minecraft.client.network.NetworkPlayerInfo;
+import net.minecraft.util.IChatComponent;
+import org.jetbrains.annotations.Nullable;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Map;
+
+@Mixin(ChatLine.class)
+public class ChatLineMixin implements ChatLineHook {
+ private boolean detected = false;
+ private boolean first = true;
+ private NetworkPlayerInfo playerInfo;
+ private NetworkPlayerInfo detectedPlayerInfo;
+ private static NetworkPlayerInfo lastPlayerInfo;
+ private static long lastUniqueId = 0;
+ private long uniqueId = 0;
+
+ @Inject(method = "<init>", at = @At("RETURN"))
+ private void onInit(int i, IChatComponent iChatComponent, int j, CallbackInfo ci) {
+ lastUniqueId++;
+ uniqueId = lastUniqueId;
+ chatLines.add(new WeakReference<>((ChatLine) (Object) this));
+ NetHandlerPlayClient netHandler = Minecraft.getMinecraft().getNetHandler();
+ if (netHandler == null) return;
+ Map<String, NetworkPlayerInfo> nicknameCache = new HashMap<>();
+ try {
+ for (String word : iChatComponent.getFormattedText().split("(§.)|\\W")) {
+ if (word.isEmpty()) continue;
+ playerInfo = netHandler.getPlayerInfo(word);
+ if (playerInfo == null) {
+ playerInfo = getPlayerFromNickname(word, netHandler, nicknameCache);
+ }
+ if (playerInfo != null) {
+ detectedPlayerInfo = playerInfo;
+ detected = true;
+ if (playerInfo == lastPlayerInfo) {
+ first = false;
+ if (ChattingConfig.INSTANCE.getHideChatHeadOnConsecutiveMessages()) {
+ playerInfo = null;
+ }
+ } else {
+ lastPlayerInfo = playerInfo;
+ }
+ break;
+ }
+ }
+ } catch (Exception ignored) {
+ }
+ }
+
+ @Nullable
+ private static NetworkPlayerInfo getPlayerFromNickname(String word, NetHandlerPlayClient connection, Map<String, NetworkPlayerInfo> nicknameCache) {
+ if (nicknameCache.isEmpty()) {
+ for (NetworkPlayerInfo p : connection.getPlayerInfoMap()) {
+ IChatComponent displayName = p.getDisplayName();
+ if (displayName != null) {
+ String nickname = displayName.getUnformattedTextForChat();
+ if (word.equals(nickname)) {
+ nicknameCache.clear();
+ return p;
+ }
+
+ nicknameCache.put(nickname, p);
+ }
+ }
+ } else {
+ // use prepared cache
+ return nicknameCache.get(word);
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean hasDetected() {
+ return detected;
+ }
+
+ @Override
+ public NetworkPlayerInfo getPlayerInfo() {
+ return playerInfo;
+ }
+
+ @Override
+ public void updatePlayerInfo() {
+ if (ChattingConfig.INSTANCE.getHideChatHeadOnConsecutiveMessages() && !first) {
+ playerInfo = null;
+ } else {
+ playerInfo = detectedPlayerInfo;
+ }
+ }
+
+ @Override
+ public long getUniqueId() {
+ return uniqueId;
+ }
+}
diff --git a/src/main/java/org/polyfrost/chatting/mixin/ClientCommandHandlerMixin.java b/src/main/java/org/polyfrost/chatting/mixin/ClientCommandHandlerMixin.java
new file mode 100644
index 0000000..ee63ed3
--- /dev/null
+++ b/src/main/java/org/polyfrost/chatting/mixin/ClientCommandHandlerMixin.java
@@ -0,0 +1,33 @@
+package org.polyfrost.chatting.mixin;
+
+import kotlin.Pair;
+import net.minecraft.client.Minecraft;
+import net.minecraft.command.CommandHandler;
+import net.minecraft.command.ICommandSender;
+import net.minecraft.util.BlockPos;
+import net.minecraftforge.client.ClientCommandHandler;
+import net.minecraftforge.fml.client.FMLClientHandler;
+import org.polyfrost.chatting.chat.ChatShortcuts;
+import org.polyfrost.chatting.config.ChattingConfig;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Redirect;
+
+import java.util.List;
+
+@Mixin(value = ClientCommandHandler.class, remap = false)
+public class ClientCommandHandlerMixin extends CommandHandler {
+ @Redirect(method = "autoComplete", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/client/ClientCommandHandler;getTabCompletionOptions(Lnet/minecraft/command/ICommandSender;Ljava/lang/String;Lnet/minecraft/util/BlockPos;)Ljava/util/List;"))
+ private List<String> addChatShortcuts(ClientCommandHandler instance, ICommandSender iCommandSender, String leftOfCursor, BlockPos blockPos) {
+ Minecraft mc = FMLClientHandler.instance().getClient();
+ List<String> autocompleteList = instance.getTabCompletionOptions(mc.thePlayer, leftOfCursor, mc.thePlayer.getPosition());
+ if (ChattingConfig.INSTANCE.getChatShortcuts()) {
+ for (Pair<String, String> pair : ChatShortcuts.INSTANCE.getShortcuts()) {
+ if (pair.getFirst().startsWith(leftOfCursor)) {
+ autocompleteList.add(pair.getFirst());
+ }
+ }
+ }
+ return autocompleteList;
+ }
+}
diff --git a/src/main/java/org/polyfrost/chatting/mixin/EntityPlayerSPMixin.java b/src/main/java/org/polyfrost/chatting/mixin/EntityPlayerSPMixin.java
new file mode 100644
index 0000000..d59d7f2
--- /dev/null
+++ b/src/main/java/org/polyfrost/chatting/mixin/EntityPlayerSPMixin.java
@@ -0,0 +1,41 @@
+package org.polyfrost.chatting.mixin;
+
+import net.minecraft.client.entity.EntityPlayerSP;
+import net.minecraft.client.network.NetHandlerPlayClient;
+import net.minecraft.network.Packet;
+import net.minecraft.network.play.client.C01PacketChatMessage;
+import org.polyfrost.chatting.chat.ChatTab;
+import org.polyfrost.chatting.chat.ChatTabs;
+import org.polyfrost.chatting.config.ChattingConfig;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Redirect;
+
+@Mixin(value = EntityPlayerSP.class, priority = 0)
+public class EntityPlayerSPMixin {
+ @Shadow @Final public NetHandlerPlayClient sendQueue;
+
+ @Redirect(method = "sendChatMessage", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/NetHandlerPlayClient;addToSendQueue(Lnet/minecraft/network/Packet;)V"))
+ private void handleSentMessages(NetHandlerPlayClient instance, Packet<?> packet, String value) {
+ if (value.startsWith("/")) {
+ sendQueue.addToSendQueue(packet);
+ return;
+ }
+ if (ChattingConfig.INSTANCE.getChatTabs() && !ChatTabs.INSTANCE.getCurrentTabs().isEmpty()) {
+ boolean sent = false;
+ for (ChatTab tab : ChatTabs.INSTANCE.getCurrentTabs()) {
+ if (tab.getPrefix() != null && !tab.getPrefix().isEmpty()) {
+ sendQueue.addToSendQueue(new C01PacketChatMessage(tab.getPrefix() + value));
+ sent = true;
+ }
+ }
+ if (!sent) {
+ sendQueue.addToSendQueue(packet);
+ }
+ } else {
+ sendQueue.addToSendQueue(packet);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiChatMixin.java b/src/main/java/org/polyfrost/chatting/mixin/GuiChatMixin.java
new file mode 100644
index 0000000..303326f
--- /dev/null
+++ b/src/main/java/org/polyfrost/chatting/mixin/GuiChatMixin.java
@@ -0,0 +1,143 @@
+package org.polyfrost.chatting.mixin;
+
+import org.polyfrost.chatting.chat.*;
+import org.polyfrost.chatting.config.ChattingConfig;
+import org.polyfrost.chatting.gui.components.ClearButton;
+import org.polyfrost.chatting.gui.components.ScreenshotButton;
+import org.polyfrost.chatting.gui.components.SearchButton;
+import org.polyfrost.chatting.hook.ChatLineHook;
+import org.polyfrost.chatting.hook.GuiNewChatHook;
+import com.google.common.collect.Lists;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.ChatLine;
+import net.minecraft.client.gui.GuiChat;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.util.MathHelper;
+import net.minecraftforge.fml.client.config.GuiUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.lwjgl.input.Mouse;
+import org.polyfrost.chatting.chat.ChatSearchingManager;
+import org.polyfrost.chatting.chat.ChatShortcuts;
+import org.polyfrost.chatting.utils.ModCompatHooks;
+import org.spongepowered.asm.mixin.Mixin;
+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.ModifyArg;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.awt.*;
+import java.awt.datatransfer.Transferable;
+import java.util.List;
+
+@Mixin(GuiChat.class)
+public abstract class GuiChatMixin extends GuiScreen {
+
+ @Unique
+ private static final List<String> COPY_TOOLTIP = Lists.newArrayList(
+ "\u00A7e\u00A7lCopy To Clipboard",
+ "\u00A7b\u00A7lNORMAL CLICK\u00A7r \u00A78- \u00A77Full Message",
+ "\u00A7b\u00A7lCTRL CLICK\u00A7r \u00A78- \u00A77Single Line",
+ "\u00A7b\u00A7lSHIFT CLICK\u00A7r \u00A78- \u00A77Screenshot Line",
+ "",
+ "\u00A7e\u00A7lModifiers",
+ "\u00A7b\u00A7lALT\u00A7r \u00A78- \u00A77Formatting Codes");
+
+ private SearchButton searchButton;
+
+ @Inject(method = "initGui", at = @At("TAIL"))
+ private void init(CallbackInfo ci) {
+ if (ChattingConfig.INSTANCE.getChatSearch()) {
+ searchButton = new SearchButton();
+ buttonList.add(searchButton);
+ }
+ buttonList.add(new ScreenshotButton());
+ buttonList.add(new ClearButton());
+ if (ChattingConfig.INSTANCE.getChatTabs()) {
+ for (ChatTab chatTab : ChatTabs.INSTANCE.getTabs()) {
+ buttonList.add(chatTab.getButton());
+ }
+ }
+ }
+
+ @Inject(method = "updateScreen", at = @At("HEAD"))
+ private void updateScreen(CallbackInfo ci) {
+ if (ChattingConfig.INSTANCE.getChatSearch() && searchButton.isEnabled()) {
+ searchButton.getInputField().updateCursorCounter();
+ }
+ }
+
+ @Inject(method = "keyTyped", at = @At("HEAD"), cancellable = true)
+ private void keyTyped(char typedChar, int keyCode, CallbackInfo ci) {
+ if (ChattingConfig.INSTANCE.getChatSearch() && searchButton.isEnabled()) {
+ ci.cancel();
+ if (keyCode == 1) {
+ searchButton.onMousePress();
+ return;
+ }
+ searchButton.getInputField().textboxKeyTyped(typedChar, keyCode);
+ ChatSearchingManager.INSTANCE.setLastSearch(searchButton.getInputField().getText());
+ }
+ }
+
+ @Inject(method = "drawScreen", at = @At("HEAD"))
+ private void onDrawScreen(int mouseX, int mouseY, float partialTicks, CallbackInfo ci) {
+ GuiNewChatHook hook = ((GuiNewChatHook) Minecraft.getMinecraft().ingameGUI.getChatGUI());
+ float f = mc.ingameGUI.getChatGUI().getChatScale();
+ int x = MathHelper.floor_float((float) mouseX / f);
+ if (hook.isHovering() && (hook.getRight() + ModCompatHooks.getXOffset() + 3) <= x && (hook.getRight() + ModCompatHooks.getXOffset()) + 13 > x) {
+ GuiUtils.drawHoveringText(COPY_TOOLTIP, mouseX, mouseY, width, height, -1, fontRendererObj);
+ GlStateManager.disableLighting();
+ }
+ }
+
+ @ModifyArg(method = "drawScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiChat;drawRect(IIIII)V"), index = 2)
+ private int modifyRight(int right) {
+ return ChattingConfig.INSTANCE.getCompactInputBox() ? (MathHelper.ceiling_float_int((float) mc.ingameGUI.getChatGUI().getChatWidth() / mc.ingameGUI.getChatGUI().getChatScale()) + 6) : right;
+ }
+
+ @ModifyArg(method = "drawScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiChat;drawRect(IIIII)V"), index = 4)
+ private int modifyInputBoxColor(int color) {
+ return ChattingConfig.INSTANCE.getInputBoxBackgroundColor().getRGB();
+ }
+
+ @Inject(method = "mouseClicked", at = @At("HEAD"))
+ private void mouseClicked(int mouseX, int mouseY, int mouseButton, CallbackInfo ci) {
+ GuiNewChatHook hook = ((GuiNewChatHook) Minecraft.getMinecraft().ingameGUI.getChatGUI());
+ float f = mc.ingameGUI.getChatGUI().getChatScale();
+ int x = MathHelper.floor_float((float) mouseX / f);
+ if (hook.isHovering()) {
+ if (((hook.getRight() + ModCompatHooks.getXOffset() + 3) <= x && (hook.getRight() + ModCompatHooks.getXOffset()) + 13 > x) || (mouseButton == 1 && ChattingConfig.INSTANCE.getRightClickCopy())) {
+ Transferable message = hook.getChattingChatComponent(Mouse.getY());
+ if (message == null) return;
+ try {
+ Toolkit.getDefaultToolkit().getSystemClipboard().setContents(message, null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ } else if ((hook.getRight() + ModCompatHooks.getXOffset() + 13) <= x && (hook.getRight() + ModCompatHooks.getXOffset()) + 23 > x) {
+ ChatLine chatLine = hook.getHoveredLine(Mouse.getY());
+ if (chatLine == null) return;
+ ((GuiNewChatAccessor) Minecraft.getMinecraft().ingameGUI.getChatGUI()).getDrawnChatLines().removeIf(line -> ((ChatLineHook) line).getUniqueId() == ((ChatLineHook) chatLine).getUniqueId());
+ ((GuiNewChatAccessor) Minecraft.getMinecraft().ingameGUI.getChatGUI()).getChatLines().removeIf(line -> ((ChatLineHook) line).getUniqueId() == ((ChatLineHook) chatLine).getUniqueId());
+ }
+ }
+
+ }
+
+ @ModifyArg(method = "keyTyped", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiChat;sendChatMessage(Ljava/lang/String;)V"), index = 0)
+ private String modifySentMessage(String original) {
+ if (ChattingConfig.INSTANCE.getChatShortcuts()) {
+ if (original.startsWith("/")) {
+ return "/" + ChatShortcuts.INSTANCE.handleSentCommand(StringUtils.substringAfter(original, "/"));
+ }
+ }
+ return original;
+ }
+
+ @Inject(method = "handleMouseInput", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiNewChat;scroll(I)V"))
+ private void handleMouseInput(CallbackInfo ci) {
+ ChatScrollingHook.INSTANCE.setShouldSmooth(true);
+ }
+}
diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatAccessor.java b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatAccessor.java
new file mode 100644
index 0000000..d0630ca
--- /dev/null
+++ b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatAccessor.java
@@ -0,0 +1,20 @@
+package org.polyfrost.chatting.mixin;
+
+import net.minecraft.client.gui.ChatLine;
+import net.minecraft.client.gui.GuiNewChat;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+import java.util.List;
+
+@Mixin(GuiNewChat.class)
+public interface GuiNewChatAccessor {
+ @Accessor
+ List<ChatLine> getDrawnChatLines();
+
+ @Accessor
+ List<ChatLine> getChatLines();
+
+ @Accessor
+ int getScrollPos();
+}
diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMapMixin.java b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMapMixin.java
new file mode 100644
index 0000000..ca1c0df
--- /dev/null
+++ b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMapMixin.java
@@ -0,0 +1,78 @@
+package org.polyfrost.chatting.mixin;
+
+import net.minecraft.client.gui.ChatLine;
+import net.minecraft.client.gui.GuiNewChat;
+import net.minecraft.util.IChatComponent;
+import org.polyfrost.chatting.hook.GuiNewChatHook;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+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 org.spongepowered.asm.mixin.injection.callback.LocalCapture;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Mixin(value = GuiNewChat.class, priority = Integer.MIN_VALUE)
+public abstract class GuiNewChatMapMixin implements GuiNewChatHook {
+
+ @Unique private final Map<ChatLine, ChatLine> drawnToFull = new HashMap<>();
+ @Unique private final List<ChatLine> tempDrawnLines = new ArrayList<>();
+ @Unique private ChatLine lastTempLine = null;
+
+ @Shadow @Final private List<ChatLine> drawnChatLines;
+ @Shadow @Final private List<ChatLine> chatLines;
+
+ @Inject(method = "setChatLine", at = @At("HEAD"))
+ private void handleSetChatLine(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) {
+ tempDrawnLines.clear();
+ }
+
+ @Inject(method = "setChatLine", at = @At(value = "INVOKE", target = "Ljava/util/List;add(ILjava/lang/Object;)V", ordinal = 0, shift = At.Shift.AFTER, remap = false))
+ private void handleDrawnLineAdded(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) {
+ if (!displayOnly) tempDrawnLines.add(drawnChatLines.get(0));
+ else if (lastTempLine != null) {
+ drawnToFull.put(drawnChatLines.get(0), lastTempLine);
+ }
+ }
+
+ @Inject(method = "setChatLine", at = @At(value = "INVOKE", target = "Ljava/util/List;remove(I)Ljava/lang/Object;", ordinal = 0, shift = At.Shift.BEFORE, remap = false))
+ private void handleDrawnLineRemoved(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) {
+ drawnToFull.remove(drawnChatLines.get(drawnChatLines.size() - 1));
+ }
+
+ @Inject(method = "setChatLine", at = @At("RETURN"))
+ private void handleLineAdded(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) {
+ if (!displayOnly) {
+ ChatLine masterLine = chatLines.get(0);
+ for (ChatLine tempDrawnLine : tempDrawnLines) drawnToFull.put(tempDrawnLine, masterLine);
+ }else {
+ lastTempLine = null;
+ }
+ }
+
+ @Inject(method = "refreshChat", at = @At("HEAD"))
+ private void handleRefreshedChat(CallbackInfo ci) {
+ drawnToFull.clear();
+ }
+
+ @Inject(method = "refreshChat", locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiNewChat;setChatLine(Lnet/minecraft/util/IChatComponent;IIZ)V", ordinal = 0, shift = At.Shift.BEFORE))
+ private void handleLineRefresh(CallbackInfo ci, int i, ChatLine chatline) {
+ lastTempLine = chatline;
+ }
+
+ @Inject(method = "clearChatMessages", at = @At("HEAD"))
+ private void handleChatCleared(CallbackInfo ci) {
+ drawnToFull.clear();
+ }
+
+ @Override
+ public ChatLine getFullMessage(ChatLine line) {
+ return drawnToFull.getOrDefault(line, null);
+ }
+}
diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin.java b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin.java
new file mode 100644
index 0000000..bc90730
--- /dev/null
+++ b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin.java
@@ -0,0 +1,257 @@
+package org.polyfrost.chatting.mixin;
+
+import cc.polyfrost.oneconfig.libs.universal.UMouse;
+import cc.polyfrost.oneconfig.utils.Notifications;
+import org.polyfrost.chatting.Chatting;
+import org.polyfrost.chatting.chat.ChatSearchingManager;
+import org.polyfrost.chatting.config.ChattingConfig;
+import org.polyfrost.chatting.hook.GuiNewChatHook;
+import org.polyfrost.chatting.utils.ModCompatHooks;
+import org.polyfrost.chatting.utils.RenderUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.*;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.MathHelper;
+import net.minecraft.util.ResourceLocation;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.*;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.spongepowered.asm.mixin.injection.invoke.arg.Args;
+
+import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.Transferable;
+import java.awt.image.BufferedImage;
+import java.util.List;
+
+@Mixin(value = GuiNewChat.class, priority = 990)
+public abstract class GuiNewChatMixin extends Gui implements GuiNewChatHook {
+ @Unique
+ private int chatting$right = 0;
+ @Unique
+ private boolean chatting$isHovering;
+ @Unique
+ private boolean chatting$chatCheck;
+ @Unique
+ private int chatting$textOpacity;
+ @Shadow
+ @Final
+ private Minecraft mc;
+ @Shadow
+ @Final
+ private List<ChatLine> drawnChatLines;
+ @Shadow
+ public abstract boolean getChatOpen();
+
+ @Shadow
+ public abstract float getChatScale();
+
+ @Shadow
+ public abstract int getLineCount();
+
+ @Shadow
+ private int scrollPos;
+
+ @Shadow public abstract int getChatWidth();
+
+ @Unique
+ private static final ResourceLocation COPY = new ResourceLocation("chatting:copy.png");
+ @Unique
+ private static final ResourceLocation DELETE = new ResourceLocation("chatting:delete.png");
+
+ /*?
+ @Unique
+ private final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
+ @ModifyArg(method = "setChatLine", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/ChatLine;<init>(ILnet/minecraft/util/IChatComponent;I)V"))
+ private IChatComponent handleAddDrawnLine(IChatComponent iChatComponent) {
+ if (!ChattingConfig.INSTANCE.getShowTimestamp()) return iChatComponent;
+ String time = " §7["+ sdf.format(new Date(System.currentTimeMillis())) + "]§r";
+ iChatComponent.appendSibling(new ChatComponentText(time));
+ return iChatComponent;
+ }
+
+ */
+
+ @Inject(method = "drawChat", at = @At("HEAD"))
+ private void checkScreenshotKeybind(int j2, CallbackInfo ci) {
+ if (Chatting.INSTANCE.getKeybind().isPressed()) {
+ Chatting.INSTANCE.setDoTheThing(true);
+ }
+ chatting$chatCheck = false;
+ }
+
+ @ModifyVariable(method = "drawChat", at = @At("HEAD"), argsOnly = true)
+ private int setUpdateCounterWhenYes(int updateCounter) {
+ return Chatting.INSTANCE.getDoTheThing() ? 0 : updateCounter;
+ }
+
+ @ModifyVariable(method = "drawChat", at = @At("STORE"), index = 2)
+ private int setChatLimitWhenYes(int linesToDraw) {
+ return Chatting.INSTANCE.getDoTheThing()
+ ? GuiNewChat.calculateChatboxHeight(mc.gameSettings.chatHeightFocused) / 9
+ : linesToDraw;
+ }
+
+ private boolean lineInBounds = false;
+
+ @ModifyArgs(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiNewChat;drawRect(IIIII)V"), slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/util/MathHelper;clamp_double(DDD)D"), to = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;enableBlend()V")))
+ private void captureDrawRect(Args args) {
+ args.set(4, ChattingConfig.INSTANCE.getChatBackgroundColor().getRGB());
+ if (mc.currentScreen instanceof GuiChat) {
+ int left = args.get(0);
+ int top = args.get(1);
+ int right = args.get(2);
+ int bottom = args.get(3);
+ if (isInBounds(left, top, right, bottom, getChatScale())) {
+ chatting$isHovering = true;
+ lineInBounds = true;
+ args.set(4, ChattingConfig.INSTANCE.getHoveredChatBackgroundColor().getRGB());
+ }
+ }
+ }
+
+ @ModifyArgs(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/FontRenderer;drawStringWithShadow(Ljava/lang/String;FFI)I"))
+ private void drawChatBox(Args args) {
+ if (mc.currentScreen instanceof GuiChat) {
+ float f = this.getChatScale();
+ int left = 0;
+ int top = (int) ((float) args.get(2) - 1);
+ int right = MathHelper.ceiling_float_int((float)getChatWidth() / f) + 4;
+ int bottom = (int) ((float) args.get(2) + 8);
+ if ((chatting$isHovering && lineInBounds) || isInBounds(left, top, right, bottom, f)) {
+ chatting$isHovering = true;
+ drawCopyChatBox(right, top);
+ }
+ }
+ lineInBounds = false;
+ }
+
+ private boolean isInBounds(int left, int top, int right, int bottom, float chatScale) {
+ int mouseX = MathHelper.floor_double(UMouse.getScaledX()) - 3;
+ int mouseY = MathHelper.floor_double(UMouse.getScaledY()) - 27 + ModCompatHooks.getYOffset() - ModCompatHooks.getChatPosition();
+ mouseX = MathHelper.floor_float((float) mouseX / chatScale);
+ mouseY = -(MathHelper.floor_float((float) mouseY / chatScale)); //WHY DO I NEED TO DO THIS
+ return mouseX >= (left + ModCompatHooks.getXOffset()) && mouseY < bottom && mouseX < (right + 23 + ModCompatHooks.getXOffset()) && mouseY >= top;
+ }
+
+ @ModifyVariable(method = "drawChat", at = @At("STORE"), ordinal = 7)
+ private int modifyYeah(int value) {
+ return chatting$textOpacity = (int) (((float) (getChatOpen() ? 255 : value)) * (mc.gameSettings.chatOpacity * 0.9F + 0.1F));
+ }
+ /*/
+ @Inject(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;scale(FFF)V"))
+ private void drawPre(int updateCounter, CallbackInfo ci) {
+ RenderUtils.timestampPre();
+ }
+
+ @Inject(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;popMatrix()V"))
+ private void drawPost(int updateCounter, CallbackInfo ci) {
+ RenderUtils.timestampPost();
+ }
+
+ */
+
+ @Inject(method = "drawChat", at = @At("RETURN"))
+ private void checkStuff(int j2, CallbackInfo ci) {
+ if (!chatting$chatCheck && chatting$isHovering) {
+ chatting$isHovering = false;
+ }
+ }
+
+ @Override
+ public int getRight() {
+ return chatting$right;
+ }
+
+ @Override
+ public boolean isHovering() {
+ return chatting$isHovering;
+ }
+
+ private void drawCopyChatBox(int right, int top) {
+ chatting$chatCheck = true;
+ GlStateManager.enableRescaleNormal();
+ GlStateManager.enableBlend();
+ GlStateManager.enableDepth();
+ GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0);
+ GlStateManager.pushMatrix();
+ mc.getTextureManager().bindTexture(COPY);
+ GlStateManager.enableRescaleNormal();
+ GlStateManager.enableAlpha();
+ GlStateManager.alphaFunc(516, 0.1f);
+ GlStateManager.enableBlend();
+ GlStateManager.blendFunc(770, 771);
+ GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
+ chatting$right = right;
+ drawModalRectWithCustomSizedTexture(right + 1, top, 0f, 0f, 9, 9, 9, 9);
+ drawRect(right + 1, top, right + 10, top + 9, (((right + ModCompatHooks.getXOffset() + 3) <= (UMouse.getScaledX() / mc.ingameGUI.getChatGUI().getChatScale()) && (right + ModCompatHooks.getXOffset()) + 13 > (UMouse.getScaledX() / mc.ingameGUI.getChatGUI().getChatScale())) ? ChattingConfig.INSTANCE.getChatButtonHoveredBackgroundColor().getRGB() : ChattingConfig.INSTANCE.getChatButtonBackgroundColor().getRGB()));
+ GlStateManager.disableAlpha();
+ GlStateManager.disableRescaleNormal();
+ mc.getTextureManager().bindTexture(DELETE);
+ GlStateManager.enableRescaleNormal();
+ GlStateManager.enableAlpha();
+ GlStateManager.alphaFunc(516, 0.1f);
+ GlStateManager.enableBlend();
+ GlStateManager.blendFunc(770, 771);
+ GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
+ drawModalRectWithCustomSizedTexture(right + 11, top, 0f, 0f, 9, 9, 9, 9);
+ drawRect(right + 11, top, right + 20, top + 9, (((right + ModCompatHooks.getXOffset() + 13) <= (UMouse.getScaledX() / mc.ingameGUI.getChatGUI().getChatScale()) && (right + ModCompatHooks.getXOffset()) + 23 > (UMouse.getScaledX() / mc.ingameGUI.getChatGUI().getChatScale())) ? ChattingConfig.INSTANCE.getChatButtonHoveredBackgroundColor().getRGB() : ChattingConfig.INSTANCE.getChatButtonBackgroundColor().getRGB()));
+ GlStateManager.disableLighting();
+ GlStateManager.popMatrix();
+ }
+
+ @Override
+ public ChatLine getHoveredLine(int mouseY) {
+ if (this.getChatOpen()) {
+ ScaledResolution scaledresolution = new ScaledResolution(this.mc);
+ int i = scaledresolution.getScaleFactor();
+ float f = this.getChatScale();
+ int k = mouseY / i - 27 + ModCompatHooks.getYOffset() - ModCompatHooks.getChatPosition();
+ k = MathHelper.floor_float((float) k / f);
+
+ if (k >= 0) {
+ int l = Math.min(this.getLineCount(), this.drawnChatLines.size());
+
+ if (k < this.mc.fontRendererObj.FONT_HEIGHT * l + l) {
+ int i1 = k / this.mc.fontRendererObj.FONT_HEIGHT + this.scrollPos;
+
+ if (i1 >= 0 && i1 < this.drawnChatLines.size()) {
+ List<ChatLine> m = ChatSearchingManager.filterMessages(ChatSearchingManager.INSTANCE.getLastSearch(), this.drawnChatLines);
+ return m != null ? m.get(i1) : null;
+ }
+
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Transferable getChattingChatComponent(int mouseY) {
+ ChatLine subLine = getHoveredLine(mouseY);
+ if (subLine != null) {
+ ChatLine fullLine = this.getFullMessage(subLine);
+ if (GuiScreen.isShiftKeyDown()) {
+ if (fullLine != null) {
+ BufferedImage image = Chatting.INSTANCE.screenshotLine(subLine);
+ if (image != null) RenderUtils.copyToClipboard(image);
+ }
+ return null;
+ }
+ ChatLine line = GuiScreen.isCtrlKeyDown() ? subLine : fullLine;
+ String message = line == null ? "Could not find chat message." : line.getChatComponent().getFormattedText();
+ String actualMessage = GuiScreen.isAltKeyDown() ? message : EnumChatFormatting.getTextWithoutFormattingCodes(message);
+ Notifications.INSTANCE.send("Chatting", line == null ? "Could not find chat message." : "Copied following text: " + actualMessage);
+ return new StringSelection(actualMessage);
+ }
+ return null;
+ }
+
+ @Override
+ public int getTextOpacity() {
+ return chatting$textOpacity;
+ }
+}
diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatHeight.java b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatHeight.java
new file mode 100644
index 0000000..3b6d8b6
--- /dev/null
+++ b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatHeight.java
@@ -0,0 +1,21 @@
+package org.polyfrost.chatting.mixin;
+
+import net.minecraft.client.gui.GuiNewChat;
+import org.polyfrost.chatting.Chatting;
+import org.polyfrost.chatting.config.ChattingConfig;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+@Mixin(GuiNewChat.class)
+public abstract class GuiNewChatMixin_ChatHeight {
+ @Shadow public abstract boolean getChatOpen();
+
+ @Inject(method = "getChatHeight", at = @At("HEAD"), cancellable = true)
+ private void customHeight_getChatHeight(CallbackInfoReturnable<Integer> cir) {
+ if (ChattingConfig.INSTANCE.getCustomChatHeight())
+ cir.setReturnValue(Chatting.INSTANCE.getChatHeight(getChatOpen()));
+ }
+}
diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatSearching.java b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatSearching.java
new file mode 100644
index 0000000..f1ad3a4
--- /dev/null
+++ b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatSearching.java
@@ -0,0 +1,31 @@
+package org.polyfrost.chatting.mixin;
+
+import net.minecraft.client.gui.ChatLine;
+import net.minecraft.client.gui.GuiNewChat;
+import net.minecraft.util.IChatComponent;
+import org.objectweb.asm.Opcodes;
+import org.polyfrost.chatting.chat.ChatSearchingManager;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.Redirect;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.util.List;
+
+@Mixin(GuiNewChat.class)
+public class GuiNewChatMixin_ChatSearching {
+ @Shadow @Final private List<ChatLine> drawnChatLines;
+
+ @Inject(method = "setChatLine", at = @At("HEAD"))
+ private void handleSetChatLine(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) {
+ ChatSearchingManager.getCache().invalidateAll();
+ }
+
+ @Redirect(method = "drawChat", at = @At(value = "FIELD", target = "Lnet/minecraft/client/gui/GuiNewChat;drawnChatLines:Ljava/util/List;", opcode = Opcodes.GETFIELD))
+ private List<ChatLine> injected(GuiNewChat instance) {
+ return ChatSearchingManager.filterMessages(ChatSearchingManager.INSTANCE.getLastSearch(), drawnChatLines);
+ }
+}
diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatTabs.java b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatTabs.java
new file mode 100644
index 0000000..c101e6d
--- /dev/null
+++ b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatTabs.java
@@ -0,0 +1,54 @@
+package org.polyfrost.chatting.mixin;
+
+import org.polyfrost.chatting.chat.ChatTabs;
+import org.polyfrost.chatting.config.ChattingConfig;
+import org.polyfrost.chatting.utils.ModCompatHooks;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.ChatLine;
+import net.minecraft.client.gui.GuiNewChat;
+import net.minecraft.util.IChatComponent;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+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(value = GuiNewChat.class, priority = 990)
+public abstract class GuiNewChatMixin_ChatTabs {
+ @Shadow @Final private Minecraft mc;
+
+ @Shadow public abstract void deleteChatLine(int id);
+
+ @Shadow @Final private List<ChatLine> chatLines;
+
+ @Inject(method = "printChatMessageWithOptionalDeletion", at = @At("HEAD"), cancellable = true)
+ private void handlePrintChatMessage(IChatComponent chatComponent, int chatLineId, CallbackInfo ci) {
+ handleChatTabMessage(chatComponent, chatLineId, mc.ingameGUI.getUpdateCounter(), false, ci);
+ }
+
+ @Inject(method = "setChatLine", at = @At("HEAD"), cancellable = true)
+ private void handleSetChatLine(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) {
+ handleChatTabMessage(chatComponent, chatLineId, updateCounter, displayOnly, ci);
+ }
+
+ private void handleChatTabMessage(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) {
+ if (ChattingConfig.INSTANCE.getChatTabs()) {
+ if (!ChatTabs.INSTANCE.shouldRender(chatComponent)) {
+ ChatTabs.INSTANCE.setHasCancelledAnimation(true);
+ if (chatLineId != 0) {
+ deleteChatLine(chatLineId);
+ }
+ if (!displayOnly) {
+ chatLines.add(0, new ChatLine(updateCounter, chatComponent, chatLineId));
+ while (this.chatLines.size() > (100 + ModCompatHooks.getExtendedChatLength())) {
+ this.chatLines.remove(this.chatLines.size() - 1);
+ }
+ }
+ ci.cancel();
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_Scrolling.java b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_Scrolling.java
new file mode 100644
index 0000000..9a5a871
--- /dev/null
+++ b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_Scrolling.java
@@ -0,0 +1,71 @@
+package org.polyfrost.chatting.mixin;
+
+import cc.polyfrost.oneconfig.gui.animations.EaseOutQuad;
+import cc.polyfrost.oneconfig.utils.MathUtils;
+import org.polyfrost.chatting.chat.ChatScrollingHook;
+import net.minecraft.client.gui.ChatLine;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.GuiNewChat;
+import org.polyfrost.chatting.Chatting;
+import org.polyfrost.chatting.config.ChattingConfig;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+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.Redirect;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.util.List;
+
+@Mixin(GuiNewChat.class)
+public abstract class GuiNewChatMixin_Scrolling extends Gui {
+ @Shadow private int scrollPos;
+ @Shadow @Final private List<ChatLine> drawnChatLines;
+
+ @Shadow public abstract int getLineCount();
+
+ @Shadow private boolean isScrolled;
+
+ @Unique
+ private EaseOutQuad chatting$scrollingAnimation;
+
+ @Inject(method = "drawChat", at = @At("HEAD"))
+ private void chatting$scrollingAnimationStart(int updateCounter, CallbackInfo ci) {
+ if (chatting$scrollingAnimation != null) {
+ if (chatting$scrollingAnimation.isFinished()) {
+ if (scrollPos == 0) {
+ isScrolled = false;
+ }
+ chatting$scrollingAnimation = null;
+ } else {
+ scrollPos = (int) chatting$scrollingAnimation.get(Chatting.INSTANCE.getDeltaTime());
+ }
+ }
+ }
+
+ @Redirect(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiNewChat;drawRect(IIIII)V", ordinal = 1))
+ private void redirectScrollBar(int left, int top, int right, int bottom, int color) {
+ if (!ChattingConfig.INSTANCE.getRemoveScrollBar()) {
+ drawRect(left, top, right, bottom, color);
+ }
+ }
+
+ @Redirect(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiNewChat;drawRect(IIIII)V", ordinal = 2))
+ private void redirectScrollBar2(int left, int top, int right, int bottom, int color) {
+ if (!ChattingConfig.INSTANCE.getRemoveScrollBar()) {
+ drawRect(left, top, right, bottom, color);
+ }
+ }
+
+ @Inject(method = "scroll", at = @At("HEAD"), cancellable = true)
+ private void injectScroll(int amount, CallbackInfo ci) {
+ if (ChattingConfig.INSTANCE.getSmoothScrolling() && amount != 0 && ChatScrollingHook.INSTANCE.getShouldSmooth()) {
+ ci.cancel();
+ ChatScrollingHook.INSTANCE.setShouldSmooth(false);
+ int result = (int) MathUtils.clamp(scrollPos + amount, 0, Math.max(drawnChatLines.size() - getLineCount() - 1, 0));
+ chatting$scrollingAnimation = new EaseOutQuad((int) (ChattingConfig.INSTANCE.getScrollingSpeed() * 1000), scrollPos, result, false);
+ }
+ }
+}
diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_SmoothMessages.java b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_SmoothMessages.java
new file mode 100644
index 0000000..28f2aa8
--- /dev/null
+++ b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_SmoothMessages.java
@@ -0,0 +1,103 @@
+package org.polyfrost.chatting.mixin;
+
+import org.polyfrost.chatting.Chatting;
+import org.polyfrost.chatting.chat.ChatSearchingManager;
+import org.polyfrost.chatting.chat.ChatTabs;
+import org.polyfrost.chatting.config.ChattingConfig;
+import org.polyfrost.chatting.utils.EaseOutQuart;
+import org.polyfrost.chatting.utils.ModCompatHooks;
+import net.minecraft.client.gui.GuiNewChat;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.IChatComponent;
+import org.spongepowered.asm.mixin.Mixin;
+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.ModifyArg;
+import org.spongepowered.asm.mixin.injection.ModifyVariable;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Taken from BetterChat under LGPL 3.0
+ * <a href="https://github.com/LlamaLad7/Better-Chat/blob/1.8.9/LICENSE">https://github.com/LlamaLad7/Better-Chat/blob/1.8.9/LICENSE</a>
+ */
+@Mixin(GuiNewChat.class)
+public abstract class GuiNewChatMixin_SmoothMessages {
+ @Shadow
+ private boolean isScrolled;
+
+ @Shadow
+ public abstract float getChatScale();
+ @Unique
+ private int chatting$newLines;
+
+ private EaseOutQuart chatting$easeOutQuart;
+ private float chatting$animationPercent;
+ @Unique
+ private int chatting$lineBeingDrawn;
+
+ @Inject(method = "drawChat", at = @At("HEAD"))
+ private void modifyChatRendering(CallbackInfo ci) {
+ if (chatting$easeOutQuart != null) {
+ if (chatting$easeOutQuart.isFinished()) {
+ chatting$easeOutQuart = null;
+ } else {
+ chatting$animationPercent = chatting$easeOutQuart.get(Chatting.INSTANCE.getDeltaTime());
+ }
+ } else {
+ chatting$animationPercent = 1;
+ }
+ }
+
+ @Inject(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;pushMatrix()V", ordinal = 0, shift = At.Shift.AFTER))
+ private void translate(CallbackInfo ci) {
+ float y = 0;
+ if (ChattingConfig.INSTANCE.getSmoothChat() && !this.isScrolled) {
+ y += (9 - 9 * chatting$animationPercent) * this.getChatScale();
+ }
+ GlStateManager.translate(0, y, 0);
+ }
+
+ @ModifyArg(method = "drawChat", at = @At(value = "INVOKE", target = "Ljava/util/List;get(I)Ljava/lang/Object;", ordinal = 0, remap = false), index = 0)
+ private int getLineBeingDrawn(int line) {
+ chatting$lineBeingDrawn = line;
+ return line;
+ }
+
+ @ModifyArg(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/FontRenderer;drawStringWithShadow(Ljava/lang/String;FFI)I"))
+ private int modifyTextOpacity(int original) {
+ if (ChattingConfig.INSTANCE.getSmoothChat() && chatting$lineBeingDrawn <= chatting$newLines) {
+ int opacity = (original >> 24) & 0xFF;
+ opacity *= chatting$animationPercent;
+ return (original & ~(0xFF << 24)) | (opacity << 24);
+ } else {
+ return original;
+ }
+ }
+
+ @Inject(method = "printChatMessageWithOptionalDeletion", at = @At("HEAD"))
+ private void resetPercentage(IChatComponent chatComponent, int chatLineId, CallbackInfo ci) {
+ if (!EnumChatFormatting.getTextWithoutFormattingCodes(chatComponent.getUnformattedText()).toLowerCase(Locale.ENGLISH).contains(ChatSearchingManager.INSTANCE.getLastSearch().toLowerCase(Locale.ENGLISH))) {
+ return;
+ }
+ if (ModCompatHooks.getBetterChatSmoothMessages()) {
+ return;
+ }
+ if (ChatTabs.INSTANCE.getHasCancelledAnimation()) {
+ ChatTabs.INSTANCE.setHasCancelledAnimation(false);
+ return;
+ }
+ chatting$easeOutQuart = new EaseOutQuart((1.0f - ChattingConfig.INSTANCE.getMessageSpeed()) * 1000f, 0f, 1f, false);
+ }
+
+ @ModifyVariable(method = "setChatLine", at = @At("STORE"), ordinal = 0)
+ private List<IChatComponent> setNewLines(List<IChatComponent> original) {
+ chatting$newLines = original.size() - 1;
+ return original;
+ }
+}
diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_TextRendering.java b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_TextRendering.java
new file mode 100644
index 0000000..aff102c
--- /dev/null
+++ b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_TextRendering.java
@@ -0,0 +1,29 @@
+package org.polyfrost.chatting.mixin;
+
+import net.minecraft.client.gui.ChatLine;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.GuiNewChat;
+import org.polyfrost.chatting.utils.ModCompatHooks;
+import org.spongepowered.asm.mixin.Mixin;
+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.Redirect;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
+
+@Mixin(value = GuiNewChat.class, priority = 990)
+public class GuiNewChatMixin_TextRendering {
+ @Unique
+ private ChatLine chatting$drawingLine = null;
+
+ @Inject(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/ChatLine;getChatComponent()Lnet/minecraft/util/IChatComponent;"), locals = LocalCapture.CAPTURE_FAILSOFT)
+ private void captureChatLine(int updateCounter, CallbackInfo ci, int i, boolean bl, int j, int k, float f, float g, int l, int m, ChatLine chatLine, int n, double d, int o, int p, int q) {
+ chatting$drawingLine = chatLine;
+ }
+
+ @Redirect(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/FontRenderer;drawStringWithShadow(Ljava/lang/String;FFI)I"))
+ private int redirectDrawString(FontRenderer instance, String text, float x, float y, int color) {
+ return ModCompatHooks.redirectDrawString(text, x, y, color, chatting$drawingLine, false);
+ }
+}
diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiUtilsMixin.java b/src/main/java/org/polyfrost/chatting/mixin/GuiUtilsMixin.java
new file mode 100644
index 0000000..1e2d0e6
--- /dev/null
+++ b/src/main/java/org/polyfrost/chatting/mixin/GuiUtilsMixin.java
@@ -0,0 +1,36 @@
+package org.polyfrost.chatting.mixin;
+
+import cc.polyfrost.oneconfig.renderer.TextRenderer;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraftforge.fml.client.config.GuiUtils;
+import org.polyfrost.chatting.config.ChattingConfig;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Redirect;
+
+@Mixin(value = GuiUtils.class, remap = false)
+public class GuiUtilsMixin {
+ @Shadow
+ public static void drawGradientRect(int zLevel, int left, int top, int right, int bottom, int startColor, int endColor) {
+ }
+
+ @Redirect(method = "drawHoveringText", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/client/config/GuiUtils;drawGradientRect(IIIIIII)V"))
+ private static void redirectBackground(int zLevel, int left, int top, int right, int bottom, int startColor, int endColor) {
+ if (!ChattingConfig.INSTANCE.getRemoveTooltipBackground()) {
+ drawGradientRect(zLevel, left, top, right, bottom, startColor, endColor);
+ }
+ }
+
+ @Redirect(method = "drawHoveringText", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/FontRenderer;drawStringWithShadow(Ljava/lang/String;FFI)I"))
+ private static int redirectText(FontRenderer instance, String text, float x, float y, int color) {
+ switch (ChattingConfig.INSTANCE.getTooltipTextRenderType()) {
+ case 0:
+ return instance.drawString(text, x, y, color, false);
+ case 2:
+ return TextRenderer.drawBorderedText(text, x, y, color, 255);
+ default:
+ return instance.drawStringWithShadow(text, x, y, color);
+ }
+ }
+}