From 3db3627ec03baa134940a92e596e591f2d88aa0c Mon Sep 17 00:00:00 2001 From: David Cole <40234707+DavidArthurCole@users.noreply.github.com> Date: Sun, 8 Sep 2024 03:29:27 -0400 Subject: Feature: Hoppity Call Warning (#2272) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../features/event/hoppity/HoppityCallWarning.kt | 155 +++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCallWarning.kt (limited to 'src/main/java/at/hannibal2/skyhanni/features') diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCallWarning.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCallWarning.kt new file mode 100644 index 000000000..57bc0975f --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCallWarning.kt @@ -0,0 +1,155 @@ +package at.hannibal2.skyhanni.features.event.hoppity + +import at.hannibal2.skyhanni.events.ConfigLoadEvent +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.events.LorenzKeyPressEvent +import at.hannibal2.skyhanni.events.SecondPassedEvent +import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.ColorUtils.toChromaColorInt +import at.hannibal2.skyhanni.utils.ConditionalUtils +import at.hannibal2.skyhanni.utils.DelayedRun +import at.hannibal2.skyhanni.utils.HypixelCommands +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.RegexUtils.matches +import at.hannibal2.skyhanni.utils.SoundUtils +import at.hannibal2.skyhanni.utils.StringUtils.isValidUuid +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.Gui +import net.minecraft.client.renderer.GlStateManager +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import org.lwjgl.input.Keyboard +import java.time.Instant +import kotlin.math.sin +import kotlin.time.Duration.Companion.seconds + +@SkyHanniModule +object HoppityCallWarning { + + /** + * Test messages (and the real ones from Hypixel) have a space at the end of + * them that the IDE kills. So it's "§r§e ✆ " + * + * REGEX-TEST: §e✆ §r§bHoppity§r§e ✆ + * REGEX-TEST: §e✆ §r§aHoppity§r§e ✆ + */ + private val initHoppityCallPattern by ChocolateFactoryAPI.patternGroup.pattern( + "hoppity.call.init", + "§e✆ §r(?:§a|§b)Hoppity§r§e ✆.*", + ) + + /** + * REGEX-TEST: §a✆ RING... §r §r§2§l[PICK UP] + * REGEX-TEST: §a✆ RING... RING... §r §r§2§l[PICK UP] + * REGEX-TEST: §a✆ RING... RING... RING... §r §r§2§l[PICK UP] + * REGEX-TEST: §a✆ RING... RING... RING... + */ + private val callRingPattern by ChocolateFactoryAPI.patternGroup.pattern( + "hoppity.call.ring", + "§a✆ (?:RING\\.{3} ?){1,3}(?:§r §r§2§l\\[PICK UP])?", + ) + + /** + * REGEX-TEST: §e[NPC] §aHoppity§f: §b✆ §f§rWhat's up, §boBlazin§f? + */ + private val pickupHoppityCallPattern by ChocolateFactoryAPI.patternGroup.pattern( + "hoppity.call.pickup", + "§e\\[NPC] §aHoppity§f: §b✆ §f§rWhat's up, .*§f\\?", + ) + + private val config get() = HoppityEggsManager.config.hoppityCallWarning + private var warningSound = SoundUtils.createSound("note.pling", 1f) + private var activeWarning = false + private var nextWarningTime: Instant? = null + private var finalWarningTime: Instant? = null + private val callLength = 7.seconds + private var acceptUUID: String? = null + + @SubscribeEvent + fun onKeyPress(event: LorenzKeyPressEvent) { + if (config.acceptHotkey == Keyboard.KEY_NONE || config.acceptHotkey != event.keyCode) return + acceptUUID?.let { + HypixelCommands.callback(acceptUUID!!) + acceptUUID = null + } + } + + @SubscribeEvent + fun onConfigLoad(event: ConfigLoadEvent) { + val soundProperty = config.hoppityCallSound + ConditionalUtils.onToggle(soundProperty) { + warningSound = SoundUtils.createSound(soundProperty.get(), 1f) + } + nextWarningTime = null + finalWarningTime = null + } + + @SubscribeEvent(priority = EventPriority.HIGHEST) + fun onChat(event: LorenzChatEvent) { + if (callRingPattern.matches(event.message) && acceptUUID == null) readPickupUuid(event) + if (!isEnabled()) return + if (initHoppityCallPattern.matches(event.message)) startWarningUser() + if (pickupHoppityCallPattern.matches(event.message)) stopWarningUser() + } + + @SubscribeEvent + fun onTick(event: SecondPassedEvent) { + if (!isEnabled()) return + if (!activeWarning) return + if (nextWarningTime == null || finalWarningTime == null) return + val currentTime = Instant.now() + if (currentTime.isAfter(nextWarningTime)) { + SoundUtils.repeatSound(100, 10, warningSound) + nextWarningTime = currentTime.plusMillis(100) + } + if (currentTime >= finalWarningTime) stopWarningUser() + } + + @SubscribeEvent + fun onRender(event: GuiRenderEvent.GuiOverlayRenderEvent) { + if (!isEnabled() || !activeWarning) return + val minecraft = Minecraft.getMinecraft() + // Calculate a fluctuating alpha value based on the sine of time, for a smooth oscillation + val randomizationAlphaDouble = ((2 + sin(Instant.now().toEpochMilli().toDouble() / 1000)) * 255 / 4) + // Ensure the alpha value is an integer and within the valid range (0-255) + val randomizationAlphaInt = randomizationAlphaDouble.toInt().coerceIn(0..255) + // Shift the alpha value 24 bits to the left to position it in the color's alpha channel. + val shiftedRandomAlpha = randomizationAlphaInt shl 24 + Gui.drawRect( + 0, + 0, + minecraft.displayWidth, + minecraft.displayHeight, + // Apply the shifted alpha and combine it with the RGB components of flashColor. + shiftedRandomAlpha or (config.flashColor.toChromaColorInt() and 0xFFFFFF), + ) + GlStateManager.color(1F, 1F, 1F, 1F) + } + + private fun readPickupUuid(event: LorenzChatEvent) { + val siblings = event.chatComponent.siblings.takeIf { it.size >= 3 } ?: return + val clickEvent = siblings[2]?.chatStyle?.chatClickEvent ?: return + if (clickEvent.action.name.lowercase() != "run_command" || !clickEvent.value.lowercase().startsWith("/cb")) return + acceptUUID = clickEvent.value.lowercase().replace("/cb ", "").takeIf { it.isValidUuid() } + if (acceptUUID != null) DelayedRun.runDelayed(12.seconds) { acceptUUID = null } + } + + private fun startWarningUser() { + if (activeWarning) return + activeWarning = true + SoundUtils.repeatSound(100, 10, warningSound) + val currentTime = Instant.now() + nextWarningTime = currentTime.plusMillis(100) + finalWarningTime = finalWarningTime ?: currentTime.plusMillis(callLength.inWholeMilliseconds) + } + + private fun stopWarningUser() { + activeWarning = false + finalWarningTime = null + nextWarningTime = null + } + + private fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled +} -- cgit