diff --git a/README.md b/README.md index fe071ca3..2a2b7c31 100644 --- a/README.md +++ b/README.md @@ -63,10 +63,15 @@ und [The Rettington Companion](https://modrinth.com/mod/the-rettington-companion der so viel Geld einzahlt, bis der Bankautomat voll ist - Mit dem `/aktien` Befehl wird die Aktien-App auf dem Händy direkt geöffnet, sodass man nicht mehr manuell durch das Handy navigieren muss -- Kisten werden automatisch aus Mülleimern eingesammelt +- Gegenstände werden automatisch aus Mülleimern eingesammelt - Es werden bestimmte Nachrichten im Chat ausgeblendet (konfigurierbar über [The Rettington Companion](https://modrinth.com/mod/the-rettington-companion)) - Sollte ein Spieler abwesend sein, wird bei `/id` angezeigt wie lang dieser schon abwesend ist +- Die Nachricht, dass man keine Bandage vergeben kann, wird nur noch in der ActionBar angezeigt, da der Chat durch diese Nachrichten + zugespammt wird +- Beim Öffnen von Schatzkisten können die Delfine deaktiviert werden +- Der "Malle, I love you" Sound kann nun optional deaktiviert werden +- `/reichensteuer` Befehl für Spieler ohne Premium Rang ### Auto @@ -92,6 +97,9 @@ und [The Rettington Companion](https://modrinth.com/mod/the-rettington-companion - Für den Bluthändler, Dealer und Schwarzmarkt wird (in der Übersicht) angezeigt, an welchem Ort er gefunden wurde - Es gibt einen Hotkey, um (für die entsprechende Fraktion relevante) Reinforcements automatisch anzunehmen (diese Reinforcements sind mit ✨ markiert) +- Leichen werden leuchtend hervorgehoben, wenn man sich in der Nähe befindet (aktuell deaktiviert, da es gegen Server-Richtlinien + verstößt) +- Es gibt eine Option, die für Polizei, FBI und Rettungsdienst die Sirene aktiviert, sobald man den Dienst betritt ### Jobs @@ -113,6 +121,14 @@ und [The Rettington Companion](https://modrinth.com/mod/the-rettington-companion - Informationen über den PayDay (Dauer bis zum nächsten PayDay, Gehalt und Erfahrung) einschließlich Reichensteuer-Warnung - Offene Notrufe +### Fehlerbehebungen auf Serverseite + +> ℹ️ Folgende Funktionen sind Fehlerbehebungen für UnicaCity, die das Serverteam selbst nicht beheben kann: + +- Maskierte Spieler haben einen verschleierten Namen (`§k`). Die Verschleierung nutzt auch Buchstaben, die im Resourcepack des Servers + zu Texturen überschrieben wurden. Dadurch wird in den Namen für maskierte Spieler ab und zu eine Textur anstatt Buchstaben angezeigt. + Dieses Problem wird durch die Mod behoben. + ## Befehle ### Allgemein diff --git a/build.gradle.kts b/build.gradle.kts index a8771e20..fb7c02fe 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -48,7 +48,7 @@ dependencies { implementation("org.atteo.classindex:classindex:3.13") annotationProcessor("org.atteo.classindex:classindex:3.13") - compileOnly("maven.modrinth:the-rettington-companion:2.2.0") + compileOnly("maven.modrinth:the-rettington-companion:2.4.0") } tasks.processResources { diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index b1b8ef56..eddabd2e 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a9db1155..ad7845be 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip networkTimeout=10000 retries=0 retryBackOffMs=500 diff --git a/src/main/java/de/rettichlp/ucutils/command/ModCommand.java b/src/main/java/de/rettichlp/ucutils/command/ModCommand.java index 6a566839..f7d8bd73 100644 --- a/src/main/java/de/rettichlp/ucutils/command/ModCommand.java +++ b/src/main/java/de/rettichlp/ucutils/command/ModCommand.java @@ -55,7 +55,7 @@ public LiteralArgumentBuilder execute(@NotNull Litera messageService.sendModMessage("UCUtils Version " + version, false); messageService.sendModMessage(empty() - .append(Component.literal("Autors").withStyle(GRAY)) + .append(Component.literal("Authors").withStyle(GRAY)) .append(Component.literal(":").withStyle(DARK_GRAY)).append(" ") .append(Component.literal(authors).withStyle(WHITE)), false); diff --git a/src/main/java/de/rettichlp/ucutils/command/RichTaxesCommand.java b/src/main/java/de/rettichlp/ucutils/command/RichTaxesCommand.java new file mode 100644 index 00000000..b41dedc2 --- /dev/null +++ b/src/main/java/de/rettichlp/ucutils/command/RichTaxesCommand.java @@ -0,0 +1,63 @@ +package de.rettichlp.ucutils.command; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import de.rettichlp.ucutils.common.registry.CommandBase; +import de.rettichlp.ucutils.common.registry.UCUtilsCommand; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import org.jetbrains.annotations.NotNull; + +import static de.rettichlp.ucutils.UCUtils.commandService; +import static de.rettichlp.ucutils.UCUtils.configuration; +import static de.rettichlp.ucutils.UCUtils.messageService; +import static de.rettichlp.ucutils.UCUtils.storage; +import static de.rettichlp.ucutils.UCUtils.utilService; +import static de.rettichlp.ucutils.common.services.CommandService.COMMAND_COOLDOWN_MILLIS; + +@UCUtilsCommand(label = "reichensteuer") +public class RichTaxesCommand extends CommandBase { + + private static final int RICH_TAXES_THRESHOLD = 100000; + + @Override + public LiteralArgumentBuilder execute(@NotNull LiteralArgumentBuilder node) { + return node + .executes(context -> { + if (storage.isPremium()) { + commandService.sendCommand("reichensteuern"); + return 1; + } + + // execute command to check money on the bank of player + commandService.sendCommand("atminfo"); + + // handle money withdraw + utilService.delayedAction(() -> { + int moneyAtmAmount = storage.getMoneyAtmAmount(); + int moneyBankAmount = configuration.getMoneyBankAmount(); + + // check atm has money + if (moneyAtmAmount <= 0) { + messageService.sendModMessage("Der ATM hat kein Geld.", false); + return; + } + + // check player has rich taxes + if (moneyBankAmount <= RICH_TAXES_THRESHOLD) { + messageService.sendModMessage("Du hast nicht ausreichend Geld auf der Bank.", false); + return; + } + + int moneyThatNeedsToBeWithdrawn = moneyBankAmount - RICH_TAXES_THRESHOLD; + + if (moneyAtmAmount >= moneyThatNeedsToBeWithdrawn) { + commandService.sendCommand("bank abbuchen " + moneyThatNeedsToBeWithdrawn); + } else { + commandService.sendCommand("bank abbuchen " + moneyAtmAmount); + messageService.sendModMessage("Du musst noch " + (moneyThatNeedsToBeWithdrawn - moneyAtmAmount) + "$ abbuchen.", false); + } + }, COMMAND_COOLDOWN_MILLIS); + + return 1; + }); + } +} diff --git a/src/main/java/de/rettichlp/ucutils/common/Storage.java b/src/main/java/de/rettichlp/ucutils/common/Storage.java index ca8db8a0..3f04c4bd 100644 --- a/src/main/java/de/rettichlp/ucutils/common/Storage.java +++ b/src/main/java/de/rettichlp/ucutils/common/Storage.java @@ -107,6 +107,10 @@ public class Storage { @Setter private Minecart minecartEntityToHighlight; + @Getter + @Setter + private int moneyAtmAmount = 0; + @Getter @Setter private boolean premium = false; @@ -171,6 +175,8 @@ public void print() { LOGGER.info("joinTimestamp: {}", this.joinTimestamp); // minecartEntityToHighlight LOGGER.info("minecartEntityToHighlight: {}", this.minecartEntityToHighlight); + // moneyAtmAmount + LOGGER.info("moneyAtmAmount: {}", this.moneyAtmAmount); // premium LOGGER.info("premium: {}", this.premium); // stockMarketCommandRunning @@ -186,6 +192,11 @@ public void print() { } public Faction getFaction(String playerName) { + Faction cachedFaction = this.playerFactionCache.get(playerName); + if (cachedFaction != null) { + return cachedFaction; + } + Faction faction = this.factionEntries.stream() .filter(factionEntry -> factionEntry.members().stream() .anyMatch(factionMember -> factionMember.username().equalsIgnoreCase(playerName))) diff --git a/src/main/java/de/rettichlp/ucutils/common/configuration/Configuration.java b/src/main/java/de/rettichlp/ucutils/common/configuration/Configuration.java index 8b9a3311..0000c42c 100644 --- a/src/main/java/de/rettichlp/ucutils/common/configuration/Configuration.java +++ b/src/main/java/de/rettichlp/ucutils/common/configuration/Configuration.java @@ -49,8 +49,7 @@ public Configuration loadFromFile() { } // load existing config - try { - Reader reader = newBufferedReader(CONFIG_PATH); + try (Reader reader = newBufferedReader(CONFIG_PATH)) { return api.getGson().fromJson(reader, Configuration.class); } catch (Exception e) { LOGGER.error("Failed to load config from {}", CONFIG_PATH, e); diff --git a/src/main/java/de/rettichlp/ucutils/common/configuration/options/MiscellaneousOptions.java b/src/main/java/de/rettichlp/ucutils/common/configuration/options/MiscellaneousOptions.java index 18cb34db..f443d65e 100644 --- a/src/main/java/de/rettichlp/ucutils/common/configuration/options/MiscellaneousOptions.java +++ b/src/main/java/de/rettichlp/ucutils/common/configuration/options/MiscellaneousOptions.java @@ -21,9 +21,10 @@ public class MiscellaneousOptions { private boolean showHydration = true; private AtmInformationType atmInformationType = NONE; - private boolean autoCollectChestsFromTrashCans = false; private boolean highlightCorpses = false; // feature rejected by UnicaCity team private boolean hideDolphins = false; + private boolean blockMalleSound = false; + private boolean autoSiren = true; @Getter @AllArgsConstructor diff --git a/src/main/java/de/rettichlp/ucutils/common/configuration/options/Options.java b/src/main/java/de/rettichlp/ucutils/common/configuration/options/Options.java index 9202cc2c..77c4ea66 100644 --- a/src/main/java/de/rettichlp/ucutils/common/configuration/options/Options.java +++ b/src/main/java/de/rettichlp/ucutils/common/configuration/options/Options.java @@ -10,6 +10,9 @@ import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import java.util.HashSet; +import java.util.Set; + import static de.rettichlp.ucutils.common.configuration.options.Options.ReinforcementType.UNICACITYADDON; import static net.minecraft.ChatFormatting.AQUA; import static net.minecraft.ChatFormatting.BLUE; @@ -18,6 +21,9 @@ import static net.minecraft.client.gui.components.Tooltip.create; import static net.minecraft.network.chat.Component.empty; import static net.minecraft.network.chat.Component.literal; +import static net.minecraft.world.item.Items.CHEST; +import static net.minecraft.world.item.Items.ENDER_CHEST; +import static net.minecraft.world.item.Items.TRAPPED_CHEST; @Getter @Setter @@ -33,6 +39,7 @@ public class Options { private boolean checkUnicacityServer = true; private ReinforcementType reinforcementType = UNICACITYADDON; + private Set autoCollectFromTrashCan = new HashSet<>(Set.of(CHEST.toString(), TRAPPED_CHEST.toString(), ENDER_CHEST.toString())); @Getter @AllArgsConstructor diff --git a/src/main/java/de/rettichlp/ucutils/common/gui/screens/options/MiscellaneousOptionsScreen.java b/src/main/java/de/rettichlp/ucutils/common/gui/screens/options/MiscellaneousOptionsScreen.java index 40abdd6e..94832015 100644 --- a/src/main/java/de/rettichlp/ucutils/common/gui/screens/options/MiscellaneousOptionsScreen.java +++ b/src/main/java/de/rettichlp/ucutils/common/gui/screens/options/MiscellaneousOptionsScreen.java @@ -2,15 +2,17 @@ import de.rettichlp.ucutils.common.configuration.options.MiscellaneousOptions; import de.rettichlp.ucutils.common.gui.screens.OptionsScreen; +import de.rettichlp.ucutils.common.gui.screens.components.CyclingButtonEntry; import de.rettichlp.ucutils.common.gui.screens.components.ToggleButtonWidget; -import net.minecraft.client.gui.layouts.LinearLayout; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.CycleButton; +import net.minecraft.client.gui.layouts.GridLayout; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import static de.rettichlp.ucutils.UCUtils.commandService; -import static de.rettichlp.ucutils.UCUtils.renderService; -import static net.minecraft.client.gui.layouts.LinearLayout.horizontal; -import static net.minecraft.client.gui.layouts.LinearLayout.vertical; +import static de.rettichlp.ucutils.UCUtils.configuration; +import static net.minecraft.client.gui.components.Tooltip.create; import static net.minecraft.network.chat.CommonComponents.NEW_LINE; import static net.minecraft.network.chat.Component.translatable; import static net.minecraft.util.CommonColors.RED; @@ -27,6 +29,10 @@ public class MiscellaneousOptionsScreen extends OptionsScreen { private static final Component HIGHLIGHT_CORPSES_TOOLTIP = translatable("ucutils.options.highlight_corpses.tooltip").append(NEW_LINE).append(translatable("ucutils.feature_disabled").withColor(RED)); private static final Component HIDE_DOLPHINS_NAME = translatable("ucutils.options.hide_dolphins.name"); private static final Component HIDE_DOLPHINS_TOOLTIP = translatable("ucutils.options.hide_dolphins.tooltip"); + private static final Component BLOCK_MALLE_SOUND_NAME = translatable("ucutils.options.block_malle_sound.name"); + private static final Component BLOCK_MALLE_SOUND_TOOLTIP = translatable("ucutils.options.block_malle_sound.tooltip"); + private static final Component AUTO_SIREN_NAME = translatable("ucutils.options.auto_siren.name"); + private static final Component AUTO_SIREN_TOOLTIP = translatable("ucutils.options.auto_siren.tooltip"); public MiscellaneousOptionsScreen(Screen parent) { super(parent, TEXT_MISCELLANEOUS); @@ -34,20 +40,52 @@ public MiscellaneousOptionsScreen(Screen parent) { @Override public void initBody() { - LinearLayout directionalLayoutWidget = this.layout.addToContents(vertical().spacing(4)); + GridLayout gridLayout = this.layout.addToContents(new GridLayout()); + gridLayout.columnSpacing(8).rowSpacing(4); + GridLayout.RowHelper gridLayoutRowHelper = gridLayout.createRowHelper(2); - LinearLayout directionalLayoutWidget1 = directionalLayoutWidget.addChild(horizontal().spacing(8)); - renderService.addToggleButton(directionalLayoutWidget1, HYDRATION_NAME, HYDRATION_TOOLTIP, (options, value) -> options.miscellaneous().showHydration(value), options -> options.miscellaneous().showHydration(), 150); - renderService.addCyclingButton(directionalLayoutWidget1, BANK_INFORMATION_NAME, MiscellaneousOptions.AtmInformationType.values(), MiscellaneousOptions.AtmInformationType::getDisplayName, (options, value) -> options.miscellaneous().atmInformationType(value), options -> options.miscellaneous().atmInformationType(), 150); + MiscellaneousOptions miscellaneousOptions = configuration.getOptions().miscellaneous(); - LinearLayout directionalLayoutWidget2 = directionalLayoutWidget.addChild(horizontal().spacing(8)); - renderService.addToggleButton(directionalLayoutWidget2, AUTO_TRASH_CAN_NAME, AUTO_TRASH_CAN_TOOLTIP, (options, value) -> options.miscellaneous().autoCollectChestsFromTrashCans(value), options -> options.miscellaneous().autoCollectChestsFromTrashCans(), 150); - ToggleButtonWidget toggleButtonWidget = renderService.addToggleButton(directionalLayoutWidget2, HIGHLIGHT_CORPSES_NAME, HIGHLIGHT_CORPSES_TOOLTIP, (options, value) -> options.miscellaneous().highlightCorpses(value), options -> options.miscellaneous().highlightCorpses(), 150); - toggleButtonWidget.active = commandService.isSuperUser(); + ToggleButtonWidget toggleButton1 = new ToggleButtonWidget(HYDRATION_NAME, miscellaneousOptions::showHydration, miscellaneousOptions.showHydration()); + toggleButton1.setWidth(150); + toggleButton1.setTooltip(create(HYDRATION_TOOLTIP)); + gridLayoutRowHelper.addChild(toggleButton1); - LinearLayout directionalLayoutWidget3 = directionalLayoutWidget.addChild(horizontal().spacing(8)); - renderService.addToggleButton(directionalLayoutWidget3, HIDE_DOLPHINS_NAME, HIDE_DOLPHINS_TOOLTIP, (options, value) -> options.miscellaneous().hideDolphins(value), options -> options.miscellaneous().hideDolphins(), 150); + CycleButton cycleButton1 = CycleButton.builder(MiscellaneousOptions.AtmInformationType::getDisplayName, miscellaneousOptions.atmInformationType()) + .withValues(MiscellaneousOptions.AtmInformationType.values()) + .withTooltip(CyclingButtonEntry::getTooltip) + .create(BANK_INFORMATION_NAME, (_, value) -> miscellaneousOptions.atmInformationType(value)); + cycleButton1.setWidth(150); + gridLayoutRowHelper.addChild(cycleButton1); - directionalLayoutWidget.visitWidgets(this::addRenderableWidget); + Button button1 = Button.builder(AUTO_TRASH_CAN_NAME, _ -> this.minecraft.gui.setScreen(new TrashCanOptionsScreen(this))) + .tooltip(create(AUTO_TRASH_CAN_TOOLTIP)) + .width(150) + .build(); + gridLayoutRowHelper.addChild(button1); + + ToggleButtonWidget toggleButton2 = new ToggleButtonWidget(HIGHLIGHT_CORPSES_NAME, miscellaneousOptions::highlightCorpses, miscellaneousOptions.highlightCorpses()); + toggleButton2.setWidth(150); + toggleButton2.setTooltip(create(HIGHLIGHT_CORPSES_TOOLTIP)); + toggleButton2.active = commandService.isSuperUser(); + gridLayoutRowHelper.addChild(toggleButton2); + + ToggleButtonWidget toggleButton3 = new ToggleButtonWidget(HIDE_DOLPHINS_NAME, miscellaneousOptions::hideDolphins, miscellaneousOptions.hideDolphins()); + toggleButton3.setWidth(150); + toggleButton3.setTooltip(create(HIDE_DOLPHINS_TOOLTIP)); + gridLayoutRowHelper.addChild(toggleButton3); + + ToggleButtonWidget toggleButton4 = new ToggleButtonWidget(BLOCK_MALLE_SOUND_NAME, miscellaneousOptions::blockMalleSound, miscellaneousOptions.blockMalleSound()); + toggleButton4.setWidth(150); + toggleButton4.setTooltip(create(BLOCK_MALLE_SOUND_TOOLTIP)); + gridLayoutRowHelper.addChild(toggleButton4); + + ToggleButtonWidget toggleButton5 = new ToggleButtonWidget(AUTO_SIREN_NAME, miscellaneousOptions::autoSiren, miscellaneousOptions.autoSiren()); + toggleButton5.setWidth(150); + toggleButton5.setTooltip(create(AUTO_SIREN_TOOLTIP)); + gridLayoutRowHelper.addChild(toggleButton5); + + gridLayout.arrangeElements(); + gridLayout.visitWidgets(this::addRenderableWidget); } } diff --git a/src/main/java/de/rettichlp/ucutils/common/gui/screens/options/SoundOptionsScreen.java b/src/main/java/de/rettichlp/ucutils/common/gui/screens/options/SoundOptionsScreen.java index e508e52a..052ae3a8 100644 --- a/src/main/java/de/rettichlp/ucutils/common/gui/screens/options/SoundOptionsScreen.java +++ b/src/main/java/de/rettichlp/ucutils/common/gui/screens/options/SoundOptionsScreen.java @@ -2,13 +2,15 @@ import de.rettichlp.ucutils.common.configuration.options.SoundOptions; import de.rettichlp.ucutils.common.gui.screens.OptionsScreen; -import net.minecraft.client.gui.layouts.LinearLayout; +import de.rettichlp.ucutils.common.gui.screens.components.CyclingButtonEntry; +import de.rettichlp.ucutils.common.gui.screens.components.ToggleButtonWidget; +import net.minecraft.client.gui.components.CycleButton; +import net.minecraft.client.gui.layouts.GridLayout; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; -import static de.rettichlp.ucutils.UCUtils.renderService; -import static net.minecraft.client.gui.layouts.LinearLayout.horizontal; -import static net.minecraft.client.gui.layouts.LinearLayout.vertical; +import static de.rettichlp.ucutils.UCUtils.configuration; +import static net.minecraft.client.gui.components.Tooltip.create; import static net.minecraft.network.chat.Component.translatable; public class SoundOptionsScreen extends OptionsScreen { @@ -34,24 +36,59 @@ public SoundOptionsScreen(Screen parent) { @Override public void initBody() { - LinearLayout directionalLayoutWidget = this.layout.addToContents(vertical().spacing(4)); + GridLayout gridLayout = this.layout.addToContents(new GridLayout()); + gridLayout.columnSpacing(8).rowSpacing(4); + GridLayout.RowHelper gridLayoutRowHelper = gridLayout.createRowHelper(2); - LinearLayout directionalLayoutWidget1 = directionalLayoutWidget.addChild(horizontal().spacing(8)); - renderService.addCyclingButton(directionalLayoutWidget1, SOUND_BANK_ROBBERY_NAME, SoundOptions.StateSelect.values(), SoundOptions.StateSelect::getDisplayName, (options, e) -> options.sound().bankRobbery(e), options -> options.sound().bankRobbery(), 150); - renderService.addCyclingButton(directionalLayoutWidget1, SOUND_BOMB_NAME, SoundOptions.StateSelect.values(), SoundOptions.StateSelect::getDisplayName, (options, e) -> options.sound().bomb(e), options -> options.sound().bomb(), 150); + SoundOptions soundOptions = configuration.getOptions().sound(); - LinearLayout directionalLayoutWidget2 = directionalLayoutWidget.addChild(horizontal().spacing(8)); - renderService.addToggleButton(directionalLayoutWidget2, SOUND_CONTRACT_SET_NAME, SOUND_CONTRACT_SET_TOOLTIP, (options, value) -> options.sound().contractSet(value), options -> options.sound().contractSet(), 150); - renderService.addToggleButton(directionalLayoutWidget2, SOUND_CONTRACT_FULFILLED_NAME, SOUND_CONTRACT_FULFILLED_TOOLTIP, (options, value) -> options.sound().contractFulfilled(value), options -> options.sound().contractFulfilled(), 150); + CycleButton cycleButton1 = CycleButton.builder(SoundOptions.StateSelect::getDisplayName, soundOptions.bankRobbery()) + .withValues(SoundOptions.StateSelect.values()) + .withTooltip(CyclingButtonEntry::getTooltip) + .create(SOUND_BANK_ROBBERY_NAME, (_, value) -> soundOptions.bankRobbery(value)); + cycleButton1.setWidth(150); + gridLayoutRowHelper.addChild(cycleButton1); - LinearLayout directionalLayoutWidget3 = directionalLayoutWidget.addChild(horizontal().spacing(8)); - renderService.addToggleButton(directionalLayoutWidget3, SOUND_SERVICE_NAME, SOUND_SERVICE_TOOLTIP, (options, value) -> options.sound().service(value), options -> options.sound().service(), 150); - renderService.addCyclingButton(directionalLayoutWidget3, SOUND_FIRE_NAME, SoundOptions.MedicSelect.values(), SoundOptions.MedicSelect::getDisplayName, (options, e) -> options.sound().fire(e), options -> options.sound().fire(), 150); + CycleButton cycleButton2 = CycleButton.builder(SoundOptions.StateSelect::getDisplayName, soundOptions.bomb()) + .withValues(SoundOptions.StateSelect.values()) + .withTooltip(CyclingButtonEntry::getTooltip) + .create(SOUND_BOMB_NAME, (_, value) -> soundOptions.bomb(value)); + cycleButton2.setWidth(150); + gridLayoutRowHelper.addChild(cycleButton2); - LinearLayout directionalLayoutWidget4 = directionalLayoutWidget.addChild(horizontal().spacing(8)); - renderService.addToggleButton(directionalLayoutWidget4, SOUND_REPORT_NAME, SOUND_REPORT_TOOLTIP, (options, value) -> options.sound().report(value), options -> options.sound().report(), 150); - renderService.addToggleButton(directionalLayoutWidget4, SOUND_NOTIFICATION_NAME, SOUND_NOTIFICATION_TOOLTIP, (options, value) -> options.sound().notification(value), options -> options.sound().notification(), 150); + ToggleButtonWidget toggleButton1 = new ToggleButtonWidget(SOUND_CONTRACT_SET_NAME, soundOptions::contractSet, soundOptions.contractSet()); + toggleButton1.setWidth(150); + toggleButton1.setTooltip(create(SOUND_CONTRACT_SET_TOOLTIP)); + gridLayoutRowHelper.addChild(toggleButton1); - directionalLayoutWidget.visitWidgets(this::addRenderableWidget); + ToggleButtonWidget toggleButton2 = new ToggleButtonWidget(SOUND_CONTRACT_FULFILLED_NAME, soundOptions::contractFulfilled, soundOptions.contractFulfilled()); + toggleButton2.setWidth(150); + toggleButton2.setTooltip(create(SOUND_CONTRACT_FULFILLED_TOOLTIP)); + gridLayoutRowHelper.addChild(toggleButton2); + + ToggleButtonWidget toggleButton3 = new ToggleButtonWidget(SOUND_SERVICE_NAME, soundOptions::service, soundOptions.service()); + toggleButton3.setWidth(150); + toggleButton3.setTooltip(create(SOUND_SERVICE_TOOLTIP)); + gridLayoutRowHelper.addChild(toggleButton3); + + CycleButton cycleButton3 = CycleButton.builder(SoundOptions.MedicSelect::getDisplayName, soundOptions.fire()) + .withValues(SoundOptions.MedicSelect.values()) + .withTooltip(CyclingButtonEntry::getTooltip) + .create(SOUND_FIRE_NAME, (_, value) -> soundOptions.fire(value)); + cycleButton3.setWidth(150); + gridLayoutRowHelper.addChild(cycleButton3); + + ToggleButtonWidget toggleButton4 = new ToggleButtonWidget(SOUND_REPORT_NAME, soundOptions::report, soundOptions.report()); + toggleButton4.setWidth(150); + toggleButton4.setTooltip(create(SOUND_REPORT_TOOLTIP)); + gridLayoutRowHelper.addChild(toggleButton4); + + ToggleButtonWidget toggleButton5 = new ToggleButtonWidget(SOUND_NOTIFICATION_NAME, soundOptions::notification, soundOptions.notification()); + toggleButton5.setWidth(150); + toggleButton5.setTooltip(create(SOUND_NOTIFICATION_TOOLTIP)); + gridLayoutRowHelper.addChild(toggleButton5); + + gridLayout.arrangeElements(); + gridLayout.visitWidgets(this::addRenderableWidget); } } diff --git a/src/main/java/de/rettichlp/ucutils/common/gui/screens/options/TrashCanOptionsScreen.java b/src/main/java/de/rettichlp/ucutils/common/gui/screens/options/TrashCanOptionsScreen.java new file mode 100644 index 00000000..d1c9c2c9 --- /dev/null +++ b/src/main/java/de/rettichlp/ucutils/common/gui/screens/options/TrashCanOptionsScreen.java @@ -0,0 +1,73 @@ +package de.rettichlp.ucutils.common.gui.screens.options; + +import de.rettichlp.ucutils.common.gui.screens.OptionsScreen; +import de.rettichlp.ucutils.common.gui.screens.components.ToggleButtonWidget; +import net.minecraft.client.gui.layouts.GridLayout; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import org.jspecify.annotations.NonNull; + +import java.util.Collection; +import java.util.Set; +import java.util.function.Consumer; + +import static de.rettichlp.ucutils.UCUtils.configuration; +import static net.minecraft.network.chat.Component.translatable; +import static net.minecraft.world.item.Items.APPLE; +import static net.minecraft.world.item.Items.CARROT; +import static net.minecraft.world.item.Items.CHEST; +import static net.minecraft.world.item.Items.ENDER_CHEST; +import static net.minecraft.world.item.Items.PAPER; +import static net.minecraft.world.item.Items.POTATO; +import static net.minecraft.world.item.Items.ROTTEN_FLESH; +import static net.minecraft.world.item.Items.TRAPPED_CHEST; + +public class TrashCanOptionsScreen extends OptionsScreen { + + public static final Component TEXT_TRASH_CAN = translatable("ucutils.options.auto_trash_can.name"); + + private static final Set CHEST_ITEMS = Set.of(CHEST.toString(), TRAPPED_CHEST.toString(), ENDER_CHEST.toString()); + + public TrashCanOptionsScreen(Screen parent) { + super(parent, TEXT_TRASH_CAN); + } + + @Override + public void initBody() { + GridLayout gridLayout = this.layout.addToContents(new GridLayout()); + gridLayout.columnSpacing(8).rowSpacing(4); + GridLayout.RowHelper gridLayoutRowHelper = gridLayout.createRowHelper(2); + + Set autoCollectFromTrashCan = configuration.getOptions().autoCollectFromTrashCan(); + + addToggleButton(gridLayoutRowHelper, translatable("block.minecraft.chest"), autoCollectFromTrashCan.containsAll(CHEST_ITEMS), value -> { + if (value) { + autoCollectFromTrashCan.addAll(CHEST_ITEMS); + } else { + autoCollectFromTrashCan.removeAll(CHEST_ITEMS); + } + }); + addToggleButton(gridLayoutRowHelper, translatable("item.minecraft.apple"), autoCollectFromTrashCan.contains(APPLE.toString()), value -> setItemState(autoCollectFromTrashCan, APPLE.toString(), value)); + addToggleButton(gridLayoutRowHelper, translatable("item.minecraft.carrot"), autoCollectFromTrashCan.contains(CARROT.toString()), value -> setItemState(autoCollectFromTrashCan, CARROT.toString(), value)); + addToggleButton(gridLayoutRowHelper, translatable("item.minecraft.paper"), autoCollectFromTrashCan.contains(PAPER.toString()), value -> setItemState(autoCollectFromTrashCan, PAPER.toString(), value)); + addToggleButton(gridLayoutRowHelper, translatable("item.minecraft.potato"), autoCollectFromTrashCan.contains(POTATO.toString()), value -> setItemState(autoCollectFromTrashCan, POTATO.toString(), value)); + addToggleButton(gridLayoutRowHelper, translatable("item.minecraft.rotten_flesh"), autoCollectFromTrashCan.contains(ROTTEN_FLESH.toString()), value -> setItemState(autoCollectFromTrashCan, ROTTEN_FLESH.toString(), value)); + + gridLayout.arrangeElements(); + gridLayout.visitWidgets(this::addRenderableWidget); + } + + private void addToggleButton(GridLayout.@NonNull RowHelper gridLayoutRowHelper, Component name, boolean currentValue, Consumer setter) { + ToggleButtonWidget toggleButton = new ToggleButtonWidget(name, setter, currentValue); + toggleButton.setWidth(150); + gridLayoutRowHelper.addChild(toggleButton); + } + + private void setItemState(Collection autoCollectFromTrashCan, String item, boolean value) { + if (value) { + autoCollectFromTrashCan.add(item); + } else { + autoCollectFromTrashCan.remove(item); + } + } +} diff --git a/src/main/java/de/rettichlp/ucutils/common/gui/widgets/CorpseCountWidget.java b/src/main/java/de/rettichlp/ucutils/common/gui/widgets/CorpseCountWidget.java index fd8da5ff..2205e66a 100644 --- a/src/main/java/de/rettichlp/ucutils/common/gui/widgets/CorpseCountWidget.java +++ b/src/main/java/de/rettichlp/ucutils/common/gui/widgets/CorpseCountWidget.java @@ -38,6 +38,8 @@ public class CorpseCountWidget extends AbstractTRCTextWidget factionCorpseCountMap = new EnumMap<>(Faction.class); + private long lastCorpseScanTime = 0; + @Override public Component text() { long totalCount = this.factionCorpseCountMap.values().stream() @@ -87,8 +89,10 @@ public void addOptions(@NonNull TRCOptionsList optionsList) { @Override public boolean isVisible() { // every second re-scan for nearby corpses - if (currentTimeMillis() / 1000 % 2 == 0) { + long now = currentTimeMillis(); + if (now - this.lastCorpseScanTime >= 1000) { this.factionCorpseCountMap = getFactionCorpseCountMap(); + this.lastCorpseScanTime = now; } // visible if in the position options screen to allow positioning return super.isVisible() && (!this.factionCorpseCountMap.isEmpty() || isWidgetPositionScreen()); diff --git a/src/main/java/de/rettichlp/ucutils/common/integrations/TheRettingtonCompanionIntegration.java b/src/main/java/de/rettichlp/ucutils/common/integrations/TheRettingtonCompanionIntegration.java index bf06f77a..75ca388a 100644 --- a/src/main/java/de/rettichlp/ucutils/common/integrations/TheRettingtonCompanionIntegration.java +++ b/src/main/java/de/rettichlp/ucutils/common/integrations/TheRettingtonCompanionIntegration.java @@ -42,39 +42,39 @@ public List> getWidgets() { public List getHiddenMessages() { return List.of( // join - toHiddenMessage(compile("» Dein Account wird nicht mit einem Passwort geschützt!")), - toHiddenMessage(compile("» Schütze deinen Account mit /passwort new \\[Passwort]\\.")), + toHiddenMessage(compile("^» Dein Account wird nicht mit einem Passwort geschützt!$")), + toHiddenMessage(compile("^» Schütze deinen Account mit /passwort new \\[Passwort]\\.$")), // rotators - toHiddenMessage(compile("\\[F\\.A\\.Q] Du hast Fragen zum Server\\? Dann schau doch mal in unser FAQ rein\\.\\n\\[F\\.A\\.Q] Dort sind viele Antworten auf all deine Fragen\\.")), - toHiddenMessage(compile("\\[Shop] Hast du schon die tollen Sachen in unserem Shop entdeckt\\?\\n\\[Shop] Wir bedanken uns bei allen Unterstützern!")), - toHiddenMessage(compile("\\[Voten] Du kannst uns täglich mit deinen Votes unterstützen!\\n\\[Voten] Sammle genug Votepunkte um dir tolle Geschenke im Voteshop zu holen!")), - toHiddenMessage(compile("\\[Forum] Hast du schon einen Account in unserem Forum\\?\\n\\[Forum] Werde noch heute ein Teil dieser großartigen Community!")), - toHiddenMessage(compile("\\[Regeln] Unwissenheit schützt vor Strafe nicht!\\n\\[Regeln] Informiere dich in unserem Regelwerk\\.")), - toHiddenMessage(compile("\\[TikTok] Kennst du schon unseren TikTok Account\\?\\n\\[TikTok] Folge uns jetzt auf TikTok!")), - toHiddenMessage(compile("\\[Discord] Kennst du schon unseren Community-Discord\\?\\n\\[Discord] Updates, Ankündigungen und vieles mehr findest du dort!")), - toHiddenMessage(compile("\\[LabyMod] Wir sind offizieller Partner von LabyMod!\\n\\[LabyMod] Schau dir doch mal die LabyMod Client-Mod an\\.")), - toHiddenMessage(compile("\\[Premium] Noch keinen Premium-Account\\?\\n\\[Premium] Besuch doch mal unseren Shop!")), - toHiddenMessage(compile("\\[Passwort] Ist dein Account schon mit einen Passwort geschützt\\?\\n\\[Passwort] Schütze deinen Account noch heute mit einen Passwort!")), - toHiddenMessage(compile("\\[Instagram] Folgst du schon dem offiziellen UnicaCity Instagram-Account\\?\\n\\[Instagram] Du kannst uns unter dem Namen UnicaCity oder @unicacityeu finden!")), - toHiddenMessage(compile("\\[Teamspeak] Besuche uns doch auf unserem Teamspeak Server\\.\\n\\[Teamspeak] Die IP ist: unicacity\\.eu")), - toHiddenMessage(compile("\\[BattlePass] Hol dir tolle Belohnungen mit unserem BattlePass!\\n\\[BattlePass] Öffne ihn jederzeit mit /battlepass\\.")), + toHiddenMessage(compile("^\\[F\\.A\\.Q] Du hast Fragen zum Server\\? Dann schau doch mal in unser FAQ rein\\.\\n\\[F\\.A\\.Q] Dort sind viele Antworten auf all deine Fragen\\.$")), + toHiddenMessage(compile("^\\[Shop] Hast du schon die tollen Sachen in unserem Shop entdeckt\\?\\n\\[Shop] Wir bedanken uns bei allen Unterstützern!$")), + toHiddenMessage(compile("^\\[Voten] Du kannst uns täglich mit deinen Votes unterstützen!\\n\\[Voten] Sammle genug Votepunkte um dir tolle Geschenke im Voteshop zu holen!$")), + toHiddenMessage(compile("^\\[Forum] Hast du schon einen Account in unserem Forum\\?\\n\\[Forum] Werde noch heute ein Teil dieser großartigen Community!$")), + toHiddenMessage(compile("^\\[Regeln] Unwissenheit schützt vor Strafe nicht!\\n\\[Regeln] Informiere dich in unserem Regelwerk\\.$")), + toHiddenMessage(compile("^\\[TikTok] Kennst du schon unseren TikTok Account\\?\\n\\[TikTok] Folge uns jetzt auf TikTok!$")), + toHiddenMessage(compile("^\\[Discord] Kennst du schon unseren Community-Discord\\?\\n\\[Discord] Updates, Ankündigungen und vieles mehr findest du dort!$")), + toHiddenMessage(compile("^\\[LabyMod] Wir sind offizieller Partner von LabyMod!\\n\\[LabyMod] Schau dir doch mal die LabyMod Client-Mod an\\.$")), + toHiddenMessage(compile("^\\[Premium] Noch keinen Premium-Account\\?\\n\\[Premium] Besuch doch mal unseren Shop!$")), + toHiddenMessage(compile("^\\[Passwort] Ist dein Account schon mit einen Passwort geschützt\\?\\n\\[Passwort] Schütze deinen Account noch heute mit einen Passwort!$")), + toHiddenMessage(compile("^\\[Instagram] Folgst du schon dem offiziellen UnicaCity Instagram-Account\\?\\n\\[Instagram] Du kannst uns unter dem Namen UnicaCity oder @unicacityeu finden!$")), + toHiddenMessage(compile("^\\[Teamspeak] Besuche uns doch auf unserem Teamspeak Server\\.\\n\\[Teamspeak] Die IP ist: unicacity\\.eu$")), + toHiddenMessage(compile("^\\[BattlePass] Hol dir tolle Belohnungen mit unserem BattlePass!\\n\\[BattlePass] Öffne ihn jederzeit mit /battlepass\\.$")), // vote - toHiddenMessage(compile(" » Mit deinen Votes kannst du uns täglich unterstützen!")), - toHiddenMessage(compile(" » Du siehst bei /vote alle Seiten bei welchen du voten kannst\\.")), - toHiddenMessage(compile(" » Du erhältst diese Nachricht nach dem Voten nicht mehr\\.")), + toHiddenMessage(compile("^ » Mit deinen Votes kannst du uns täglich unterstützen!$")), + toHiddenMessage(compile("^ » Du siehst bei /vote alle Seiten bei welchen du voten kannst\\.$")), + toHiddenMessage(compile("^ » Du erhältst diese Nachricht nach dem Voten nicht mehr\\.$")), // afk - toHiddenMessage(compile(" {2}Info: Deine PayDay-Zeit läuft nun weiter\\.")), + toHiddenMessage(compile("^ {2}Info: Deine PayDay-Zeit läuft nun weiter\\.$")), // rubbish - toHiddenMessage(compile("Du hast etwas aus dem Mülleimer genommen\\.")), - toHiddenMessage(compile("Du durchwühlst den Mülleimer\\.")), + toHiddenMessage(compile("^Du hast etwas aus dem Mülleimer genommen\\.$")), + toHiddenMessage(compile("^Du durchwühlst den Mülleimer\\.$")), // cooking - toHiddenMessage(compile("\\[Küche] \uD83C\uDF73 Klicke das schwebende Item bevor es verschwindet!")), - toHiddenMessage(compile("\\[Küche] ✔ Erwischt! Qualität steigt\\.")), + toHiddenMessage(compile("^\\[Küche] \uD83C\uDF73 Klicke das schwebende Item bevor es verschwindet!$")), + toHiddenMessage(compile("^\\[Küche] ✔ Erwischt! Qualität steigt\\.$")), // lumberjack - toHiddenMessage(compile("\\[Holzfäller] 3 Perk\\(s\\) geladen\\.")), + toHiddenMessage(compile("^\\[Holzfäller] 3 Perk\\(s\\) geladen\\.$")), // faction: medics - toHiddenMessage(compile("\\[ʟᴀʙᴏʀ] \\d+ Kräuter-Rezept\\(e\\) ausgegeben \\(\\d+g Gras verbraucht · \\d+g verbleibend\\)\\.")), - toHiddenMessage(compile("\\[Tierarzt] Standort: -?\\d+ -?\\d+ -?\\d+ – behandle das Tier per Sneak-Rechtsklick\\.")) + toHiddenMessage(compile("^\\[ʟᴀʙᴏʀ] \\d+ Kräuter-Rezept\\(e\\) ausgegeben \\(\\d+g Gras verbraucht · \\d+g verbleibend\\)\\.$")), + toHiddenMessage(compile("^\\[Tierarzt] Standort: -?\\d+ -?\\d+ -?\\d+ – behandle das Tier per Sneak-Rechtsklick\\.$")) ); } diff --git a/src/main/java/de/rettichlp/ucutils/common/models/Faction.java b/src/main/java/de/rettichlp/ucutils/common/models/Faction.java index c8f83e8f..4763c124 100644 --- a/src/main/java/de/rettichlp/ucutils/common/models/Faction.java +++ b/src/main/java/de/rettichlp/ucutils/common/models/Faction.java @@ -6,6 +6,8 @@ import net.minecraft.world.Nameable; import org.jspecify.annotations.NonNull; +import java.util.stream.Stream; + import static net.minecraft.network.chat.TextColor.BLUE; import static net.minecraft.network.chat.TextColor.DARK_AQUA; import static net.minecraft.network.chat.TextColor.DARK_BLUE; @@ -44,6 +46,10 @@ public enum Faction { private final TextColor color; private final String icon; + private static final Faction[] CORPSE_FACTIONS = Stream.of(values()) + .filter(faction -> faction != NULL) + .toArray(Faction[]::new); + public static @NonNull Faction getFactionByCorpse(@NonNull Nameable itemEntity) { if (itemEntity.getCustomName() == null) { throw new IllegalArgumentException("ItemEntity has no custom name"); @@ -51,11 +57,7 @@ public enum Faction { String corpseName = itemEntity.getCustomName().getString(); - for (Faction faction : Faction.values()) { - if (faction == NULL) { - continue; - } - + for (Faction faction : CORPSE_FACTIONS) { if (corpseName.contains(faction.getIcon())) { return faction; } diff --git a/src/main/java/de/rettichlp/ucutils/common/models/StockMarketEntry.java b/src/main/java/de/rettichlp/ucutils/common/models/StockMarketEntry.java index b7148864..4f3a54f8 100644 --- a/src/main/java/de/rettichlp/ucutils/common/models/StockMarketEntry.java +++ b/src/main/java/de/rettichlp/ucutils/common/models/StockMarketEntry.java @@ -30,6 +30,11 @@ public record StockMarketEntry(Company company, double price, double changeMoney private static final Pattern BUY_PRICE_PATTERN = compile("EK-Preis: (?-?\\d+\\.\\d+)\\$"); public @Nullable Color getColor() { + // check if company is insolvent + if (this.price <= 0) { + return null; + } + // check if price is below or equal to min value if (this.price <= this.company.getMinValue()) { return BLUE; diff --git a/src/main/java/de/rettichlp/ucutils/common/registry/Registry.java b/src/main/java/de/rettichlp/ucutils/common/registry/Registry.java index d9b8a8ec..48e73801 100644 --- a/src/main/java/de/rettichlp/ucutils/common/registry/Registry.java +++ b/src/main/java/de/rettichlp/ucutils/common/registry/Registry.java @@ -15,6 +15,8 @@ import net.fabricmc.fabric.api.client.message.v1.ClientSendMessageEvents; import org.jetbrains.annotations.NotNull; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -33,6 +35,7 @@ public class Registry { private static final String NAVI_TARGET_REACHED_MESSAGE = "Du hast dein Ziel erreicht."; private final Set listenerInstances = getListenerInstances(); + private final Map, Set> listenersByInterface = new HashMap<>(); private boolean initialized = false; @@ -134,12 +137,15 @@ public void registerListeners() { .collect(toSet()); } + @SuppressWarnings("unchecked") private Set getListenersImplementing(Class listenerInterface) { - return storage.isUnicaCity() - ? this.listenerInstances.stream() - .filter(listenerInterface::isInstance) - .map(listenerInterface::cast) - .collect(toSet()) - : emptySet(); + if (!storage.isUnicaCity()) { + return emptySet(); + } + + return (Set) this.listenersByInterface.computeIfAbsent(listenerInterface, iface -> this.listenerInstances.stream() + .filter(iface::isInstance) + .map(iface::cast) + .collect(toSet())); } } diff --git a/src/main/java/de/rettichlp/ucutils/common/services/CommandService.java b/src/main/java/de/rettichlp/ucutils/common/services/CommandService.java index 7554f09b..f46acddb 100644 --- a/src/main/java/de/rettichlp/ucutils/common/services/CommandService.java +++ b/src/main/java/de/rettichlp/ucutils/common/services/CommandService.java @@ -7,8 +7,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Timer; -import java.util.TimerTask; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; import static de.rettichlp.ucutils.UCUtils.LOGGER; import static de.rettichlp.ucutils.UCUtils.nameTagService; @@ -18,6 +18,8 @@ import static java.lang.Boolean.getBoolean; import static java.lang.System.currentTimeMillis; import static java.util.Objects.nonNull; +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; +import static java.util.concurrent.TimeUnit.MILLISECONDS; public class CommandService { @@ -31,6 +33,12 @@ public class CommandService { @Getter private final List requestCommands = new ArrayList<>(); + private final ScheduledExecutorService commandScheduler = newSingleThreadScheduledExecutor(runnable -> { + Thread thread = new Thread(runnable, "ucutils-command-scheduler"); + thread.setDaemon(true); + return thread; + }); + public void sendCommand(String command) { LOGGER.info("UCUtils executing command: {}", command); networkHandler.sendCommand(command); @@ -53,18 +61,16 @@ public void sendCommands(List commandStrings) { public void sendCommands(List commandStrings, long cooldownMillis) { // to modifiable list List commands = new ArrayList<>(commandStrings); + ScheduledFuture[] taskHolder = new ScheduledFuture[1]; - new Timer().scheduleAtFixedRate(new TimerTask() { - @Override - public void run() { - if (commands.isEmpty()) { - this.cancel(); - return; - } - - sendCommand(commands.removeFirst()); + taskHolder[0] = this.commandScheduler.scheduleAtFixedRate(() -> { + if (commands.isEmpty()) { + taskHolder[0].cancel(false); + return; } - }, 0, cooldownMillis); + + sendCommand(commands.removeFirst()); + }, 0, cooldownMillis, MILLISECONDS); } public void sendCommandsWithAwaitingResponse(@NotNull List commands) { diff --git a/src/main/java/de/rettichlp/ucutils/common/services/NameTagService.java b/src/main/java/de/rettichlp/ucutils/common/services/NameTagService.java index 0fffed70..90168b65 100644 --- a/src/main/java/de/rettichlp/ucutils/common/services/NameTagService.java +++ b/src/main/java/de/rettichlp/ucutils/common/services/NameTagService.java @@ -1,6 +1,7 @@ package de.rettichlp.ucutils.common.services; import net.minecraft.ChatFormatting; +import net.minecraft.client.multiplayer.PlayerInfo; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.world.scores.Team; @@ -34,21 +35,18 @@ public class NameTagService { .append(literal("]").withStyle(DARK_GRAY)); public boolean isAfk(String targetName) { - return networkHandler.getOnlinePlayers().stream() - .filter(entry -> entry.getProfile().name().equals(targetName)) - .anyMatch(entry -> { - Team team = entry.getTeam(); - return team != null && !isADuty(targetName) && team.getCollisionRule() == NEVER; - }); + PlayerInfo playerInfo = networkHandler.getPlayerInfo(targetName); + if (playerInfo == null) { + return false; + } + + Team team = playerInfo.getTeam(); + return team != null && !isADuty(playerInfo) && team.getCollisionRule() == NEVER; } - public boolean isADuty(String targetName) { - return networkHandler.getOnlinePlayers().stream() - .filter(entry -> entry.getProfile().name().equals(targetName)) - .anyMatch(entry -> { - Component displayName = entry.getTabListDisplayName(); - return displayName != null && displayName.contains(A_DUTY_PREFIX); - }); + private boolean isADuty(@NotNull PlayerInfo playerInfo) { + Component displayName = playerInfo.getTabListDisplayName(); + return displayName != null && displayName.contains(A_DUTY_PREFIX); } public @NotNull ChatFormatting getWantedPointColor(int wantedPointAmount) { diff --git a/src/main/java/de/rettichlp/ucutils/common/services/SyncService.java b/src/main/java/de/rettichlp/ucutils/common/services/SyncService.java index 3a559025..4b485423 100644 --- a/src/main/java/de/rettichlp/ucutils/common/services/SyncService.java +++ b/src/main/java/de/rettichlp/ucutils/common/services/SyncService.java @@ -27,13 +27,13 @@ public class SyncService { public void syncFactionMembers() { + storage.getFactionEntries().clear(); + for (Faction faction : Faction.values()) { if (faction == NULL) { continue; } - storage.getFactionEntries().clear(); - api.getFactionMembers(faction, factionMembers -> { // to faction entry FactionEntry factionEntry = new FactionEntry(faction, factionMembers); diff --git a/src/main/java/de/rettichlp/ucutils/common/services/UtilService.java b/src/main/java/de/rettichlp/ucutils/common/services/UtilService.java index 3016b069..847b6c6f 100644 --- a/src/main/java/de/rettichlp/ucutils/common/services/UtilService.java +++ b/src/main/java/de/rettichlp/ucutils/common/services/UtilService.java @@ -9,10 +9,11 @@ import org.jspecify.annotations.NonNull; import java.time.ZoneId; -import java.util.Timer; -import java.util.TimerTask; +import java.util.concurrent.ScheduledExecutorService; import static de.rettichlp.ucutils.UCUtils.MOD_ID; +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static net.minecraft.world.item.Items.SKELETON_SKULL; import static net.minecraft.world.item.Items.WITHER_SKELETON_SKULL; @@ -21,13 +22,14 @@ public class UtilService { @Getter private final ZoneId serverZoneId = ZoneId.of("Europe/Berlin"); + private final ScheduledExecutorService delayedActionScheduler = newSingleThreadScheduledExecutor(runnable -> { + Thread thread = new Thread(runnable, "ucutils-delayed-action"); + thread.setDaemon(true); + return thread; + }); + public void delayedAction(Runnable runnable, long milliseconds) { - new Timer().schedule(new TimerTask() { - @Override - public void run() { - Minecraft.getInstance().execute(runnable); - } - }, milliseconds); + this.delayedActionScheduler.schedule(() -> Minecraft.getInstance().execute(runnable), milliseconds, MILLISECONDS); } public String getVersion() { diff --git a/src/main/java/de/rettichlp/ucutils/listener/impl/EconomyListener.java b/src/main/java/de/rettichlp/ucutils/listener/impl/EconomyListener.java index 855cda0f..72ef347d 100644 --- a/src/main/java/de/rettichlp/ucutils/listener/impl/EconomyListener.java +++ b/src/main/java/de/rettichlp/ucutils/listener/impl/EconomyListener.java @@ -68,6 +68,7 @@ public class EconomyListener implements IMessageReceiveListener { private static final Pattern STOCK_MARKET_SELL_PATTERN = compile("^\\[Aktien] (?\\d+)x (?.+) verkauft für (?\\d+)\\$\\. \\(Gebühr: (?\\d+)\\$, Steuer: (?\\d+)\\$\\) (?[+-]\\d+)\\$ Brutto / (?[+-]\\d+)\\$ Netto$"); // other + private static final Pattern ATM_MONEY_AMOUNT_PATTERN = compile("ATM \\d+: (?\\d+)\\$/100000\\$"); private static final Pattern BUSINESS_CASH_PATTERN = compile("^Kasse: (\\d+)\\$$"); private static final Pattern EXP_PATTERN = compile("(?[+-]\\d+) Exp!( \\(x(?\\d)\\))?$"); private static final Pattern MAX_EXP_REACHED_PATTERN = compile("^Du hast die maximale Exp erreicht! Benutze /buylevel um ein Level aufzusteigen\\.$"); @@ -77,6 +78,7 @@ public class EconomyListener implements IMessageReceiveListener { private static final Pattern MEDIC_REVIVE_PATTERN = compile("^Du wirst von (?:\\[UC])?(?[a-zA-Z0-9_]+) wiederbelebt\\.$"); private static final Pattern REVIVE_ADMIN_PATTERN = compile("^Du wurdest von \\[UC](?[a-zA-Z0-9_]+) wiederbelebt\\.$"); private static final Pattern BACK_IN_LIFE_PATTERN = compile("^\\[Friedhof] Du lebst nun wieder\\.$"); + private static final Pattern LUMBERJACK_SELL_PATTERN = compile("^\\[Holzfäller] Du hast (\\d+x .+|dein ganzes Inventar abgeladen: \\d+ Items) für §6(?\\d+)\\$§a( §6\\(\\+\\d+% Bonus\\))?( verkauft)?\\.$"); private long lastMedicReviveAction = 0; private boolean maxExperiencePerLevelReached = false; @@ -306,6 +308,13 @@ public boolean onMessageReceive(Component text, String message) { return true; } + Matcher moneyAtmAmountMatcher = ATM_MONEY_AMOUNT_PATTERN.matcher(message); + if (moneyAtmAmountMatcher.find()) { + int moneyAtmAmount = parseInt(moneyAtmAmountMatcher.group("moneyAtmAmount")); + storage.setMoneyAtmAmount(moneyAtmAmount); + return true; + } + Matcher businessCashMatcher = BUSINESS_CASH_PATTERN.matcher(message); if (businessCashMatcher.find()) { String amountString = businessCashMatcher.group(1); @@ -384,6 +393,13 @@ public boolean onMessageReceive(Component text, String message) { } storage.setDead(false); + return true; + } + + Matcher lumberjackSellMatcher = LUMBERJACK_SELL_PATTERN.matcher(message); + if (lumberjackSellMatcher.find()) { + int amount = parseInt(lumberjackSellMatcher.group("amount")); + configuration.setMoneyCashAmount(configuration.getMoneyCashAmount() + amount); } return true; diff --git a/src/main/java/de/rettichlp/ucutils/listener/impl/SoundTriggerListener.java b/src/main/java/de/rettichlp/ucutils/listener/impl/SoundTriggerListener.java index c584d149..ac8a3f7f 100644 --- a/src/main/java/de/rettichlp/ucutils/listener/impl/SoundTriggerListener.java +++ b/src/main/java/de/rettichlp/ucutils/listener/impl/SoundTriggerListener.java @@ -29,16 +29,14 @@ public class SoundTriggerListener implements IMessageReceiveListener { @Override public boolean onMessageReceive(Component text, String message) { - Faction playerFaction = storage.getFaction(player.getGameProfile().name()); - Matcher bankRobberyMatcher = BANK_ROBBERY_PATTERN.matcher(message); - if (bankRobberyMatcher.find() && configuration.getOptions().sound().bankRobbery().verify(playerFaction)) { + if (bankRobberyMatcher.find() && configuration.getOptions().sound().bankRobbery().verify(playerFaction())) { BANK_ROBBERY.play(); return true; } Matcher bombFoundMatcher = BOMB_FOUND_PATTERN.matcher(message); - if (bombFoundMatcher.find() && configuration.getOptions().sound().bomb().verify(playerFaction)) { + if (bombFoundMatcher.find() && configuration.getOptions().sound().bomb().verify(playerFaction())) { BOMB_SOUND.play(); return true; } @@ -56,11 +54,15 @@ public boolean onMessageReceive(Component text, String message) { } Matcher fireStartMatcher = FIRE_START_PATTERN.matcher(message); - if (fireStartMatcher.find() && configuration.getOptions().sound().fire().verify(playerFaction)) { + if (fireStartMatcher.find() && configuration.getOptions().sound().fire().verify(playerFaction())) { FIRE.play(); return true; } return true; } + + private Faction playerFaction() { + return storage.getFaction(player.getGameProfile().name()); + } } diff --git a/src/main/java/de/rettichlp/ucutils/listener/impl/faction/FactionListener.java b/src/main/java/de/rettichlp/ucutils/listener/impl/faction/FactionListener.java index de707ab9..10dd2c5f 100644 --- a/src/main/java/de/rettichlp/ucutils/listener/impl/faction/FactionListener.java +++ b/src/main/java/de/rettichlp/ucutils/listener/impl/faction/FactionListener.java @@ -42,6 +42,8 @@ @UCUtilsListener public class FactionListener implements IMessageReceiveListener, IMessageSendListener { + private static final Pattern DUTY_PATTERN = compile("^(Du bist nun als Arzt im Dienst!|Du hast den Dienst wieder angetreten\\.)$"); + private static final Pattern REINFORCEMENT_PATTERN = compile("^(?:(?.+)! )?(?.+) (?:\\[UC])?(?[a-zA-Z0-9_]+) benötigt Unterstützung in der Nähe von (?.+)! \\((?\\d+) Meter entfernt\\)$"); private static final Pattern REINFORCEMENT_BUTTON_PATTERN = compile("^§7» §c§lRoute anzeigen §8\\| §c§lUnterwegs$"); private static final Pattern REINFORCMENT_ON_THE_WAY_PATTERN = compile("^(?.+) (?:\\[UC])?(?[a-zA-Z0-9_]+) kommt zum Verstärkungsruf von (?:\\[UC])?(?[a-zA-Z0-9_]+)! \\((?\\d+) Meter entfernt\\)$"); @@ -69,6 +71,14 @@ public class FactionListener implements IMessageReceiveListener, IMessageSendLis @Override public boolean onMessageReceive(Component text, String message) { + Matcher dutyMatcher = DUTY_PATTERN.matcher(message); + if (dutyMatcher.find()) { + if (configuration.getOptions().miscellaneous().autoSiren()) { + commandService.sendCommand("sirene"); + } + return true; + } + Matcher reinforcementMatcher = REINFORCEMENT_PATTERN.matcher(message); if (reinforcementMatcher.find()) { String type = ofNullable(reinforcementMatcher.group("type")).orElse("Reinforcement"); diff --git a/src/main/java/de/rettichlp/ucutils/listener/impl/job/AnglerListener.java b/src/main/java/de/rettichlp/ucutils/listener/impl/job/AnglerListener.java index e2303a45..4f5a9952 100644 --- a/src/main/java/de/rettichlp/ucutils/listener/impl/job/AnglerListener.java +++ b/src/main/java/de/rettichlp/ucutils/listener/impl/job/AnglerListener.java @@ -12,6 +12,7 @@ import java.awt.image.BufferedImage; import java.io.File; +import java.util.concurrent.ExecutorService; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -20,6 +21,7 @@ import static de.rettichlp.ucutils.UCUtils.storage; import static de.rettichlp.ucutils.UCUtils.utilService; import static java.awt.image.BufferedImage.TYPE_INT_RGB; +import static java.util.concurrent.Executors.newSingleThreadExecutor; import static java.util.regex.Pattern.compile; import static javax.imageio.ImageIO.write; import static net.minecraft.core.component.DataComponents.MAP_ID; @@ -35,6 +37,12 @@ public class AnglerListener implements IMessageReceiveListener { private static final Pattern ANGLER_CAPTCHA_PATTERN = compile("^Lies den Code auf der Karte in deiner Nebenhand ab und schreib ihn in den Chat\\.$"); private static final Pattern ANGLER_CAPTCHA_CONTINUED_PATTERN = compile("^Deine Combo wurde fortgesetzt! \\(\\d+\\)$"); + private static final ExecutorService CAPTCHA_IMAGE_WRITER = newSingleThreadExecutor(runnable -> { + Thread thread = new Thread(runnable, "ucutils-captcha-image-writer"); + thread.setDaemon(true); + return thread; + }); + @Override public boolean onMessageReceive(Component text, String message) { Minecraft client = Minecraft.getInstance(); @@ -76,15 +84,17 @@ private void saveCaptchaImage(MapId mapId) { return; } - File outputFile = new File("screenshots/ucutils/captcha.png"); + CAPTCHA_IMAGE_WRITER.execute(() -> { + File outputFile = new File("screenshots/ucutils/captcha.png"); - try { - outputFile.mkdirs(); - outputFile.createNewFile(); - write(mapImage, "PNG", outputFile); - } catch (Exception e) { - LOGGER.error("Failed to save captcha image", e); - } + try { + outputFile.getParentFile().mkdirs(); + outputFile.createNewFile(); + write(mapImage, "PNG", outputFile); + } catch (Exception e) { + LOGGER.error("Failed to save captcha image", e); + } + }); } private @Nullable BufferedImage getMapImage(MapId mapId) { diff --git a/src/main/java/de/rettichlp/ucutils/mixin/AbstractContainerScreenMixin.java b/src/main/java/de/rettichlp/ucutils/mixin/AbstractContainerScreenMixin.java index d474d603..1ab4a310 100644 --- a/src/main/java/de/rettichlp/ucutils/mixin/AbstractContainerScreenMixin.java +++ b/src/main/java/de/rettichlp/ucutils/mixin/AbstractContainerScreenMixin.java @@ -29,6 +29,7 @@ import java.awt.Color; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.regex.Matcher; import java.util.stream.Stream; @@ -55,7 +56,6 @@ import static net.minecraft.network.chat.Component.literal; import static net.minecraft.network.chat.Component.translatable; import static net.minecraft.world.inventory.ContainerInput.PICKUP; -import static net.minecraft.world.item.Items.CHEST; import static net.minecraft.world.item.Items.PLAYER_HEAD; @Mixin(AbstractContainerScreen.class) @@ -97,22 +97,24 @@ protected AbstractContainerScreenMixin(Component title) { extractStockMarketHighlight(graphics, mouseX, mouseY, a); extractStockMarketLegend(graphics, mouseX, mouseY, a); } - case "Telefon" -> { + case "ʜᴀɴᴅʏ » üʙᴇʀsɪᴄʜᴛ" -> { if (storage.isStockMarketCommandRunning()) { - clickOnItemWithName("Apps", gameMode); + clickOnItemWithName("ᴀᴘᴘs", gameMode); } } - case "App-Menü" -> { + case "ʜᴀɴᴅʏ » ᴀᴘᴘ-ᴍᴇɴü" -> { if (storage.isStockMarketCommandRunning()) { clickOnItemWithName("Aktien", gameMode); } } case "Mülleimer" -> { - if (configuration.getOptions().miscellaneous().autoCollectChestsFromTrashCans()) { - for (int i = 0; i < 5; i++) { - if (getMenu().slots.get(i).getItem().is(CHEST)) { - gameMode.handleContainerInput(getMenu().containerId, i, 0, PICKUP, player); - } + Set autoCollectFromTrashCan = configuration.getOptions().autoCollectFromTrashCan(); + + for (int i = 0; i < 5; i++) { + ItemStack itemStack = getMenu().slots.get(i).getItem(); + boolean shouldCollect = autoCollectFromTrashCan.contains(itemStack.getItem().toString()); + if (shouldCollect) { + gameMode.handleContainerInput(getMenu().containerId, i, 0, PICKUP, player); } } } diff --git a/src/main/java/de/rettichlp/ucutils/mixin/FontSetMixin.java b/src/main/java/de/rettichlp/ucutils/mixin/FontSetMixin.java new file mode 100644 index 00000000..d917313b --- /dev/null +++ b/src/main/java/de/rettichlp/ucutils/mixin/FontSetMixin.java @@ -0,0 +1,33 @@ +package de.rettichlp.ucutils.mixin; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import net.minecraft.client.gui.font.FontSet; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +@Mixin(FontSet.class) +public abstract class FontSetMixin { + + @Unique + private static final IntSet EXCLUDED_RANDOM_GLYPHS = new IntOpenHashSet(); + + static { + "\uE000\uE001\uE002\uE003\uE004\uE005\uE006\uE007\uE008\uE009\uE00A\uE00B\uE00C\uE00D\uE00E\uE00F\uE010\uE011\uE012\uE013\uE014".codePoints().forEach(EXCLUDED_RANDOM_GLYPHS::add); + } + + @ModifyVariable(method = "getRandomGlyph", at = @At("STORE"), name = "chars") + private IntList ucutils$getRandomGlyphStore(IntList chars) { + if (chars == null || chars.isEmpty()) { + return chars; + } + + IntList filtered = new IntArrayList(chars); + filtered.removeIf(EXCLUDED_RANDOM_GLYPHS::contains); + return filtered; + } +} diff --git a/src/main/java/de/rettichlp/ucutils/mixin/HudMixin.java b/src/main/java/de/rettichlp/ucutils/mixin/HudMixin.java index 5f180bad..30fafa2e 100644 --- a/src/main/java/de/rettichlp/ucutils/mixin/HudMixin.java +++ b/src/main/java/de/rettichlp/ucutils/mixin/HudMixin.java @@ -11,6 +11,7 @@ import net.minecraft.resources.Identifier; import net.minecraft.util.profiling.Profiler; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.level.saveddata.maps.MapId; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -51,26 +52,34 @@ public abstract class HudMixin { @Unique private static final Identifier CAPTCHA_IDENTIFIER = fromNamespaceAndPath(MOD_ID, "captcha"); + @Unique + private MapId loadedCaptchaMap; + @Shadow @Final private Minecraft minecraft; @Inject(method = "extractRenderState", at = @At("TAIL")) private void ucutils$extractChatTail(GuiGraphicsExtractor graphics, DeltaTracker deltaTracker, CallbackInfo ci) { - if (storage.getCaptchaMap() == null) { + MapId captchaMap = storage.getCaptchaMap(); + if (captchaMap == null) { + this.loadedCaptchaMap = null; return; } - NativeImage nativeImage; - try (InputStream is = newInputStream(Path.of("screenshots/ucutils/captcha.png"))) { - nativeImage = read(is); - } catch (Exception e) { - LOGGER.error("Failed to read captcha image", e); - return; - } + if (!captchaMap.equals(this.loadedCaptchaMap)) { + NativeImage nativeImage; + try (InputStream is = newInputStream(Path.of("screenshots/ucutils/captcha.png"))) { + nativeImage = read(is); + } catch (Exception e) { + LOGGER.error("Failed to read captcha image", e); + return; + } - AbstractTexture texture = new DynamicTexture(() -> "captcha", nativeImage); - this.minecraft.getTextureManager().register(CAPTCHA_IDENTIFIER, texture); + AbstractTexture texture = new DynamicTexture(() -> "captcha", nativeImage); + this.minecraft.getTextureManager().register(CAPTCHA_IDENTIFIER, texture); + this.loadedCaptchaMap = captchaMap; + } int side = graphics.guiWidth() / 4; int x = graphics.guiWidth() / 2 - side / 2; diff --git a/src/main/java/de/rettichlp/ucutils/mixin/SoundManagerMixin.java b/src/main/java/de/rettichlp/ucutils/mixin/SoundManagerMixin.java new file mode 100644 index 00000000..d3eb5ae6 --- /dev/null +++ b/src/main/java/de/rettichlp/ucutils/mixin/SoundManagerMixin.java @@ -0,0 +1,29 @@ +package de.rettichlp.ucutils.mixin; + +import net.minecraft.client.resources.sounds.SoundInstance; +import net.minecraft.client.sounds.SoundEngine; +import net.minecraft.client.sounds.SoundManager; +import net.minecraft.resources.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import static de.rettichlp.ucutils.UCUtils.configuration; +import static net.minecraft.client.sounds.SoundEngine.PlayResult.NOT_STARTED; +import static net.minecraft.resources.Identifier.fromNamespaceAndPath; + +@Mixin(SoundManager.class) +public abstract class SoundManagerMixin { + + @Unique + private static final Identifier MALLE_SOUND_IDENTIFIER = fromNamespaceAndPath("ucmusic", "music.malle_i_love_you"); + + @Inject(method = "play", at = @At("HEAD"), cancellable = true) + private void ucutils$playHead(SoundInstance instance, CallbackInfoReturnable cir) { + if (configuration.getOptions().miscellaneous().blockMalleSound() && MALLE_SOUND_IDENTIFIER.equals(instance.getIdentifier())) { + cir.setReturnValue(NOT_STARTED); + } + } +} diff --git a/src/main/resources/assets/ucutils/lang/de_de.json b/src/main/resources/assets/ucutils/lang/de_de.json index 47c3fd93..a7dfb3fb 100644 --- a/src/main/resources/assets/ucutils/lang/de_de.json +++ b/src/main/resources/assets/ucutils/lang/de_de.json @@ -91,11 +91,15 @@ "ucutils.options.hydration.name": "Durstanzeige", "ucutils.options.hydration.tooltip": "Zeigt deinen Durst über der Hungeranzeige an", "ucutils.options.auto_trash_can.name": "Automatischer Mülleimer", - "ucutils.options.auto_trash_can.tooltip": "Sammelt automatisch Kisten aus Mülleimern", + "ucutils.options.auto_trash_can.tooltip": "Sammelt automatisch Gegenstände aus Mülleimern", "ucutils.options.highlight_corpses.name": "Leichen hervorheben", "ucutils.options.highlight_corpses.tooltip": "Lässt Leichen in der Farbe ihrer Fraktion leuchten", "ucutils.options.hide_dolphins.name": "Verstecke Delfine", "ucutils.options.hide_dolphins.tooltip": "Verhindert das Spawnen von Delfinen während des öffnens einer Schatzkiste", + "ucutils.options.block_malle_sound.name": "Malle-Sound blockieren", + "ucutils.options.block_malle_sound.tooltip": "Verhindert das Abspielen des \"Malle, I love you\"-Sounds", + "ucutils.options.auto_siren.name": "Automatische Sirene", + "ucutils.options.auto_siren.tooltip": "Aktiviert automatisch die Sirene, wenn du in den Dienst gehst", "ucutils.options.check_unicacity_server.name": "UnicaCity Check", "ucutils.options.check_unicacity_server.tooltip": "Überprüft ob du auf UnicaCity spielst. Wenn aktiviert sind bestimmte Funktionen nur auf UnicaCity aktiviert um Probleme mit anderen Servern zu vermeiden.", "ucutils.options.faction_chat_color.name": "Farbe des Fraktionschats", diff --git a/src/main/resources/assets/ucutils/lang/en_gb.json b/src/main/resources/assets/ucutils/lang/en_gb.json index 7c5592e3..8426d4de 100644 --- a/src/main/resources/assets/ucutils/lang/en_gb.json +++ b/src/main/resources/assets/ucutils/lang/en_gb.json @@ -91,11 +91,15 @@ "ucutils.options.hydration.name": "Hydration display", "ucutils.options.hydration.tooltip": "Displays your hydration above your hunger bar", "ucutils.options.auto_trash_can.name": "Auto trash can", - "ucutils.options.auto_trash_can.tooltip": "Automatically collects chests from trash cans", + "ucutils.options.auto_trash_can.tooltip": "Automatically collects items from trash cans", "ucutils.options.highlight_corpses.name": "Highlight corpses", "ucutils.options.highlight_corpses.tooltip": "Makes corpses glow in their faction's colour", "ucutils.options.hide_dolphins.name": "Hide dolphins", "ucutils.options.hide_dolphins.tooltip": "Prevents dolphins from spawning while opening a treasure chest", + "ucutils.options.block_malle_sound.name": "Block Malle sound", + "ucutils.options.block_malle_sound.tooltip": "Prevents the \"Malle, I love you\" sound from playing", + "ucutils.options.auto_siren.name": "Auto siren", + "ucutils.options.auto_siren.tooltip": "Automatically activates the siren when you go on duty", "ucutils.options.check_unicacity_server.name": "Check for UnicaCity", "ucutils.options.check_unicacity_server.tooltip": "Checks if you are playing on the UnicaCity server. If enabled, certain features will only be available on the UnicaCity server to prevent issues on other servers.", "ucutils.options.faction_chat_color.name": "Faction chat color", diff --git a/src/main/resources/assets/ucutils/lang/en_us.json b/src/main/resources/assets/ucutils/lang/en_us.json index 6e7910ea..4a1ba921 100644 --- a/src/main/resources/assets/ucutils/lang/en_us.json +++ b/src/main/resources/assets/ucutils/lang/en_us.json @@ -91,11 +91,15 @@ "ucutils.options.hydration.name": "Hydration display", "ucutils.options.hydration.tooltip": "Displays your hydration above your hunger bar", "ucutils.options.auto_trash_can.name": "Auto trash can", - "ucutils.options.auto_trash_can.tooltip": "Automatically collects chests from trash cans", + "ucutils.options.auto_trash_can.tooltip": "Automatically collects items from trash cans", "ucutils.options.highlight_corpses.name": "Highlight corpses", "ucutils.options.highlight_corpses.tooltip": "Makes corpses glow in their faction's color", "ucutils.options.hide_dolphins.name": "Hide dolphins", "ucutils.options.hide_dolphins.tooltip": "Prevents dolphins from spawning while opening a treasure chest", + "ucutils.options.block_malle_sound.name": "Block Malle sound", + "ucutils.options.block_malle_sound.tooltip": "Prevents the \"Malle, I love you\" sound from playing", + "ucutils.options.auto_siren.name": "Auto siren", + "ucutils.options.auto_siren.tooltip": "Automatically activates the siren when you go on duty", "ucutils.options.check_unicacity_server.name": "Check for UnicaCity", "ucutils.options.check_unicacity_server.tooltip": "Checks if you are playing on the UnicaCity server. If enabled, certain features will only be available on the UnicaCity server to prevent issues on other servers.", "ucutils.options.faction_chat_color.name": "Faction chat color", diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 81ffc13d..3d24700a 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -42,7 +42,7 @@ "fabric-api": "*", "the-rettington-companion": [ "0.0.0", - ">=2.2.0" + ">=2.4.0" ] }, "recommends": { diff --git a/src/main/resources/ucutils.mixins.json b/src/main/resources/ucutils.mixins.json index f4f47be7..9a921fa6 100644 --- a/src/main/resources/ucutils.mixins.json +++ b/src/main/resources/ucutils.mixins.json @@ -21,11 +21,13 @@ "ClientPlayNetworkHandlerMixin", "EntityHitboxDebugRendererMixin", "EntityRendererMixin", + "FontSetMixin", "HudMixin", "ItemEntityRendererMixin", "KeyboardHandlerMixin", "LocalPlayerMixin", "PauseScreenMixin", - "PlayerTabOverlayMixin" + "PlayerTabOverlayMixin", + "SoundManagerMixin" ] }