diff options
| author | Rime <81419447+Emirlol@users.noreply.github.com> | 2025-02-27 23:48:55 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-02-28 04:48:55 +0800 |
| commit | fc478774143a73aa1470e6348d75896231fa21ca (patch) | |
| tree | d0f808a784f2eae1d394cf43fa23949ec4fdc61b | |
| parent | 37422acb185653f1d33c606687a97715db1a7264 (diff) | |
| download | Skyblocker-fc478774143a73aa1470e6348d75896231fa21ca.tar.gz Skyblocker-fc478774143a73aa1470e6348d75896231fa21ca.tar.bz2 Skyblocker-fc478774143a73aa1470e6348d75896231fa21ca.zip | |
Chat rule location config overhaul (#1138)
* Chat rule location config overhaul
* Fix incorrect logic in ItemTickList
* Boolean → boolean
* Update location name
* Remove locations list
* Revamp widgets in `ChatRuleConfigScreen`
* Complete codec to decode both string and enumset
* Take negated locations into account
* Fix exclusion parsing and add tests
* Clean up codec with Codec::either
* Dynamic width calculation
Also moves `Ignore Case` button to the next row, with the location config button.
* Remove stale javadoc
* Small code cleanup
* Remove `UNKNOWN` and `MODERN_FORAGING_ISLAND` from the location selector
* Future-proofing
* Consider valid locations set of only `Location.UNKNOWN` as empty
---------
Co-authored-by: Kevin <92656833+kevinthegreat1@users.noreply.github.com>
13 files changed, 1181 insertions, 804 deletions
diff --git a/src/main/java/de/hysky/skyblocker/config/screens/powdertracker/ItemTickList.java b/src/main/java/de/hysky/skyblocker/config/screens/powdertracker/ItemTickList.java index ed67b456..db3dc933 100644 --- a/src/main/java/de/hysky/skyblocker/config/screens/powdertracker/ItemTickList.java +++ b/src/main/java/de/hysky/skyblocker/config/screens/powdertracker/ItemTickList.java @@ -9,16 +9,49 @@ import net.minecraft.client.gui.widget.CheckboxWidget; import net.minecraft.client.gui.widget.ElementListWidget; import net.minecraft.text.Text; +import java.util.Collection; import java.util.List; -public class ItemTickList extends ElementListWidget<ItemTickList.ItemTickEntry> { - private final List<String> filters; - private final List<String> allItems; +/** + * A checkbox list for filter configuring purposes. + */ +public class ItemTickList<T> extends ElementListWidget<ItemTickList.ItemTickEntry> { + private final Collection<T> filters; + private final Collection<T> allItems; + private final boolean whitelist; - public ItemTickList(MinecraftClient minecraftClient, int width, int height, int y, int entryHeight, List<String> filters, List<String> allItems) { + /** + * + * @param minecraftClient Minecraft client. + * @param width The width of the list. + * @param height The height of the list. + * @param y The y value at which the list should render. + * @param entryHeight Height of a single item + * @param filters The items that will be marked. This should be a subset of allItems. + * @param allItems All possible values + */ + public ItemTickList(MinecraftClient minecraftClient, int width, int height, int y, int entryHeight, Collection<T> filters, Collection<T> allItems) { super(minecraftClient, width, height, y, entryHeight); this.filters = filters; this.allItems = allItems; + this.whitelist = false; + } + + /** + * @param minecraftClient Minecraft client. + * @param width The width of the list. + * @param height The height of the list. + * @param y The y value at which the list should render. + * @param entryHeight Height of a single item + * @param filters The items that will be marked. This should be a subset of allItems. + * @param allItems All possible values + * @param whitelist Whether the filter logic works as a whitelist or blacklist, to change whether the boxes for items in the filters collection should be checked. As an example: PowderFilter keeps which items to remove inside the filter (blacklist), while ChatRuleLocation keeps which locations the feature should work in (whitelist). + */ + public ItemTickList(MinecraftClient minecraftClient, int width, int height, int y, int entryHeight, Collection<T> filters, Collection<T> allItems, boolean whitelist) { + super(minecraftClient, width, height, y, entryHeight); + this.filters = filters; + this.allItems = allItems; + this.whitelist = whitelist; } public void clearAndInit() { @@ -26,14 +59,19 @@ public class ItemTickList extends ElementListWidget<ItemTickList.ItemTickEntry> init(); } - public ItemTickList init() { - for (String item : allItems) { + public ItemTickList<T> init() { + for (T item : allItems) { ItemTickEntry entry = new ItemTickEntry( - CheckboxWidget.builder(Text.of(item), client.textRenderer) - .checked(!filters.contains(item)) + CheckboxWidget.builder(Text.of(item.toString()), client.textRenderer) + .checked(whitelist == filters.contains(item)) .callback((checkbox1, checked) -> { - if (checked) filters.remove(item); - else filters.add(item); + if (whitelist) { + if (checked) filters.add(item); + else filters.remove(item); + } else { + if (checked) filters.remove(item); + else filters.add(item); + } }) .build() ); diff --git a/src/main/java/de/hysky/skyblocker/config/screens/powdertracker/PowderFilterConfigScreen.java b/src/main/java/de/hysky/skyblocker/config/screens/powdertracker/PowderFilterConfigScreen.java index 84337d7b..fbd2668a 100644 --- a/src/main/java/de/hysky/skyblocker/config/screens/powdertracker/PowderFilterConfigScreen.java +++ b/src/main/java/de/hysky/skyblocker/config/screens/powdertracker/PowderFilterConfigScreen.java @@ -34,7 +34,7 @@ public class PowderFilterConfigScreen extends Screen { assert client != null; context.drawCenteredTextWithShadow(client.textRenderer, Text.translatable("skyblocker.config.mining.crystalHollows.powderTrackerFilter.screenTitle").formatted(Formatting.BOLD), width / 2, (32 - client.textRenderer.fontHeight) / 2, 0xFFFFFF); }); - ItemTickList itemTickList = addDrawableChild(new ItemTickList(MinecraftClient.getInstance(), width, height - 96, 32, 24, filters, allItems).init()); + ItemTickList<String> itemTickList = addDrawableChild(new ItemTickList<>(MinecraftClient.getInstance(), width, height - 96, 32, 24, filters, allItems).init()); //Grid code gratuitously stolen from WaypointsScreen. Same goes for the y and heights above. GridWidget gridWidget = new GridWidget(); gridWidget.getMainPositioner().marginX(5).marginY(2); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRule.java b/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRule.java index 7fd6844d..d60b18e5 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRule.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRule.java @@ -1,263 +1,277 @@ package de.hysky.skyblocker.skyblock.chat; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import de.hysky.skyblocker.utils.CollectionUtils; +import de.hysky.skyblocker.utils.Location; import de.hysky.skyblocker.utils.Utils; import net.minecraft.sound.SoundEvent; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.VisibleForTesting; +import java.util.Arrays; +import java.util.EnumSet; import java.util.List; -import java.util.Objects; import java.util.Optional; +import java.util.function.Function; import java.util.regex.Pattern; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; - /** * Data class to contain all the settings for a chat rule */ public class ChatRule { - private static final Codec<ChatRule> CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codec.STRING.fieldOf("name").forGetter(ChatRule::getName), - Codec.BOOL.fieldOf("enabled").forGetter(ChatRule::getEnabled), - Codec.BOOL.fieldOf("isPartialMatch").forGetter(ChatRule::getPartialMatch), - Codec.BOOL.fieldOf("isRegex").forGetter(ChatRule::getRegex), - Codec.BOOL.fieldOf("isIgnoreCase").forGetter(ChatRule::getIgnoreCase), - Codec.STRING.fieldOf("filter").forGetter(ChatRule::getFilter), - Codec.STRING.fieldOf("validLocations").forGetter(ChatRule::getValidLocations), - Codec.BOOL.fieldOf("hideMessage").forGetter(ChatRule::getHideMessage), - Codec.BOOL.fieldOf("showActionBar").forGetter(ChatRule::getShowActionBar), - Codec.BOOL.fieldOf("showAnnouncement").forGetter(ChatRule::getShowAnnouncement), - Codec.STRING.optionalFieldOf("replaceMessage").forGetter(ChatRule::getReplaceMessageOpt), - SoundEvent.CODEC.optionalFieldOf("customSound").forGetter(ChatRule::getCustomSoundOpt)) - .apply(instance, ChatRule::new)); - public static final Codec<List<ChatRule>> LIST_CODEC = CODEC.listOf(); - - 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. - */ - protected 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 ChatRule(String name, Boolean enabled, Boolean isPartialMatch, Boolean isRegex, Boolean isIgnoreCase, String filter, String validLocations, Boolean hideMessage, Boolean showActionBar, Boolean showAnnouncement, String replaceMessage, SoundEvent customSound) { - this.name = name; - this.enabled = enabled; - this.isPartialMatch = isPartialMatch; - this.isRegex = isRegex; - this.isIgnoreCase = isIgnoreCase; - this.filter = filter; - this.validLocations = validLocations; - this.hideMessage = hideMessage; - this.showActionBar = showActionBar; - this.showAnnouncement = showAnnouncement; - this.replaceMessage = replaceMessage; - this.customSound = customSound; - } - - private ChatRule(String name, Boolean enabled, Boolean isPartialMatch, Boolean isRegex, Boolean isIgnoreCase, String filter, String validLocations, Boolean hideMessage, Boolean showActionBar, Boolean showAnnouncement, Optional<String> replaceMessage, Optional<SoundEvent> customSound) { - this(name, enabled, isPartialMatch, isRegex, isIgnoreCase, filter, validLocations, hideMessage, showActionBar, showAnnouncement, replaceMessage.orElse(null), customSound.orElse(null)); - } - - protected String getName() { - return name; - } - - protected void setName(String name) { - this.name = name; - } - - protected Boolean getEnabled() { - return enabled; - } - - protected void setEnabled(Boolean enabled) { - this.enabled = enabled; - } - - protected Boolean getPartialMatch() { - return isPartialMatch; - } - - protected void setPartialMatch(Boolean partialMatch) { - isPartialMatch = partialMatch; - } - - protected Boolean getRegex() { - return isRegex; - } - - protected void setRegex(Boolean regex) { - isRegex = regex; - } - - protected Boolean getIgnoreCase() { - return isIgnoreCase; - } - - protected void setIgnoreCase(Boolean ignoreCase) { - isIgnoreCase = ignoreCase; - } - - protected String getFilter() { - return filter; - } - - protected void setFilter(String filter) { - this.filter = filter; - } - - protected Boolean getHideMessage() { - return hideMessage; - } - - protected void setHideMessage(Boolean hideMessage) { - this.hideMessage = hideMessage; - } - - protected Boolean getShowActionBar() { - return showActionBar; - } - - protected void setShowActionBar(Boolean showActionBar) { - this.showActionBar = showActionBar; - } - - protected Boolean getShowAnnouncement() { - return showAnnouncement; - } - - protected void setShowAnnouncement(Boolean showAnnouncement) { - this.showAnnouncement = showAnnouncement; - } - - protected String getReplaceMessage() { - return replaceMessage; - } - - private Optional<String> getReplaceMessageOpt() { - return replaceMessage == null ? Optional.empty() : Optional.of(replaceMessage); - } - - protected void setReplaceMessage(String replaceMessage) { - this.replaceMessage = replaceMessage; - } - - protected SoundEvent getCustomSound() { - return customSound; - } - - private Optional<SoundEvent> getCustomSoundOpt() { - return customSound == null ? Optional.empty() : Optional.of(customSound); - } - - protected void setCustomSound(SoundEvent customSound) { - this.customSound = customSound; - } - - protected String getValidLocations() { - return validLocations; - } - - protected 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 - */ - protected 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 cleanedMapLocation = Utils.getMap().toLowerCase().replace(" ", ""); - Boolean isLocationValid = null; - for (String validLocation : validLocations.replace(" ", "").toLowerCase().split(",")) {//the locations are split by "," and start with ! if not locations - if (validLocation == null) continue; - if (validLocation.startsWith("!")) {//not location - if (Objects.equals(validLocation.substring(1), cleanedMapLocation)) { - isLocationValid = false; - break; - } else { - isLocationValid = true; - } - } else { - if (Objects.equals(validLocation, cleanedMapLocation)) { //normal location - isLocationValid = true; - break; - } - } - } - - //if location is not in the list at all and is a not a "!" location or and is a normal location - if (isLocationValid != null && isLocationValid) { - return true; - } - - return false; - } + /** + * Codec that can decode both {@link String} and {@link EnumSet} of locations, while encoding only {@link EnumSet} of locations. + * <br> + * This is necessary due to a change in how the locations are stored in the config. + */ + @VisibleForTesting + static final Codec<EnumSet<Location>> LOCATION_FIXING_CODEC = Codec.either(Location.SET_CODEC, Codec.STRING).xmap( + either -> either.map(Function.identity(), ChatRule::encodeString), + Either::left + ); + + private static final Codec<ChatRule> CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.STRING.fieldOf("name").forGetter(ChatRule::getName), + Codec.BOOL.fieldOf("enabled").forGetter(ChatRule::getEnabled), + Codec.BOOL.fieldOf("isPartialMatch").forGetter(ChatRule::getPartialMatch), + Codec.BOOL.fieldOf("isRegex").forGetter(ChatRule::getRegex), + Codec.BOOL.fieldOf("isIgnoreCase").forGetter(ChatRule::getIgnoreCase), + Codec.STRING.fieldOf("filter").forGetter(ChatRule::getFilter), + LOCATION_FIXING_CODEC.fieldOf("validLocations").forGetter(ChatRule::getValidLocations), + Codec.BOOL.fieldOf("hideMessage").forGetter(ChatRule::getHideMessage), + Codec.BOOL.fieldOf("showActionBar").forGetter(ChatRule::getShowActionBar), + Codec.BOOL.fieldOf("showAnnouncement").forGetter(ChatRule::getShowAnnouncement), + Codec.STRING.optionalFieldOf("replaceMessage").forGetter(ChatRule::getReplaceMessageOpt), + SoundEvent.CODEC.optionalFieldOf("customSound").forGetter(ChatRule::getCustomSoundOpt) + ).apply(instance, ChatRule::new)); + public static final Codec<List<ChatRule>> LIST_CODEC = CODEC.listOf(); + + private String name; + + // Inputs + private boolean enabled; + private boolean isPartialMatch; + private boolean isRegex; + private boolean isIgnoreCase; + private String filter; + private EnumSet<Location> validLocations; + + // Outputs + private boolean hideMessage; + private boolean showActionBar; + private boolean showAnnouncement; + private String replaceMessage; + private SoundEvent customSound; + + /** + * Creates a chat rule with default options. + */ + protected ChatRule() { + this.name = "New Rule"; + + this.enabled = true; + this.isPartialMatch = false; + this.isRegex = false; + this.isIgnoreCase = true; + this.filter = ""; + this.validLocations = EnumSet.noneOf(Location.class); + + this.hideMessage = true; + this.showActionBar = false; + this.showAnnouncement = false; + this.replaceMessage = null; + this.customSound = null; + } + + public ChatRule(String name, boolean enabled, boolean isPartialMatch, boolean isRegex, boolean isIgnoreCase, String filter, EnumSet<Location> validLocations, boolean hideMessage, boolean showActionBar, boolean showAnnouncement, @Nullable String replaceMessage, @Nullable SoundEvent customSound) { + this.name = name; + this.enabled = enabled; + this.isPartialMatch = isPartialMatch; + this.isRegex = isRegex; + this.isIgnoreCase = isIgnoreCase; + this.filter = filter; + this.validLocations = validLocations; + this.hideMessage = hideMessage; + this.showActionBar = showActionBar; + this.showAnnouncement = showAnnouncement; + this.replaceMessage = replaceMessage; + this.customSound = customSound; + } + + private ChatRule(String name, boolean enabled, boolean isPartialMatch, boolean isRegex, boolean isIgnoreCase, String filter, EnumSet<Location> validLocations, boolean hideMessage, boolean showActionBar, boolean showAnnouncement, Optional<String> replaceMessage, Optional<SoundEvent> customSound) { + this(name, enabled, isPartialMatch, isRegex, isIgnoreCase, filter, validLocations, hideMessage, showActionBar, showAnnouncement, replaceMessage.orElse(null), customSound.orElse(null)); + } + + protected String getName() { + return name; + } + + protected void setName(String name) { + this.name = name; + } + + protected boolean getEnabled() { + return enabled; + } + + protected void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + protected boolean getPartialMatch() { + return isPartialMatch; + } + + protected void setPartialMatch(boolean partialMatch) { + isPartialMatch = partialMatch; + } + + protected boolean getRegex() { + return isRegex; + } + + protected void setRegex(boolean regex) { + isRegex = regex; + } + + protected boolean getIgnoreCase() { + return isIgnoreCase; + } + + protected void setIgnoreCase(boolean ignoreCase) { + isIgnoreCase = ignoreCase; + } + + protected String getFilter() { + return filter; + } + + protected void setFilter(String filter) { + this.filter = filter; + } + + protected boolean getHideMessage() { + return hideMessage; + } + + protected void setHideMessage(boolean hideMessage) { + this.hideMessage = hideMessage; + } + + protected boolean getShowActionBar() { + return showActionBar; + } + + protected void setShowActionBar(boolean showActionBar) { + this.showActionBar = showActionBar; + } + + protected boolean getShowAnnouncement() { + return showAnnouncement; + } + + protected void setShowAnnouncement(boolean showAnnouncement) { + this.showAnnouncement = showAnnouncement; + } + + protected String getReplaceMessage() { + return replaceMessage; + } + + private Optional<String> getReplaceMessageOpt() { + return Optional.ofNullable(replaceMessage); + } + + protected void setReplaceMessage(String replaceMessage) { + this.replaceMessage = replaceMessage; + } + + protected SoundEvent getCustomSound() { + return customSound; + } + + private Optional<SoundEvent> getCustomSoundOpt() { + return Optional.ofNullable(customSound); + } + + protected void setCustomSound(SoundEvent customSound) { + this.customSound = customSound; + } + + protected EnumSet<Location> getValidLocations() { + return validLocations; + } + + protected void setValidLocations(EnumSet<Location> 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 + */ + protected 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; + } + } + + // As a special case, if there are no valid locations all locations are valid. + // This exists because it doesn't make sense to remove all valid locations, you should disable the chat rule if you want to do that. + // This way, we can also default to an empty set for validLocations. + if (validLocations.isEmpty()) return true; + // UNKNOWN isn't a valid location, so we act the same as the list being empty. + if (validLocations.size() == 1 && validLocations.contains(Location.UNKNOWN)) return true; + return validLocations.contains(Utils.getLocation()); + } + + // This maps invalid entries to `Location.UNKNOWN`, which is better than failing outright. + private static EnumSet<Location> encodeString(String string) { + // Necessary for empty strings, which would've been decoded as UNKNOWN otherwise. + if (string.isEmpty()) return EnumSet.noneOf(Location.class); + + // If a location's name contains a ! prefix, it's negated, meaning every location except that one is valid. + if (string.contains("!")) return EnumSet.complementOf( + Arrays.stream(string.split(", ?")) + .filter(s1 -> s1.startsWith("!")) // Filter out the non-negated locations because the negation of any element in the list already implies those non-negated locations being valid. + .map(s -> s.substring(1)) // Skip the `!` + .map(Location::fromFriendlyName) + .collect(CollectionUtils.enumSetCollector(Location.class)) + ); + return Arrays.stream(string.split(", ?")) + .map(Location::fromFriendlyName) + .collect(CollectionUtils.enumSetCollector(Location.class)); + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRuleConfigScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRuleConfigScreen.java index 11d5b72b..54236d07 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRuleConfigScreen.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRuleConfigScreen.java @@ -1,18 +1,23 @@ package de.hysky.skyblocker.skyblock.chat; +import de.hysky.skyblocker.utils.WidgetUtils; +import it.unimi.dsi.fastutil.ints.IntIntImmutablePair; +import it.unimi.dsi.fastutil.ints.IntIntMutablePair; import it.unimi.dsi.fastutil.ints.IntIntPair; -import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.MinecraftClient; 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.client.gui.widget.TextWidget; +import net.minecraft.client.gui.widget.Widget; import net.minecraft.sound.SoundEvent; import net.minecraft.sound.SoundEvents; import net.minecraft.text.MutableText; import net.minecraft.text.Text; +import net.minecraft.util.Formatting; import net.minecraft.util.Identifier; - import java.awt.*; import java.util.List; import java.util.Map; @@ -20,327 +25,529 @@ import java.util.Map; 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("skyblocker.config.chat.chatRules.screen.ruleScreen.sounds.pling"), SoundEvents.BLOCK_NOTE_BLOCK_PLING.value()), - entry(Text.translatable("skyblocker.config.chat.chatRules.screen.ruleScreen.sounds.cave"), SoundEvents.AMBIENT_CAVE.value()), - entry(Text.translatable("skyblocker.config.chat.chatRules.screen.ruleScreen.sounds.zombie"), SoundEvents.ENTITY_ZOMBIE_AMBIENT), - entry(Text.translatable("skyblocker.config.chat.chatRules.screen.ruleScreen.sounds.crit"), SoundEvents.ENTITY_PLAYER_ATTACK_CRIT), - entry(Text.translatable("skyblocker.config.chat.chatRules.screen.ruleScreen.sounds.arrowHit"), SoundEvents.ENTITY_ARROW_HIT_PLAYER), - entry(Text.translatable("skyblocker.config.chat.chatRules.screen.ruleScreen.sounds.amethyst"), SoundEvents.BLOCK_AMETHYST_BLOCK_HIT), - entry(Text.translatable("skyblocker.config.chat.chatRules.screen.ruleScreen.sounds.anvil"), SoundEvents.BLOCK_ANVIL_LAND) - ); - - private int buttonWidth = 75; - - 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("skyblocker.config.chat.chatRules.screen.ruleScreen")); - this.chatRuleIndex = chatRuleIndex; - this.chatRule = ChatRulesHandler.chatRuleList.get(chatRuleIndex); - this.parent = parent; - this.currentSoundIndex = getCurrentSoundIndex(); - } - - private int getCurrentSoundIndex() { - if (chatRule.getCustomSound() == null) return -1; //if no sound just return -1 - - List<SoundEvent> soundOptions = soundsLookup.values().stream().toList(); - Identifier ruleSoundId = chatRule.getCustomSound().id(); - - for (int i = 0; i < soundOptions.size(); i++) { - if (soundOptions.get(i).id().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 - calculateMaxButtonWidth(); - IntIntPair currentPos = IntIntPair.of((this.width - getMaxUsedWidth()) / 2,(int)((this.height - getMaxUsedHeight()) * 0.33)); - int lineXOffset; - - nameLabelTextPos = currentPos; - lineXOffset = client.textRenderer.getWidth(Text.translatable("skyblocker.config.chat.chatRules.screen.ruleScreen.name")) + SPACER_X; - nameInput = new TextFieldWidget(client.textRenderer, currentPos.leftInt() + lineXOffset, currentPos.rightInt(), 100, 20, Text.of("")); - nameInput.setText(chatRule.getName()); - nameInput.setTooltip(Tooltip.of(Text.translatable("skyblocker.config.chat.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("skyblocker.config.chat.chatRules.screen.ruleScreen.filter")) + SPACER_X; - filterInput = new TextFieldWidget(client.textRenderer, currentPos.leftInt() + lineXOffset, currentPos.rightInt(), 200, 20, Text.of("")); - filterInput.setMaxLength(96); - filterInput.setText(chatRule.getFilter()); - filterInput.setTooltip(Tooltip.of(Text.translatable("skyblocker.config.chat.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("skyblocker.config.chat.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(buttonWidth,20) - .tooltip(Tooltip.of(Text.translatable("skyblocker.config.chat.chatRules.screen.ruleScreen.partialMatch.@Tooltip"))) - .build(); - lineXOffset += buttonWidth + SPACER_X; - regexTextPos = IntIntPair.of(currentPos.leftInt() + lineXOffset,currentPos.rightInt()); - lineXOffset += client.textRenderer.getWidth(Text.translatable("skyblocker.config.chat.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(buttonWidth,20) - .tooltip(Tooltip.of(Text.translatable("skyblocker.config.chat.chatRules.screen.ruleScreen.regex.@Tooltip"))) - .build(); - lineXOffset += buttonWidth + SPACER_X; - ignoreCaseTextPos = IntIntPair.of(currentPos.leftInt() + lineXOffset,currentPos.rightInt()); - lineXOffset += client.textRenderer.getWidth(Text.translatable("skyblocker.config.chat.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(buttonWidth,20) - .tooltip(Tooltip.of(Text.translatable("skyblocker.config.chat.chatRules.screen.ruleScreen.ignoreCase.@Tooltip"))) - .build(); |
