aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/de/hysky
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/de/hysky')
-rw-r--r--src/main/java/de/hysky/skyblocker/SkyblockerMod.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java9
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java34
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRule.java213
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRuleAnnouncementScreen.java49
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRuleConfigScreen.java326
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRulesConfigListWidget.java196
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRulesConfigScreen.java75
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRulesHandler.java167
9 files changed, 1070 insertions, 3 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
index dca41bd1..edd4241e 100644
--- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
+++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
@@ -5,7 +5,9 @@ import com.google.gson.GsonBuilder;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.debug.Debug;
import de.hysky.skyblocker.skyblock.*;
+import de.hysky.skyblocker.skyblock.chat.ChatRuleAnnouncementScreen;
import de.hysky.skyblocker.skyblock.crimson.kuudra.Kuudra;
+import de.hysky.skyblocker.skyblock.chat.ChatRulesHandler;
import de.hysky.skyblocker.skyblock.dungeon.*;
import de.hysky.skyblocker.skyblock.dungeon.partyfinder.PartyFinderScreen;
import de.hysky.skyblocker.skyblock.dungeon.puzzle.*;
@@ -107,6 +109,8 @@ public class SkyblockerMod implements ClientModInitializer {
CrystalsLocationsManager.init();
ChatMessageListener.init();
Shortcuts.init();
+ ChatRulesHandler.init();
+ ChatRuleAnnouncementScreen.init();
DiscordRPCManager.init();
LividColor.init();
FishingHelper.init();
diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
index 78458291..4a1fe614 100644
--- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
+++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
@@ -1192,6 +1192,15 @@ public class SkyblockerConfig {
@SerialEntry
public ChatFilterResult hideDicer = ChatFilterResult.PASS;
+
+ @SerialEntry
+ public ChatRuleConfig chatRuleConfig = new ChatRuleConfig();
+ }
+ public static class ChatRuleConfig {
+ @SerialEntry
+ public int announcementLength = 60;
+ @SerialEntry
+ public int announcementScale = 3;
}
public enum Info {
diff --git a/src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java
index acdc8169..0f95bcaa 100644
--- a/src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java
+++ b/src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java
@@ -2,10 +2,12 @@ package de.hysky.skyblocker.config.categories;
import de.hysky.skyblocker.config.ConfigUtils;
import de.hysky.skyblocker.config.SkyblockerConfig;
+import de.hysky.skyblocker.skyblock.chat.ChatRulesConfigScreen;
+import de.hysky.skyblocker.skyblock.dwarven.CrystalsHudConfigScreen;
import de.hysky.skyblocker.utils.chat.ChatFilterResult;
-import dev.isxander.yacl3.api.ConfigCategory;
-import dev.isxander.yacl3.api.Option;
-import dev.isxander.yacl3.api.OptionDescription;
+import dev.isxander.yacl3.api.*;
+import dev.isxander.yacl3.api.controller.IntegerSliderControllerBuilder;
+import net.minecraft.client.MinecraftClient;
import net.minecraft.text.Text;
public class MessageFilterCategory {
@@ -126,6 +128,32 @@ public class MessageFilterCategory {
newValue -> config.messages.hideDicer = newValue)
.controller(ConfigUtils::createEnumCyclingListController)
.build())
+ //chat rules options
+ .group(OptionGroup.createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules"))
+ .collapsed(false)
+ .option(ButtonOption.createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen"))
+ .text(Text.translatable("text.skyblocker.open"))
+ .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new ChatRulesConfigScreen(screen)))
+ .build())
+ .option(Option.<Integer>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.announcementLength"))
+ .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.announcementLength.@Tooltip")))
+ .binding(defaults.messages.chatRuleConfig.announcementLength,
+ () -> config.messages.chatRuleConfig.announcementLength,
+ newValue -> config.messages.chatRuleConfig.announcementLength = newValue)
+ .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(5, 200).step(1))
+ .build())
+ .option(Option.<Integer>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.announcementScale"))
+ .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.announcementScale.@Tooltip")))
+ .binding(defaults.messages.chatRuleConfig.announcementScale,
+ () -> config.messages.chatRuleConfig.announcementScale,
+ newValue -> config.messages.chatRuleConfig.announcementScale = newValue)
+ .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(1, 8).step(1))
+ .build())
+ .build())
.build();
}
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRule.java b/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRule.java
new file mode 100644
index 00000000..4877fdb6
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRule.java
@@ -0,0 +1,213 @@
+package de.hysky.skyblocker.skyblock.chat;
+
+import de.hysky.skyblocker.utils.Utils;
+import net.minecraft.sound.SoundEvent;
+
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+/**
+ * Data class to contain all the settings for a chat rule
+ */
+public class ChatRule {
+
+ private String name;
+
+ //inputs
+ private Boolean enabled;
+ private Boolean isPartialMatch;
+ private Boolean isRegex;
+ private Boolean isIgnoreCase;
+ private String filter;
+ private String validLocations;
+
+ //output
+ private Boolean hideMessage;
+ private Boolean showActionBar;
+ private Boolean showAnnouncement;
+ private String replaceMessage;
+ private SoundEvent customSound;
+ /**
+ * Creates a chat rule with default options.
+ */
+ public ChatRule(){
+ this.name = "New Rule";
+
+ this.enabled = true;
+ this.isPartialMatch = false;
+ this.isRegex = false;
+ this.isIgnoreCase = true;
+ this.filter = "";
+ this.validLocations = "";
+
+ this.hideMessage = true;
+ this.showActionBar = false;
+ this.showAnnouncement = false;
+ this.replaceMessage = null;
+ this.customSound = null;
+ }
+
+ public Boolean getEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public Boolean getPartialMatch() {
+ return isPartialMatch;
+ }
+
+ public void setPartialMatch(Boolean partialMatch) {
+ isPartialMatch = partialMatch;
+ }
+
+ public Boolean getRegex() {
+ return isRegex;
+ }
+
+ public void setRegex(Boolean regex) {
+ isRegex = regex;
+ }
+
+ public Boolean getIgnoreCase() {
+ return isIgnoreCase;
+ }
+
+ public void setIgnoreCase(Boolean ignoreCase) {
+ isIgnoreCase = ignoreCase;
+ }
+
+ public String getFilter() {
+ return filter;
+ }
+
+ public void setFilter(String filter) {
+ this.filter = filter;
+ }
+
+ public Boolean getHideMessage() {
+ return hideMessage;
+ }
+
+ public void setHideMessage(Boolean hideMessage) {
+ this.hideMessage = hideMessage;
+ }
+
+ public Boolean getShowActionBar() {
+ return showActionBar;
+ }
+
+ public void setShowActionBar(Boolean showActionBar) {
+ this.showActionBar = showActionBar;
+ }
+
+ public Boolean getShowAnnouncement() {
+ return showAnnouncement;
+ }
+
+ public void setShowAnnouncement(Boolean showAnnouncement) {
+ this.showAnnouncement = showAnnouncement;
+ }
+
+ public String getReplaceMessage() {
+ return replaceMessage;
+ }
+
+ public void setReplaceMessage(String replaceMessage) {
+ this.replaceMessage = replaceMessage;
+ }
+
+ public SoundEvent getCustomSound() {
+ return customSound;
+ }
+
+ public void setCustomSound(SoundEvent customSound) {
+ this.customSound = customSound;
+ }
+
+ public String getValidLocations() {
+ return validLocations;
+ }
+
+ public void setValidLocations(String validLocations) {
+ this.validLocations = validLocations;
+ }
+
+ /**
+ * checks every input option and if the games state and the inputted str matches them returns true.
+ * @param inputString the chat message to check if fits
+ * @return if the inputs are all true and the outputs should be performed
+ */
+ public Boolean isMatch(String inputString){
+ //enabled
+ if (!enabled) return false;
+
+ //ignore case
+ String testString;
+ String testFilter;
+ if (isIgnoreCase){
+ testString = inputString.toLowerCase();
+ testFilter = filter.toLowerCase();
+ }else {
+ testString = inputString;
+ testFilter = filter;
+ }
+
+ //filter
+ if (testFilter.isBlank()) return false;
+ if(isRegex) {
+ if (isPartialMatch) {
+ if (! Pattern.compile(testFilter).matcher(testString).find()) return false;
+ }else {
+ if (!testString.matches(testFilter)) return false;
+ }
+ } else{
+ if (isPartialMatch) {
+ if (!testString.contains(testFilter)) return false;
+ }else {
+ if (!testFilter.equals(testString)) return false;
+ }
+ }
+
+ //location
+ if (validLocations.isBlank()){ //if no locations do not check
+ return true;
+ }
+ String rawLocation = Utils.getLocationRaw();
+ Boolean isLocationValid = null;
+ for (String validLocation : validLocations.replace(" ", "").toLowerCase().split(",")) {//the locations are raw locations split by "," and start with ! if not locations
+ String rawValidLocation = ChatRulesHandler.locations.get(validLocation.replace("!",""));
+ if (rawValidLocation == null) continue;
+ if (validLocation.startsWith("!")) {//not location
+ if (Objects.equals(rawValidLocation, rawLocation.toLowerCase())) {
+ isLocationValid = false;
+ break;
+ }
+ }
+ else {
+ if (Objects.equals(rawValidLocation, rawLocation.toLowerCase())) { //normal location
+ isLocationValid = true;
+ break;
+ }
+ }
+ }
+ if (isLocationValid != null && isLocationValid){//if location is not in the list at all and is a not a "!" location or and is a normal location
+ return true;
+ }
+
+ return false;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
+
+
+
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRuleAnnouncementScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRuleAnnouncementScreen.java
new file mode 100644
index 00000000..2cb6ca5c
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRuleAnnouncementScreen.java
@@ -0,0 +1,49 @@
+package de.hysky.skyblocker.skyblock.chat;
+
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.text.Text;
+
+
+public class ChatRuleAnnouncementScreen {
+ private static final MinecraftClient CLIENT = MinecraftClient.getInstance();
+ private static float timer;
+ private static Text text = null;
+
+ public static void init() {
+ HudRenderCallback.EVENT.register((context, tickDelta) -> {
+ if (timer <= 0 || text == null) {
+ return;
+ }
+ render(context, tickDelta);
+ });
+ }
+
+ /**
+ * renders {@link ChatRuleAnnouncementScreen#text} to the middle of the top of the screen.
+ * @param context render context
+ * @param tickDelta difference from last render to remove from timer
+ */
+ private static void render(DrawContext context, float tickDelta) {
+ int scale = SkyblockerConfigManager.get().messages.chatRuleConfig.announcementScale;
+ //decrement timer
+ timer -= tickDelta;
+ //scale text up and center
+ MatrixStack matrices = context.getMatrices();
+ matrices.push();
+ matrices.translate(context.getScaledWindowWidth() / 2f, context.getScaledWindowHeight() * 0.3, 0f);
+ matrices.scale(scale, scale, 0f);
+ //render text
+ context.drawCenteredTextWithShadow(CLIENT.textRenderer,text,0, 0, 0xFFFFFF);
+
+ matrices.pop();
+ }
+
+ protected static void setText(Text newText) {
+ text = newText;
+ timer = SkyblockerConfigManager.get().messages.chatRuleConfig.announcementLength;
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRuleConfigScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRuleConfigScreen.java
new file mode 100644
index 00000000..3adfc6fe
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRuleConfigScreen.java
@@ -0,0 +1,326 @@
+package de.hysky.skyblocker.skyblock.chat;
+
+import it.unimi.dsi.fastutil.ints.IntIntPair;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.tooltip.Tooltip;
+import net.minecraft.client.gui.widget.ButtonWidget;
+import net.minecraft.client.gui.widget.TextFieldWidget;
+import net.minecraft.sound.SoundEvent;
+import net.minecraft.sound.SoundEvents;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Identifier;
+
+
+import java.awt.*;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static java.util.Map.entry;
+
+public class ChatRuleConfigScreen extends Screen {
+ private static final int SPACER_X = 5;
+ private static final int SPACER_Y = 25;
+
+ private final Map<MutableText, SoundEvent> soundsLookup = Map.ofEntries(
+ entry(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.pling"), SoundEvents.BLOCK_NOTE_BLOCK_PLING.value()),
+ entry(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.cave"), SoundEvents.AMBIENT_CAVE.value()),
+ entry(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.zombie"), SoundEvents.ENTITY_ZOMBIE_AMBIENT),
+ entry(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.crit"), SoundEvents.ENTITY_PLAYER_ATTACK_CRIT),
+ entry(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.arrowHit"), SoundEvents.ENTITY_ARROW_HIT_PLAYER),
+ entry(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.amethyst"), SoundEvents.BLOCK_AMETHYST_BLOCK_HIT),
+ entry(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.anvil"), SoundEvents.BLOCK_ANVIL_LAND)
+ );
+
+ private final int chatRuleIndex;
+ private final ChatRule chatRule;
+ private TextFieldWidget nameInput;
+ private TextFieldWidget filterInput;
+ private ButtonWidget partialMatchToggle;
+ private ButtonWidget regexToggle;
+ private ButtonWidget ignoreCaseToggle;
+ private TextFieldWidget locationsInput;
+ private ButtonWidget hideMessageToggle;
+ private ButtonWidget actionBarToggle;
+ private ButtonWidget announcementToggle;
+ private ButtonWidget soundsToggle;
+ private TextFieldWidget replaceMessageInput;
+
+ //textLocations
+ private IntIntPair nameLabelTextPos;
+ private IntIntPair inputsLabelTextPos;
+ private IntIntPair filterLabelTextPos;
+ private IntIntPair partialMatchTextPos;
+ private IntIntPair regexTextPos;
+ private IntIntPair ignoreCaseTextPos;
+ private IntIntPair locationLabelTextPos;
+ private IntIntPair outputsLabelTextPos;
+ private IntIntPair hideMessageTextPos;
+ private IntIntPair actionBarTextPos;
+ private IntIntPair announcementTextPos;
+ private IntIntPair customSoundLabelTextPos;
+ private IntIntPair replaceMessageLabelTextPos;
+
+ private int currentSoundIndex;
+
+ private final Screen parent;
+
+ public ChatRuleConfigScreen(Screen parent, int chatRuleIndex) {
+ super(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen"));
+ this.chatRuleIndex = chatRuleIndex;
+ this.chatRule = ChatRulesHandler.chatRuleList.get(chatRuleIndex);
+ this.parent = parent;
+ this.currentSoundIndex = getCurrentSoundIndex();
+ }
+
+ private int getCurrentSoundIndex() {
+ List<SoundEvent> soundOptions = soundsLookup.values().stream().toList();
+ Identifier ruleSoundId = chatRule.getCustomSound().getId();
+ for (int i = 0; i < soundOptions.size(); i++) {
+ if (soundOptions.get(i).getId().compareTo(ruleSoundId) == 0) {
+ return i;
+ }
+ }
+ //not found
+ return -1;
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+ if (client == null) return;
+ //start centered on the X and 1/3 down on the Y
+ IntIntPair currentPos = IntIntPair.of((this.width - getMaxUsedWidth()) / 2,(int)((this.height -getMaxUsedHeight()) * 0.33));
+ int lineXOffset;
+
+ nameLabelTextPos = currentPos;
+ lineXOffset = client.textRenderer.getWidth(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.name")) + SPACER_X;
+ nameInput = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, currentPos.leftInt() + lineXOffset, currentPos.rightInt(), 100, 20, Text.of(""));
+ nameInput.setText(chatRule.getName());
+ nameInput.setTooltip(Tooltip.of(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.name.@Tooltip")));
+ currentPos = IntIntPair.of(currentPos.leftInt(),currentPos.rightInt() + SPACER_Y);
+
+ inputsLabelTextPos = currentPos;
+ currentPos = IntIntPair.of(currentPos.leftInt() + 10 ,currentPos.rightInt() + SPACER_Y);
+
+ filterLabelTextPos = currentPos;
+ lineXOffset = client.textRenderer.getWidth(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.filter")) + SPACER_X;
+ filterInput = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, currentPos.leftInt() + lineXOffset, currentPos.rightInt(), 200, 20, Text.of(""));
+ filterInput.setMaxLength(96);
+ filterInput.setText(chatRule.getFilter());
+ filterInput.setTooltip(Tooltip.of(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.filter.@Tooltip")));
+ currentPos = IntIntPair.of(currentPos.leftInt(),currentPos.rightInt() + SPACER_Y);
+ lineXOffset = 0;
+
+ partialMatchTextPos = IntIntPair.of(currentPos.leftInt() + lineXOffset,currentPos.rightInt());
+ lineXOffset += client.textRenderer.getWidth(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.partialMatch")) + SPACER_X;
+ partialMatchToggle = ButtonWidget.builder(enabledButtonText(chatRule.getPartialMatch()), a -> {
+ chatRule.setPartialMatch(!chatRule.getPartialMatch());
+ partialMatchToggle.setMessage(enabledButtonText(chatRule.getPartialMatch()));
+ })
+ .position(currentPos.leftInt() + lineXOffset, currentPos.rightInt())
+ .size(75,20)
+ .tooltip(Tooltip.of(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.partialMatch.@Tooltip")))
+ .build();
+ lineXOffset += 75 + SPACER_X;
+ regexTextPos = IntIntPair.of(currentPos.leftInt() + lineXOffset,currentPos.rightInt());
+ lineXOffset += client.textRenderer.getWidth(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.regex")) + SPACER_X;
+ regexToggle = ButtonWidget.builder(enabledButtonText(chatRule.getRegex()), a -> {
+ chatRule.setRegex(!chatRule.getRegex());
+ regexToggle.setMessage(enabledButtonText(chatRule.getRegex()));
+ })
+ .position(currentPos.leftInt() + lineXOffset, currentPos.rightInt())
+ .size(75,20)
+ .tooltip(Tooltip.of(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.regex.@Tooltip")))
+ .build();
+ lineXOffset += 75 + SPACER_X;
+ ignoreCaseTextPos = IntIntPair.of(currentPos.leftInt() + lineXOffset,currentPos.rightInt());
+ lineXOffset += client.textRenderer.getWidth(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.ignoreCase")) + SPACER_X;
+ ignoreCaseToggle = ButtonWidget.builder(enabledButtonText(chatRule.getIgnoreCase()), a -> {
+ chatRule.setIgnoreCase(!chatRule.getIgnoreCase());
+ ignoreCaseToggle.setMessage(enabledButtonText(chatRule.getIgnoreCase()));
+ })
+ .position(currentPos.leftInt() + lineXOffset, currentPos.rightInt())
+ .size(75,20)
+ .tooltip(Tooltip.of(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.ignoreCase.@Tooltip")))
+ .build();
+ currentPos = IntIntPair.of(currentPos.leftInt(),currentPos.rightInt() + SPACER_Y);
+
+ locationLabelTextPos = currentPos;
+ lineXOffset = client.textRenderer.getWidth(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.locations")) + SPACER_X;
+ locationsInput = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, currentPos.leftInt() + lineXOffset, currentPos.rightInt(), 200, 20, Text.of(""));
+ locationsInput.setText(chatRule.getValidLocations());
+ MutableText locationToolTip = Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.locations.@Tooltip");
+ ChatRulesHandler.locationsList.forEach(location -> locationToolTip.append(" " + location + ",\n"));
+ locationsInput.setTooltip(Tooltip.of(locationToolTip));
+ currentPos = IntIntPair.of(currentPos.leftInt(),currentPos.rightInt() + SPACER_Y);
+
+ outputsLabelTextPos = IntIntPair.of(currentPos.leftInt() - 10,currentPos.rightInt());
+ currentPos = IntIntPair.of(currentPos.leftInt(),currentPos.rightInt() + SPACER_Y);
+
+ hideMessageTextPos = currentPos;
+ lineXOffset = client.textRenderer.getWidth(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.hideMessage")) + SPACER_X;
+ hideMessageToggle = ButtonWidget.builder(enabledButtonText(chatRule.getHideMessage()), a -> {
+ chatRule.setHideMessage(!chatRule.getHideMessage());
+ hideMessageToggle.setMessage(enabledButtonText(chatRule.getHideMessage()));
+ })
+ .position(currentPos.leftInt() + lineXOffset, currentPos.rightInt())
+ .size(75,20)
+ .tooltip(Tooltip.of(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.hideMessage.@Tooltip")))
+ .build();
+ lineXOffset += 75 + SPACER_X;
+ actionBarTextPos = IntIntPair.of(currentPos.leftInt() + lineXOffset,currentPos.rightInt());
+ lineXOffset += client.textRenderer.getWidth(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.actionBar")) + SPACER_X;
+ actionBarToggle = ButtonWidget.builder(enabledButtonText(chatRule.getShowActionBar()), a -> {
+ chatRule.setShowActionBar(!chatRule.getShowActionBar());
+ actionBarToggle.setMessage(enabledButtonText(chatRule.getShowActionBar()));
+ })
+ .position(currentPos.leftInt() + lineXOffset, currentPos.rightInt())
+ .size(75,20)
+ .tooltip(Tooltip.of(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.actionBar.@Tooltip")))
+ .build();
+ lineXOffset = 0;
+ currentPos = IntIntPair.of(currentPos.leftInt(),currentPos.rightInt() + SPACER_Y);
+
+ announcementTextPos = IntIntPair.of(currentPos.leftInt() + lineXOffset,currentPos.rightInt());
+ lineXOffset += client.textRenderer.getWidth(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.announcement")) + SPACER_X;
+ announcementToggle = ButtonWidget.builder(enabledButtonText(chatRule.getShowAnnouncement()), a -> {
+ chatRule.setShowAnnouncement(!chatRule.getShowAnnouncement());
+ announcementToggle.setMessage(enabledButtonText(chatRule.getShowAnnouncement()));
+ })
+ .position(currentPos.leftInt() + lineXOffset, currentPos.rightInt())
+ .size(75,20)
+ .tooltip(Tooltip.of(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.announcement.@Tooltip")))
+ .build();
+ lineXOffset += 75 + SPACER_X;
+ customSoundLabelTextPos = IntIntPair.of(currentPos.leftInt() + lineXOffset,currentPos.rightInt());
+ lineXOffset += client.textRenderer.getWidth(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds")) + SPACER_X;
+ soundsToggle = ButtonWidget.builder(getSoundName(), a -> {
+ currentSoundIndex += 1;
+ if (currentSoundIndex == soundsLookup.size()) {
+ currentSoundIndex = -1;
+ }
+ MutableText newText = getSoundName();
+ soundsToggle.setMessage(newText);
+ SoundEvent sound = soundsLookup.get(newText);
+ chatRule.setCustomSound(sound);
+ if (client.player != null && sound != null) {
+ client.player.playSound(sound, 100f, 0.1f);
+ }
+ })
+ .position(currentPos.leftInt() + lineXOffset, currentPos.rightInt())
+ .size(100,20)
+ .tooltip(Tooltip.of(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.@Tooltip")))
+ .build();
+ currentPos = IntIntPair.of(currentPos.leftInt(),currentPos.rightInt() + SPACER_Y);
+
+ replaceMessageLabelTextPos = currentPos;
+ lineXOffset = client.textRenderer.getWidth(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.replace")) + SPACER_X;
+ replaceMessageInput = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, currentPos.leftInt() + lineXOffset, currentPos.rightInt(), 200, 20, Text.of(""));
+ replaceMessageInput.setMaxLength(96);
+ replaceMessageInput.setTooltip(Tooltip.of(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.replace.@Tooltip")));
+ replaceMessageInput.setText(chatRule.getReplaceMessage());
+
+ ButtonWidget finishButton = ButtonWidget.builder(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.finish"), a -> close())
+ .position(this.width - 75 - SPACER_Y, this.height - SPACER_Y)
+ .size(75, 20)
+ .build();
+
+ addDrawableChild(nameInput);
+ addDrawableChild(filterInput);
+ addDrawableChild(partialMatchToggle);
+ addDrawableChild(regexToggle);
+ addDrawableChild(ignoreCaseToggle);
+ addDrawableChild(locationsInput);
+ addDrawableChild(hideMessageToggle);
+ addDrawableChild(actionBarToggle);
+ addDrawableChild(announcementToggle);
+ addDrawableChild(soundsToggle);
+ addDrawableChild(replaceMessageInput);
+ addDrawableChild(finishButton);
+ }
+
+ /**
+ * Works out the width of the maximum line
+ * @return the max used width
+ */
+ private int getMaxUsedWidth() {
+ if (client == null) return 0;
+ //text
+ int total = client.textRenderer.getWidth(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.partialMatch"));
+ total += client.textRenderer.getWidth(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.regex"));
+ total += client.textRenderer.getWidth(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.ignoreCase"));
+ //space
+ total += SPACER_X * 6;
+ //button width
+ total += 75 * 3;
+ return total;
+ }
+
+ /**
+ * Works out the height used
+ * @return height used by the gui
+ */
+ private int getMaxUsedHeight() {
+ //there are 8 rows so just times the spacer by 8
+ return SPACER_Y * 8;
+ }
+
+ private Text enabledButtonText(boolean enabled) {
+ if (enabled){
+ return Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.true").withColor(Color.green.getRGB());
+ }else {
+ return Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.false").withColor(Color.red.getRGB());
+ }
+ }
+
+ @Override
+ public void render(DrawContext context, int mouseX, int mouseY, float delta) {
+ super.render(context, mouseX, mouseY, delta);
+ context.drawCenteredTextWithShadow(this.textRenderer, this.title, this.width / 2, 16, 0xFFFFFF);
+
+ //draw labels ands text
+ int yOffset = (SPACER_Y - this.textRenderer.fontHeight) / 2;
+ context.drawTextWithShadow(this.textRenderer,Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.inputs"), inputsLabelTextPos.leftInt(), inputsLabelTextPos.rightInt() + yOffset, 0xFFFFFF);
+ context.drawTextWithShadow(this.textRenderer,Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.name"), nameLabelTextPos.leftInt(), nameLabelTextPos.rightInt() + yOffset, 0xFFFFFF);
+ context.drawTextWithShadow(this.textRenderer,Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.filter"), filterLabelTextPos.leftInt(), filterLabelTextPos.rightInt() + yOffset, 0xFFFFFF);
+ context.drawTextWithShadow(this.textRenderer,Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.partialMatch"), partialMatchTextPos.leftInt(), partialMatchTextPos.rightInt() + yOffset, 0xFFFFFF);
+ context.drawTextWithShadow(this.textRenderer,Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.regex"), regexTextPos.leftInt(), regexTextPos.rightInt() + yOffset, 0xFFFFFF);
+ context.drawTextWithShadow(this.textRenderer,Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.ignoreCase"), ignoreCaseTextPos.leftInt(), ignoreCaseTextPos.rightInt() + yOffset, 0xFFFFFF);
+ context.drawTextWithShadow(this.textRenderer,Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.locations"), locationLabelTextPos.leftInt(), locationLabelTextPos.rightInt() + yOffset, 0xFFFFFF);
+ context.drawTextWithShadow(this.textRenderer,Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.outputs"), outputsLabelTextPos.leftInt(), outputsLabelTextPos.rightInt() + yOffset, 0xFFFFFF);
+ context.drawTextWithShadow(this.textRenderer,Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.hideMessage"), hideMessageTextPos.leftInt(), hideMessageTextPos.rightInt() + yOffset, 0xFFFFFF);
+ context.drawTextWithShadow(this.textRenderer,Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.actionBar"), actionBarTextPos.leftInt(), actionBarTextPos.rightInt() + yOffset, 0xFFFFFF);
+ context.drawTextWithShadow(this.textRenderer,Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.announcement"), announcementTextPos.leftInt(), announcementTextPos.rightInt() + yOffset, 0xFFFFFF);
+ context.drawTextWithShadow(this.textRenderer,Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds"), customSoundLabelTextPos.leftInt(), customSoundLabelTextPos.rightInt() + yOffset, 0xFFFFFF);
+ context.drawTextWithShadow(this.textRenderer,Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.replace"), replaceMessageLabelTextPos.leftInt(), replaceMessageLabelTextPos.rightInt() + yOffset, 0xFFFFFF);
+ }
+
+ @Override
+ public void close() {
+ //todo add checks to see if valid rule e.g. has name
+ //and if valid save a
+ if (client != null ) {
+ save();
+ client.setScreen(parent);
+ }
+ }
+ private void save(){
+ chatRule.setName(nameInput.getText());
+ chatRule.setFilter(filterInput.getText());
+ chatRule.setReplaceMessage(replaceMessageInput.getText());
+ chatRule.setValidLocations(locationsInput.getText());
+
+ ChatRulesHandler.chatRuleList.set(chatRuleIndex,chatRule);
+ }
+
+ private MutableText getSoundName() {
+ if (currentSoundIndex == -1){
+ return Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.none");
+ }
+ return soundsLookup.keySet().stream().toList().get(currentSoundIndex);
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRulesConfigListWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRulesConfigListWidget.java
new file mode 100644
index 00000000..c5d7fcc8
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRulesConfigListWidget.java
@@ -0,0 +1,196 @@
+package de.hysky.skyblocker.skyblock.chat;
+
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.Element;
+import net.minecraft.client.gui.Selectable;
+import net.minecraft.client.gui.screen.ConfirmScreen;
+import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder;
+import net.minecraft.client.gui.screen.narration.NarrationPart;
+import net.minecraft.client.gui.tooltip.Tooltip;
+import net.minecraft.client.gui.widget.ButtonWidget;
+import net.minecraft.client.gui.widget.ElementListWidget;
+import net.minecraft.screen.ScreenTexts;
+import net.minecraft.text.Text;
+
+import java.awt.*;
+import java.util.List;
+
+public class ChatRulesConfigListWidget extends ElementListWidget<ChatRulesConfigListWidget.AbstractChatRuleEntry> {
+ private final ChatRulesConfigScreen screen;
+
+ private Boolean hasChanged;
+
+ public ChatRulesConfigListWidget(MinecraftClient minecraftClient, ChatRulesConfigScreen screen, int width, int height, int y, int itemHeight) {
+ super(minecraftClient, width, height, y, itemHeight);
+ this.screen = screen;
+ this.hasChanged = false;
+ //add labels
+ addEntry(new chatRuleLabelsEntry());
+ //add entry fall all existing rules
+ for (int i = 0; i < (long) ChatRulesHandler.chatRuleList.size(); i++){
+ addEntry(new chatRuleConfigEntry(i));
+ }
+ }
+
+ @Override
+ public int getRowWidth() {
+ return super.getRowWidth() + 100;
+ }
+
+ @Override
+ protected int getScrollbarPositionX() {
+ return super.getScrollbarPositionX() + 50;
+ }
+
+ protected void addRuleAfterSelected() {
+ hasChanged = true;
+ int newIndex = children().indexOf(getSelectedOrNull()) + 1;
+ ChatRulesHandler.chatRuleList.add(newIndex, new ChatRule());
+ children().add(newIndex + 1, new chatRuleConfigEntry(newIndex));
+ }
+
+ protected boolean removeEntry(AbstractChatRuleEntry entry) {
+ hasChanged = true;
+ return super.removeEntry(entry);
+ }
+
+ protected void saveRules() {
+ hasChanged = false;
+ ChatRulesHandler.saveChatRules();
+ }
+
+ protected boolean hasChanges(){
+ return (hasChanged || children().stream().filter(chatRuleConfigEntry.class::isInstance).map(chatRuleConfigEntry.class::cast).anyMatch(chatRuleConfigEntry::isChange));
+ }
+
+ protected static abstract class AbstractChatRuleEntry extends Entry<ChatRulesConfigListWidget.AbstractChatRuleEntry> {
+ }
+
+ private class chatRuleLabelsEntry extends AbstractChatRuleEntry {
+
+ @Override
+ public List<? extends Selectable> selectableChildren() {
+ return List.of();
+ }
+
+ @Override
+ public List<? extends Element> children() {
+ return List.of();
+ }
+
+ @Override
+ public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
+ context.drawCenteredTextWithShadow(client.textRenderer, Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleName"), width / 2 - 125, y + 5, 0xFFFFFF);
+ context.drawCenteredTextWithShadow(client.textRenderer, Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleEnabled"), width / 2, y + 5, 0xFFFFFF);
+ context.drawCenteredTextWithShadow(client.textRenderer, Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.modify"), width / 2 + 100, y + 5, 0xFFFFFF);
+ }
+ }
+
+ private class chatRuleConfigEntry extends AbstractChatRuleEntry {
+ //data
+ private final int chatRuleIndex;
+ private final ChatRule chatRule;
+
+ private final List<? extends Element> children;
+
+ //widgets
+ private final ButtonWidget enabledButton;
+ private final ButtonWidget openConfigButton;
+ private final ButtonWidget deleteButton;
+
+ //text location
+ private final int nameX = width / 2 - 125;
+ //saved data
+ private double oldScrollAmount = 0;
+
+
+ public chatRuleConfigEntry(int chatRuleIndex) {
+ this.chatRuleIndex = chatRuleIndex;
+ this.chatRule = ChatRulesHandler.chatRuleList.get(chatRuleIndex);
+
+ enabledButton = ButtonWidget.builder(enabledButtonText() , a -> toggleEnabled())
+ .size(50,20)
+ .position(width / 2 - 25,5)
+ .build();
+
+ openConfigButton = ButtonWidget.builder(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.editRule"), a -> {
+ client.setScreen(new ChatRuleConfigScreen(screen, chatRuleIndex));
+ })
+ .size(50,20)
+ .position(width / 2 + 45,5)
+ .tooltip(Tooltip.of(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.editRule.@Tooltip")))
+ .build();
+
+ deleteButton = ButtonWidget.builder(Text.translatable("selectServer.delete"), a -> {
+ oldScrollAmount = getScrollAmount();
+ client.setScreen(new ConfirmScreen(this::deleteEntry, Text.translatable("skyblocker.shortcuts.deleteQuestion"), Text.translatable("skyblocker.shortcuts.deleteWarning", chatRule.getName()), Text.translatable("selectServer.deleteButton"), ScreenTexts.CANCEL));
+ })
+ .size(50,20)
+ .position(width / 2 + 105,5)
+ .build();
+
+ children = List.of(enabledButton, openConfigButton, deleteButton);
+ }
+
+ private Text enabledButtonText() {
+ if (chatRule.getEnabled()){
+ return Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.true").withColor(Color.green.getRGB());
+ }else {
+ return Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.false").withColor(Color.red.getRGB());
+ }
+ }
+ private void toggleEnabled() {
+ hasChanged = true;
+ chatRule.setEnabled(!chatRule.getEnabled());
+ enabledButton.setMessage(enabledButtonText());
+ }
+
+ private void deleteEntry(boolean confirmedAction) {
+ if (confirmedAction){
+ //delete this
+ ChatRulesHandler.chatRuleList.remove(chatRuleIndex);
+ removeEntry(this);
+ }
+ client.setScreen(screen);
+ setScrollAmount(oldScrollAmount);
+ }
+
+ @Override
+ public List<? extends Selectable> selectableChildren() {
+ return List.of(new Selectable() {
+ @Override
+ public SelectionType getType() {
+ return SelectionType.HOVERED;
+ }
+
+ @Override
+ public void appendNarrations(NarrationMessageBuilder builder) {
+ builder.put(NarrationPart.TITLE,chatRule.getName());
+ }
+ });
+ }
+
+ @Override
+ public List<? extends Element> children() {
+ return children;
+ }
+
+ @Override
+ public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { //todo get strings form en_us.json
+ //widgets
+ enabledButton.setY(y);
+ enabledButton.render(context, mouseX, mouseY, tickDelta);
+ openConfigButton.setY(y);
+ openConfigButton.render(context, mouseX, mouseY, tickDelta);
+ deleteButton.setY(y);
+ deleteButton.render(context, mouseX, mouseY, tickDelta);
+ //text
+ context.drawCenteredTextWithShadow(client.textRenderer, chatRule.getName(), nameX, y + 5, 0xFFFFFF);
+ }
+
+ public boolean isChange() {
+ return (!chatRule.getEnabled().equals(ChatRulesHandler.chatRuleList.get(chatRuleIndex).getEnabled()));
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRulesConfigScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRulesConfigScreen.java
new file mode 100644
index 00000000..1a6815dc
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRulesConfigScreen.java
@@ -0,0 +1,75 @@
+package de.hysky.skyblocker.skyblock.chat;
+
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.screen.ConfirmScreen;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.widget.ButtonWidget;
+import net.minecraft.client.gui.widget.GridWidget;
+import net.minecraft.client.gui.widget.SimplePositioningWidget;
+import net.minecraft.screen.ScreenTexts;
+import net.minecraft.text.Text;
+
+public class ChatRulesConfigScreen extends Screen {
+
+ private ChatRulesConfigListWidget chatRulesConfigListWidget;
+ private final Screen parent;
+
+ public ChatRulesConfigScreen(Screen parent) {
+ super(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen"));
+ this.parent = parent;
+ }
+
+ @Override
+ public void setTooltip(Text tooltip) {
+ super.setTooltip(tooltip);
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+ chatRulesConfigListWidget = new ChatRulesConfigListWidget(client, this, width, height - 96, 32, 25);
+ addDrawableChild(chatRulesConfigListWidget);
+ GridWidget gridWidget = new GridWidget();
+ gridWidget.getMainPositioner().marginX(5).marginY(2);
+ GridWidget.Adder adder = gridWidget.createAdder(3);
+ adder.add(ButtonWidget.builder(ScreenTexts.CANCEL, button -> {
+ if (client != null) {
+ close();
+ }
+ }).build());
+ ButtonWidget buttonNew1 = ButtonWidget.builder(Text.translatable("text.autoconfig.skyblocker.option.messages.chatRules.screen.new"), buttonNew -> chatRulesConfigListWidget.addRuleAfterSelected()).build();
+ adder.add(buttonNew1);
+ ButtonWidget buttonDone = ButtonWidget.builder(ScreenTexts.DONE, button -> {
+ chatRulesConfigListWidget.saveRules();
+ if (client != null) {
+ close();
+ }
+ }).build();
+ adder.add(buttonDone);
+ gridWidget.refreshPositions();
+ SimplePositioningWidget.setPos(gridWidget, 0, this.height - 64, this.width, 64);
+ gridWidget.forEachChild(this::addDrawableChild);
+
+ }
+
+ @Override
+ public void render(DrawContext context, int mouseX, int mouseY, float delta) {
+ super.render(context, mouseX, mouseY, delta);
+ context.drawCenteredTextWithShadow(this.textRenderer, this.title, this.width / 2, 16, 0xFFFFFF);
+ }
+
+ @Override
+ public void close() {
+ if (client != null && chatRulesConfigListWidget.hasChanges()) {
+ client.setScreen(new ConfirmScreen(confirmedAction -> {
+ if (confirmedAction) {
+ this.client.setScreen(parent);
+ } else {
+ client.setScreen(this);
+ }
+ }, Text.translatable("text.skyblocker.quit_config"), Text.translatable("text.skyblocker.quit_config_sure"), Text.translatable("text.skyblocker.quit_discard"), ScreenTexts.CANCEL));
+ } else {
+ this.client.setScreen(parent);
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRulesHandler.java b/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRulesHandler.java
new file mode 100644
index 00000000..bde55aed
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRulesHandler.java
@@ -0,0 +1,167 @@
+package de.hysky.skyblocker.skyblock.chat;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.reflect.TypeToken;
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.utils.Http;
+import de.hysky.skyblocker.utils.Utils;
+import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Style;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.util.*;
+
+public class ChatRulesHandler {
+ private static final MinecraftClient CLIENT = MinecraftClient.getInstance();
+ private static final Logger LOGGER = LoggerFactory.getLogger(ChatRule.class);
+ private static final Path CHAT_RULE_FILE = SkyblockerMod.CONFIG_DIR.resolve("chat_rules.json");
+ /**
+ * look up table for the locations input by the users to raw locations
+ */
+ protected static final HashMap<String, String> locations = new HashMap<>();
+ /**
+ * list of possible locations still formatted for the tool tip
+ */
+ protected static final List<String> locationsList = new ArrayList<>();
+
+ protected static final List<ChatRule> chatRuleList = new ArrayList<>();
+
+ public static void init() {
+ loadChatRules();
+ loadLocations();
+ ClientReceiveMessageEvents.ALLOW_GAME.register(ChatRulesHandler::checkMessage);
+ }
+
+ private static void loadChatRules() {
+ try (BufferedReader reader = Files.newBufferedReader(CHAT_RULE_FILE)) {
+ Type chatRulesType = new TypeToken<Map<String, List<ChatRule>>>() {
+ }.getType();
+ Map<String, List<ChatRule>> chatRules = SkyblockerMod.GSON.fromJson(reader,chatRulesType);
+ chatRuleList.addAll(chatRules.get("rules"));
+
+ LOGGER.info("[Skyblocker] Loaded chat rules");
+ } catch (NoSuchFileException e) {
+ //todo create default chat rules
+ LOGGER.warn("[Skyblocker] chat rule file not found, using default rules. This is normal when using for the first time.");
+ } catch (IOException e) {
+ LOGGER.error("[Skyblocker] Failed to load shortcuts file", e);
+ }
+ }
+
+ private static void loadLocations() {
+ try {
+ String response = Http.sendGetRequest("https://api.hypixel.net/v2/resources/games");
+ JsonObject locationsJson = JsonParser.parseString(response).getAsJsonObject().get("games").getAsJsonObject().get("SKYBLOCK").getAsJsonObject().get("modeNames").getAsJsonObject();
+ for (Map.Entry<String, JsonElement> entry : locationsJson.entrySet()) {
+ //fix old naming todo remove when hypixel fix
+ if (Objects.equals(entry.getKey(), "instanced")) {
+ locationsList.add(entry.getValue().getAsString());
+ locations.put(entry.getValue().getAsString().replace(" ", "").toLowerCase(), "kuudra");
+ continue;
+ }
+ locationsList.add(entry.getValue().getAsString());
+ //add to list in a simplified for so more lenient for user input
+ locations.put(entry.getValue().getAsString().replace(" ", "").toLowerCase(),entry.getKey());
+ }
+ }
+ catch (Exception e) {
+ LOGGER.error("[Skyblocker] Failed to load locations!", e);
+ }
+ }
+
+ protected static void saveChatRules() {
+ JsonObject chatRuleJson = new JsonObject();
+ chatRuleJson.add("rules", SkyblockerMod.GSON.toJsonTree(chatRuleList));
+ try (BufferedWriter writer = Files.newBufferedWriter(CHAT_RULE_FILE)) {
+ SkyblockerMod.GSON.toJson(chatRuleJson, writer);
+ LOGGER.info("[Skyblocker] Saved chat rules file");
+ } catch (IOException e) {
+ LOGGER.error("[Skyblocker] Failed to save chat rules file", e);
+ }
+ }
+
+ /**
+ * Checks each rule in {@link ChatRulesHandler#chatRuleList} to see if they are a match for the message and if so change outputs based on the options set in the {@link ChatRule}.
+ * @param message the chat message
+ * @param overlay if its overlay
+ */
+ private static boolean checkMessage(Text message, Boolean overlay) {
+ if (!Utils.isOnSkyblock()) return true; //do not work not on skyblock
+ if (overlay) return true; //ignore messages in overlay
+ String plain = Formatting.strip(message.getString());
+ for (ChatRule rule : chatRuleList) {
+ if (rule.isMatch(plain)) {
+ //get a replacement message
+ Text newMessage;
+ if (!rule.getReplaceMessage().isBlank()) {
+ newMessage = formatText(rule.getReplaceMessage());
+ }
+ else {
+ newMessage = message;
+ }
+
+ if (rule.getShowAnnouncement()) {
+ ChatRuleAnnouncementScreen.setText(newMessage);
+ }
+
+ //show in action bar
+ if (rule.getShowActionBar() && CLIENT.player != null) {
+ CLIENT.player.sendMessage(newMessage, true);
+ }
+
+ //hide message
+ if (!rule.getHideMessage() && CLIENT.player != null) {
+ CLIENT.player.sendMessage(newMessage, false);
+ }
+
+ //play sound
+ if (rule.getCustomSound() != null && CLIENT.player != null) {
+ CLIENT.player.playSound(rule.getCustomSound(), 100f, 0.1f);
+ }
+
+ //do not send original message
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Converts a string with color codes into a formatted Text object
+ * @param codedString the string with color codes in
+ * @return formatted text
+ */
+ protected static MutableText formatText(String codedString) {
+ if (codedString.contains(String.valueOf(Formatting.FORMATTING_CODE_PREFIX)) || codedString.contains("&")){
+ MutableText newText = Text.literal("");
+ String[] parts = codedString.split("[" + Formatting.FORMATTING_CODE_PREFIX +"&]");
+ Style style = Style.EMPTY;
+ for (String part : parts) {
+ if (part.isEmpty()) continue;
+ Formatting formatting = Formatting.byCode(part.charAt(0));
+ if (formatting != null){
+ style = style.withFormatting(formatting);
+ Text.literal(part.substring(1)).getWithStyle(style).forEach(newText::append);
+ } else {
+ newText.append(Text.of(part));
+ }
+ }
+ return newText;
+ }
+ return Text.literal(codedString);
+ }
+}