package de.cowtipper.cowlection.config.gui;
import de.cowtipper.cowlection.Cowlection;
import de.cowtipper.cowlection.util.MooChatComponent;
import de.cowtipper.cowlection.util.TickDelay;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.*;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IChatComponent;
import net.minecraftforge.client.event.ClientChatReceivedEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.client.GuiScrollingList;
import net.minecraftforge.fml.client.config.GuiButtonExt;
import net.minecraftforge.fml.client.config.GuiConfigEntries;
import net.minecraftforge.fml.client.config.GuiEditArrayEntries;
import net.minecraftforge.fml.client.config.GuiUtils;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import java.awt.*;
import java.net.URI;
import java.util.List;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Based on GuiModList
*/
public class MooConfigBestFriendsMigration extends GuiScreen {
/**
*
* §9§m-----------------------------------------------------
* §r§a[VIP] PLAYER§r§a is now a best friend!§r§9§m
* -----------------------------------------------------§r
*
* §9§m-----------------------------------------------------
* §r§a[VIP] PLAYER§r§e is no longer a best friend!§r§9§m
* -----------------------------------------------------§r
*
* §9§m-----------------------------------------------------
* §r§6[MVP§r§d++§r§6] PLAYER§r§c isn't on your friends list!§r§9§m
* -----------------------------------------------------§r
*
*/
private static final Pattern BEST_FRIEND_CHAT_PATTERN = Pattern.compile("-{20,}\\s*(?:\\[[^]]+] )?(\\w+)([^\n]+)\\s*-{20,}");
private BestFriendsListGui bestFriendsGui;
private GuiButton btnRemoveAll;
private GuiButton btnHelp;
private GuiButton btnClose;
private boolean isMigrating = false;
@Override
public void onGuiClosed() {
MinecraftForge.EVENT_BUS.unregister(this);
if (Cowlection.getInstance().getFriendsHandler().getBestFriendsListSize() == 0) {
new TickDelay(() -> Cowlection.getInstance().getChatHelper().sendMessage(new MooChatComponent("[§2Cowlection§a] All Cowlection best friends have been migrated or removed.").green()
.appendFreshSibling(new MooChatComponent("You can now use Hypixel's commands instead: §b[open patch notes]").green().setUrl("https://hypixel.net/threads/social-update-online-status-best-friends-more.4638020/")
.appendFreshSibling(new MooChatComponent(" §6➊ §aView best friends list: §2/friend list best §aor §2/fl best").green().setSuggestCommand("/fl best"))
.appendFreshSibling(new MooChatComponent(" §6➋ §aAdd or remove best friend: §2/friend best ").green().setSuggestCommand("/f best ")))),
isMigrating ? 20 : 0);
}
}
// priority = highest to ignore other mods modifying the chat output
@SubscribeEvent(priority = EventPriority.HIGHEST, receiveCanceled = true)
public void onBestFriendsAddResponse(ClientChatReceivedEvent e) {
if (e.type == 2 || !isMigrating) return;
String text = EnumChatFormatting.getTextWithoutFormattingCodes(e.message.getUnformattedText());
if (!text.startsWith("-----------------------------") || !text.endsWith("-----------------------------"))
return;
Matcher matcher = BEST_FRIEND_CHAT_PATTERN.matcher(text);
if (!matcher.matches()) return;
String username = matcher.group(1);
String rawFriendStatus = matcher.group(2);
int friendStatus;
if (rawFriendStatus.contains("no longer a best friend")) {
friendStatus = 0xFFaa0000;
} else if (rawFriendStatus.contains("now a best friend")) {
friendStatus = 0xFF00aa00;
} else if (rawFriendStatus.contains("isn't on your friends list")) {
friendStatus = 0xFFff5555;
} else {
return;
}
this.bestFriendsGui.setBestFriendStatus(username, friendStatus);
}
@Override
public void initGui() {
if (Cowlection.getInstance().getFriendsHandler().getBestFriendsListSize() == 0) {
mc.displayGuiScreen(null);
return;
}
MinecraftForge.EVENT_BUS.register(this);
this.buttonList.clear();
// remove all button
this.buttonList.add(this.btnRemoveAll = new GuiButtonExt(3, this.width - 69, 4, 20, 20, EnumChatFormatting.DARK_RED + "♺"));
// help button
this.buttonList.add(this.btnHelp = new GuiButtonExt(1, this.width - 47, 4, 20, 20, "?"));
// close button
this.buttonList.add(this.btnClose = new GuiButtonExt(2, this.width - 25, 4, 20, 20, EnumChatFormatting.RED + "X"));
// scrollable gui
this.bestFriendsGui = new BestFriendsListGui(this.width / 2, this.height, this.isMigrating);
}
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
this.drawGradientRect(this.width / 2, 0, this.width, this.height, -1072689136, -804253680); // from #drawDefaultBackground
super.drawScreen(mouseX, mouseY, partialTicks);
GlStateManager.pushMatrix();
double scaleFactor = 1.3;
GlStateManager.scale(scaleFactor, scaleFactor, 0);
this.drawString(this.fontRendererObj, "Cowlection: Best friends migration", (int) ((this.width / 2 + 8) / scaleFactor), 6, 0xFFCC00);
GlStateManager.popMatrix();
this.bestFriendsGui.drawScreen(mouseX, mouseY, partialTicks);
if (btnRemoveAll.isMouseOver()) {
List removeAllTooltip = new ArrayList<>();
removeAllTooltip.add(EnumChatFormatting.RED + "Discard/Remove " + EnumChatFormatting.DARK_RED + "all " + EnumChatFormatting.RED + "Cowlection best friends");
drawHoveringText(removeAllTooltip, mouseX, mouseY);
} else if (btnHelp.isMouseOver()) {
List helpTooltip = new ArrayList<>();
helpTooltip.add("" + EnumChatFormatting.GOLD + EnumChatFormatting.BOLD + "Cowlection: Best friends migration");
helpTooltip.add(EnumChatFormatting.GREEN + "If you have any questions or need help with the best friends list migration:");
helpTooltip.add(EnumChatFormatting.GREEN + "Join the Cowshed discord and open a ticket!");
drawHoveringText(helpTooltip, mouseX, mouseY);
} else if (btnClose.isMouseOver()) {
drawHoveringText(Arrays.asList(EnumChatFormatting.RED + "Save & Close", "" + EnumChatFormatting.GRAY + EnumChatFormatting.ITALIC + "Hint:" + EnumChatFormatting.RESET + " alternatively press ESC"), mouseX, mouseY);
}
}
@Override
protected void actionPerformed(GuiButton button) {
if (button == btnRemoveAll) {
// show best friends deletion confirmation screen
GuiYesNo guiDeleteRule = new GuiYesNo(MooConfigBestFriendsMigration.this,
EnumChatFormatting.RED + "Discard/remove " + EnumChatFormatting.DARK_RED + Cowlection.getInstance().getFriendsHandler().getBestFriendsListSize() + " (= all)" + EnumChatFormatting.RED + " from Cowlection best friends list?",
EnumChatFormatting.RED + "This action cannot be reverted!",
1337);
mc.displayGuiScreen(guiDeleteRule);
} else if (button == btnHelp) {
GuiConfirmOpenLink guiHelp = new GuiConfirmOpenLink(this, Cowlection.INVITE_URL, 9001, true);
guiHelp.disableSecurityWarning();
mc.displayGuiScreen(guiHelp);
} else if (button == btnClose) {
mc.displayGuiScreen(null);
}
}
@Override
public void confirmClicked(boolean result, int id) {
super.confirmClicked(result, id);
if (result) {
if (id == 1337) {
Cowlection.getInstance().getFriendsHandler().removeAllBestFriends();
} else if (id == 9001) {
try {
Desktop.getDesktop().browse(new URI(Cowlection.INVITE_URL));
} catch (Throwable throwable) {
Cowlection.getInstance().getLogger().error("Couldn't open link " + Cowlection.INVITE_URL, throwable);
}
} else if (id < 500) {
// user confirmed rule deletion
BestFriendsListGui.BestFriendEntry removedBestFriendEntry = this.bestFriendsGui.bestFriendEntries.get(id);
if (removedBestFriendEntry != null) {
removedBestFriendEntry.removeBestFriend();
}
}
}
mc.displayGuiScreen(this);
}
/**
* Based on GuiModList.Info
*/
private class BestFriendsListGui extends GuiScrollingList {
private final MooChatComponent BEST_FRIENDS_MIGRATIONS_GUIDE = new MooChatComponent("The 'best friends list' feature has been removed from this mod, as Hypixel added a very similar feature ").red()
.appendSibling(new MooChatComponent("[click to open patch notes]")
.darkAqua()
.setUrl("https://hypixel.net/threads/social-update-online-status-best-friends-more.4638020/"))
.appendFreshSibling(new MooChatComponent("You can either…\n §e➊ §ftry to add someone from your Cowlection best friends list to your Hypixel best friends list (§2+§f button), or\n §e➋ §fdiscard/remove them from your Cowlection best friends list without trying to add them on Hypixel as a best friend (§c♺§f button)."
+ "\n§4§l❢ §eOnly players who are already on your §6normal §eHypixel friends list can be added as a best friends."))
.appendFreshSibling(new MooChatComponent("➜ Start migrating my clicking the button at the top.").gray());
private final List introduction;
private final List bestFriendEntries;
private final boolean isMigrating;
private final GuiButton startMigratingButton;
private BestFriendEntry hoveredEntry;
public BestFriendsListGui(int width, int height, boolean isMigrating) {
super(MooConfigBestFriendsMigration.this.mc,
width - 6, height,
27, height - 3, width + 3, 18,
MooConfigBestFriendsMigration.this.width, MooConfigBestFriendsMigration.this.height);
setHeaderInfo(true, isMigrating ? 20 : 30);
this.bestFriendEntries = isMigrating ? convertToEntries() : Collections.emptyList();
this.introduction = isMigrating ? Collections.emptyList() : this.resizeContent();
this.startMigratingButton = isMigrating ? null : new GuiButtonExt(21000, this.left + 25, 0, 170, 20, EnumChatFormatting.DARK_GREEN + "➜ " + EnumChatFormatting.GREEN + "Start best friends migration");
this.isMigrating = isMigrating;
}
private List convertToEntries() {
Set bestFriends = Cowlection.getInstance().getFriendsHandler().getBestFriends();
return bestFriends.stream().map(BestFriendEntry::new).collect(Collectors.toList());
}
public void setBestFriendStatus(String username, int friendStatus) {
for (BestFriendEntry bestFriendEntry : this.bestFriendEntries) {
if (bestFriendEntry.bestFriend.equals(username)) {
bestFriendEntry.setStatus(friendStatus);
}
}
}
@Override
protected void drawHeader(int entryRight, int relativeY, Tessellator tess) {
if (relativeY < 0) return;
boolean hasEntriesLeft = this.bestFriendEntries.size() > 0;
if (hasEntriesLeft) {
MooConfigBestFriendsMigration.this.fontRendererObj.drawStringWithShadow(EnumChatFormatting.GREEN + "Migrate " + EnumChatFormatting.WHITE + "or " + EnumChatFormatting.RED + "remove " + EnumChatFormatting.WHITE + "best friend", this.left + 6, relativeY + 5, 0xFFCC00);
} else if (!isMigrating && startMigratingButton != null) {
this.startMigratingButton.yPosition = relativeY + 3;
this.startMigratingButton.drawButton(mc, mouseX, mouseY);
}
}
@Override
protected void clickHeader(int x, int y) {
super.clickHeader(x, y);
if (this.startMigratingButton.isMouseOver()) {
MooConfigBestFriendsMigration.this.isMigrating = true;
MooConfigBestFriendsMigration.this.initGui();
}
}
private List resizeContent() {
return new ArrayList<>(GuiUtilRenderComponents.splitText(BEST_FRIENDS_MIGRATIONS_GUIDE,
this.listWidth - 8, MooConfigBestFriendsMigration.this.fontRendererObj, false, true));
}
@Override
protected int getSize() {
return isMigrating ? this.bestFriendEntries.size() : this.introduction.size();
}
@Override
protected void elementClicked(int index, boolean doubleClick) {
if (isMigrating) {
this.bestFriendEntries.get(index).mousePressed(mouseX, mouseY, index);
} else {
IChatComponent line = introduction.get(index);
if (line != null) {
int xOffset = this.left;
for (IChatComponent part : line) {
if (!(part instanceof ChatComponentText)) {
continue;
}
xOffset += MooConfigBestFriendsMigration.this.fontRendererObj.getStringWidth(((ChatComponentText) part).getChatComponentText_TextValue());
if (xOffset >= this.mouseX) {
MooConfigBestFriendsMigration.this.handleComponentClick(part);
break;
}
}
}
}
}
@Override
protected boolean isSelected(int index) {
return false;
}
@Override
protected void drawBackground() {
}
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
hoveredEntry = null;
super.drawScreen(mouseX, mouseY, partialTicks);
if (hoveredEntry != null) {
hoveredEntry.drawToolTip(mouseX, mouseY);
}
}
@Override
protected void drawSlot(int slotIdx, int entryRight, int slotTop, int slotBuffer, Tessellator tess) {
if (this.isMigrating) {
BestFriendEntry bestFriendEntry = bestFriendEntries.get(slotIdx);
if (bestFriendEntry != null) {
bestFriendEntry.drawEntry(this.left + 4, slotTop, this.right - 4);
if (mouseY >= slotTop && mouseY < slotTop + this.slotHeight) {
// mouse is over this slot
hoveredEntry = bestFriendEntry;
}
}
} else {
IChatComponent line = introduction.get(slotIdx);
if (line != null) {
MooConfigBestFriendsMigration.this.fontRendererObj.drawStringWithShadow(line.getFormattedText(), this.left + 4, slotTop, 0xFFffffff);
}
}
}
/**
* Based on:
*
* @see GuiConfigEntries.StringEntry
* @see GuiEditArrayEntries.BaseEntry
*/
private class BestFriendEntry {
private final String bestFriend;
private final GuiButton btnAdd;
private final List btnAddTooltip;
private final GuiButton btnRemove;
private final List btnRemoveTooltip;
private int friendStatus = 0xFFffffff;
public BestFriendEntry(String bestFriend) {
this.bestFriend = bestFriend;
this.btnAdd = new GuiButtonExt(50, 0, 0, 18, 16, "+");
this.btnAdd.packedFGColour = GuiUtils.getColorCode('2', true);
this.btnAddTooltip = new ArrayList<>();
this.btnAddTooltip.add("Try to §aadd §e" + bestFriend + " §fto Hypixel best friends list");
this.btnAddTooltip.add("§7Runs the command §8/friend best " + bestFriend);
this.btnRemove = new GuiButtonExt(50, 0, 0, 18, 16, "♺");
this.btnRemove.packedFGColour = GuiUtils.getColorCode('c', true);
this.btnRemoveTooltip = new ArrayList<>();
this.btnRemoveTooltip.add("§cRemove §e" + bestFriend + " §ffrom your Cowlection best friends list");
this.btnRemoveTooltip.add("§7Runs the command §8/moo remove " + bestFriend);
}
public void setStatus(int friendStatus) {
this.friendStatus = friendStatus;
}
public void drawEntry(int x, int y, int right) {
int currentX = x;
btnAdd.xPosition = currentX;
btnAdd.yPosition = y - 1;
btnAdd.drawButton(mc, mouseX, mouseY);
currentX += btnAdd.width + 3;
btnRemove.xPosition = currentX;
btnRemove.yPosition = y - 1;
btnRemove.drawButton(mc, mouseX, mouseY);
currentX += btnRemove.width + 3;
MooConfigBestFriendsMigration.this.fontRendererObj.drawStringWithShadow(this.bestFriend, (currentX + 1), (y + 4), friendStatus);
if (this.friendStatus < 0xFFffffff) {
drawRect(x, y - 1, right, y + 15, 0x99666666);
}
}
public void drawToolTip(int mouseX, int mouseY) {
if (btnAdd.isMouseOver()) {
drawHoveringText(this.btnAddTooltip, mouseX, mouseY);
} else if (btnRemove.isMouseOver()) {
drawHoveringText(btnRemoveTooltip, mouseX, mouseY);
}
}
public void mousePressed(int mouseX, int mouseY, int slotIndex) {
if (btnAdd.mousePressed(mc, mouseX, mouseY)) {
Minecraft.getMinecraft().thePlayer.sendChatMessage("/friend best " + this.bestFriend);
this.removeBestFriend();
} else if (btnRemove.mousePressed(mc, mouseX, mouseY)) {
if (isShiftKeyDown()) {
this.removeBestFriend();
} else {
// show best friend deletion confirmation screen
GuiYesNo guiDeleteRule = new GuiYesNo(MooConfigBestFriendsMigration.this,
EnumChatFormatting.RED + "Discard/remove " + EnumChatFormatting.YELLOW + bestFriend + EnumChatFormatting.RESET + " from Cowlection best friends list?",
EnumChatFormatting.GRAY + "Hint: You can hold " + EnumChatFormatting.WHITE + "SHIFT " + EnumChatFormatting.GRAY + "while clicking the " + EnumChatFormatting.RED + "♺ " + EnumChatFormatting.GRAY + "button to skip this confirmation screen.",
slotIndex);
mc.displayGuiScreen(guiDeleteRule);
}
}
}
public void removeBestFriend() {
Cowlection.getInstance().getFriendsHandler().removeBestFriend(this.bestFriend);
this.friendStatus = 0xFFbbbbbb;
if (Cowlection.getInstance().getFriendsHandler().getBestFriendsListSize() == 0) {
mc.displayGuiScreen(null);
}
}
}
}
}