Skip to content

Commit

Permalink
Feature: Hoppity Call Warning (#2272)
Browse files Browse the repository at this point in the history
Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com>
  • Loading branch information
DavidArthurCole and hannibal002 authored Sep 8, 2024
1 parent 9352ace commit 3db3627
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package at.hannibal2.skyhanni.config.features.event.hoppity;

import at.hannibal2.skyhanni.config.FeatureToggle;
import at.hannibal2.skyhanni.utils.OSUtils;
import com.google.gson.annotations.Expose;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorButton;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorColour;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorKeybind;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorText;
import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
import io.github.notenoughupdates.moulconfig.observer.Property;
import org.lwjgl.input.Keyboard;

public class HoppityCallWarningConfig {

@Expose
@ConfigOption(name = "Hoppity Call Warning", desc = "Warn when hoppity is calling you.")
@ConfigEditorBoolean
@FeatureToggle
public boolean enabled = true;

@Expose
@ConfigOption(
name = "Accept Call Hotkey",
desc = "Accept the call from hoppity by pressing this keybind."
)
@ConfigEditorKeybind(defaultKey = Keyboard.KEY_NONE)
public int acceptHotkey = Keyboard.KEY_NONE;

@Expose
@ConfigOption(name = "Warning Sound", desc = "The sound that plays when hoppity calls.\n" +
"§eYou can use custom sounds, put it in the §bskyhanni/sounds §efolder in your resource pack.\n" +
"§eThen write §bskyhanni:yourfilename\n" +
"§cMust be a .ogg file")
@ConfigEditorText
public Property<String> hoppityCallSound = Property.of("note.pling");

@Expose
@ConfigOption(name = "Flash Color", desc = "Color of the screen when flashing")
@ConfigEditorColour
public String flashColor = "0:127:0:238:255";

@ConfigOption(name = "Sounds", desc = "Click to open the list of available sounds.")
@ConfigEditorButton(buttonText = "OPEN")
public Runnable sounds = () -> OSUtils.openBrowser("https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/mapping-and-modding-tutorials/2213619-1-8-all-playsound-sound-arguments");
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@

public class HoppityEggsConfig {

@Expose
@ConfigOption(name = "Hoppity Abiphone Calls", desc = "")
@Accordion
public HoppityCallWarningConfig hoppityCallWarning = new HoppityCallWarningConfig();

@Expose
@ConfigOption(name = "Event Summary", desc = "")
@Accordion
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
4 changes: 4 additions & 0 deletions src/main/java/at/hannibal2/skyhanni/utils/HypixelCommands.kt
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ object HypixelCommands {
send("chatprompt $prompt")
}

fun callback(uuid: String) {
send("cb $uuid")
}

private fun send(command: String) {
@Suppress("DEPRECATION")
// TODO rename function
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/at/hannibal2/skyhanni/utils/StringUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -561,4 +561,13 @@ object StringUtils {
fun String.width(): Int = Minecraft.getMinecraft().fontRendererObj.getStringWidth(this)

fun String.lastColorCode(): String? = minecraftColorCodesPattern.findAll(this).lastOrNull()

fun String.isValidUuid(): Boolean {
return try {
UUID.fromString(this)
true
} catch (e: IllegalArgumentException) {
false
}
}
}

0 comments on commit 3db3627

Please sign in to comment.