Skip to content

Commit

Permalink
Feature: Broodmother alerts/messages and damage indicator support (ha…
Browse files Browse the repository at this point in the history
…nnibal002#2325)

Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com>
  • Loading branch information
MTOnline69 and hannibal002 authored Sep 12, 2024
1 parent 996b704 commit 4e23db7
Show file tree
Hide file tree
Showing 10 changed files with 322 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package at.hannibal2.skyhanni.config.features.combat;

import at.hannibal2.skyhanni.config.FeatureToggle;
import at.hannibal2.skyhanni.config.features.combat.broodmother.BroodmotherConfig;
import at.hannibal2.skyhanni.config.features.combat.damageindicator.DamageIndicatorConfig;
import at.hannibal2.skyhanni.config.features.combat.ghostcounter.GhostCounterConfig;
import com.google.gson.annotations.Expose;
Expand Down Expand Up @@ -60,6 +61,11 @@ public class CombatConfig {
@Accordion
public FlareConfig flare = new FlareConfig();

@Expose
@ConfigOption(name = "Broodmother", desc = "")
@Accordion
public BroodmotherConfig broodmother = new BroodmotherConfig();

@Expose
@ConfigOption(name = "Hide Damage Splash", desc = "Hide all damage splashes anywhere in SkyBlock.")
@ConfigEditorBoolean
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package at.hannibal2.skyhanni.config.features.combat.broodmother;

import at.hannibal2.skyhanni.config.FeatureToggle;
import at.hannibal2.skyhanni.config.core.config.Position;
import at.hannibal2.skyhanni.features.combat.BroodmotherFeatures.StageEntry;
import com.google.gson.annotations.Expose;
import io.github.notenoughupdates.moulconfig.annotations.Accordion;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDraggableList;
import io.github.notenoughupdates.moulconfig.annotations.ConfigLink;
import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class BroodmotherConfig {

@Expose
@ConfigOption(name = "Countdown", desc = "Display a countdown until the Broodmother will spawn.\n" +
"§cCountdown will not show unless the time until spawn has been established, and may be off by a few seconds.")
@ConfigEditorBoolean
@FeatureToggle
public boolean countdown = true;

@Expose
@ConfigOption(name = "Spawn Alert", desc = "Send a chat message, title and sound when the Broodmother spawns.")
@ConfigEditorBoolean
@FeatureToggle
public boolean alertOnSpawn = false;

@Expose
@ConfigOption(name = "Alert Settings", desc = "")
@Accordion
public BroodmotherSpawnAlertConfig spawnAlert = new BroodmotherSpawnAlertConfig();

@Expose
@ConfigOption(name = "Imminent Warning", desc = "Warns you when the Broodmother is 1 minute away from spawning.")
@ConfigEditorBoolean
@FeatureToggle
public boolean imminentWarning = false;

@Expose
@ConfigOption(name = "Chat Messages", desc = "Send a chat message when the Broodmother enters these stages.\n" +
"§cThe 'Alive!' and 'Imminent' stages are overridden by the \"Spawn Alert\" and \"Imminent Warning\" features.")
@ConfigEditorDraggableList
public List<StageEntry> stages = new ArrayList<>(Arrays.asList(
StageEntry.SLAIN,
StageEntry.ALIVE
));

@Expose
@ConfigOption(name = "Stage on Server Join", desc = "Send a chat message with the Broodmother's current stage upon joining the Spider's Den.")
@ConfigEditorBoolean
@FeatureToggle
public boolean stageOnJoin = false;

@Expose
@ConfigOption(name = "Hide own kills", desc = "Disable the chat message for the §eSlain §rstage if at the Spider Mound.")
@ConfigEditorBoolean
@FeatureToggle
public boolean hideSlainWhenNearby = false;

@Expose
@ConfigLink(owner = BroodmotherConfig.class, field = "countdown")
public Position countdownPosition = new Position(10, 10, false, true);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package at.hannibal2.skyhanni.config.features.combat.broodmother;

import at.hannibal2.skyhanni.features.combat.BroodmotherFeatures;
import at.hannibal2.skyhanni.utils.OSUtils;
import com.google.gson.annotations.Expose;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorButton;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorText;
import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;

public class BroodmotherSpawnAlertConfig {

@Expose
@ConfigOption(name = "Alert Sound", desc = "The sound that plays for the alert.")
@ConfigEditorText
public String alertSound = "note.pling";

@Expose
@ConfigOption(name = "Pitch", desc = "The pitch of the alert sound.")
@ConfigEditorSlider(minValue = 0.5f, maxValue = 2.0f, minStep = 0.1f)
public float pitch = 1.0f;

@ConfigOption(name = "Test Sound", desc = "Test current sound settings.")
@ConfigEditorButton(buttonText = "Test")
public Runnable testSound = BroodmotherFeatures::playTestSound;

@Expose
@ConfigOption(name = "Repeat Sound", desc = "How many times the sound should be repeated.")
@ConfigEditorSlider(minValue = 1, maxValue = 20, minStep = 1)
public int repeatSound = 20;

@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");

@Expose
@ConfigOption(name = "Text", desc = "The text with color to be displayed as the title notification.")
@ConfigEditorText
public String text = "&4Broodmother has spawned!";

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.List;

import static at.hannibal2.skyhanni.config.features.combat.damageindicator.DamageIndicatorConfig.BossCategory.ARACHNE;
import static at.hannibal2.skyhanni.config.features.combat.damageindicator.DamageIndicatorConfig.BossCategory.BROODMOTHER;
import static at.hannibal2.skyhanni.config.features.combat.damageindicator.DamageIndicatorConfig.BossCategory.DIANA_MOBS;
import static at.hannibal2.skyhanni.config.features.combat.damageindicator.DamageIndicatorConfig.BossCategory.GARDEN_PESTS;
import static at.hannibal2.skyhanni.config.features.combat.damageindicator.DamageIndicatorConfig.BossCategory.INFERNO_DEMONLORD;
Expand Down Expand Up @@ -96,6 +97,7 @@ public String toString() {
DIANA_MOBS,
SEA_CREATURES,
ARACHNE,
BROODMOTHER,
THE_RIFT_BOSSES,
RIFTSTALKER_BLOODFIEND,
REINDRAKE,
Expand Down Expand Up @@ -129,6 +131,7 @@ public enum BossCategory implements HasLegacyId {
RIFTSTALKER_BLOODFIEND("§bRiftstalker Bloodfiend", 23),
REINDRAKE("§6Reindrake", 24),
GARDEN_PESTS("§aGarden Pests", 25),
BROODMOTHER("§bBroodmother")
;

private final String str;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ enum class TabWidget(
),
BROODMOTHER(
// language=RegExp
"Broodmother: (?:§.)*(?<time>.*)",
"Broodmother: (?:§.)*(?<stage>.*)",
),
EYES_PLACED(
// language=RegExp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package at.hannibal2.skyhanni.features.combat

import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.data.IslandType
import at.hannibal2.skyhanni.data.model.TabWidget
import at.hannibal2.skyhanni.events.GuiRenderEvent
import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent
import at.hannibal2.skyhanni.events.SecondPassedEvent
import at.hannibal2.skyhanni.events.WidgetUpdateEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.ChatUtils
import at.hannibal2.skyhanni.utils.HypixelCommands
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland
import at.hannibal2.skyhanni.utils.RenderUtils.renderString
import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.SoundUtils
import at.hannibal2.skyhanni.utils.SoundUtils.playSound
import at.hannibal2.skyhanni.utils.StringUtils
import at.hannibal2.skyhanni.utils.TimeUtils.format
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import kotlin.reflect.KMutableProperty0
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
import kotlin.time.DurationUnit

@SkyHanniModule
object BroodmotherFeatures {

enum class StageEntry(private val str: String, val minutes: Int) {
SLAIN("§eSlain", 10),
DORMANT("§eDormant", 9),
SOON("§6Soon", 6),
AWAKENING("§6Awakening", 3),
IMMINENT("§4Imminent", 1),
ALIVE("§4Alive!", 0);

override fun toString() = str
}

private val config get() = SkyHanniMod.feature.combat.broodmother
private val spawnAlertConfig get() = config.spawnAlert

private var lastStage: StageEntry? = null
private var currentStage: StageEntry? = null
private var broodmotherSpawnTime = SimpleTimeMark.farPast()
private var display = ""

@SubscribeEvent
fun onTabListUpdate(event: WidgetUpdateEvent) {
if (!event.isWidget(TabWidget.BROODMOTHER)) return
val newStage = event.widget.matchMatcherFirstLine { group("stage") } ?: ""
if (newStage.isNotEmpty()) {
lastStage = currentStage
currentStage = StageEntry.valueOf(newStage.replace("!", "").uppercase())
onStageUpdate()
}
}

private fun onStageUpdate() {
ChatUtils.debug("New Broodmother stage: $currentStage")

if (lastStage == null) {
if (onServerJoin()) return
}

// ignore Hypixel bug where the stage may temporarily revert to Imminent after the Broodmother's death
if (currentStage == StageEntry.IMMINENT && lastStage == StageEntry.ALIVE) return

if (currentStage == StageEntry.ALIVE) {
onBroodmotherSpawn()
return
}

val timeUntilSpawn = currentStage?.minutes?.minutes
broodmotherSpawnTime = SimpleTimeMark.now() + timeUntilSpawn!!

if (currentStage == StageEntry.IMMINENT && config.imminentWarning) {
playImminentWarning()
return
}

if (config.stages.contains(currentStage) && lastStage != null) {
if (currentStage == StageEntry.SLAIN) {
onBroodmotherSlain()
} else {
val pluralize = StringUtils.pluralize(timeUntilSpawn.toInt(DurationUnit.MINUTES), "minute")
ChatUtils.chat(
"Broodmother: $lastStage §e-> $currentStage§e. §b${timeUntilSpawn.inWholeMinutes} $pluralize §euntil it spawns!"
)
}
}
}

private fun onServerJoin(): Boolean {
// don't send if user has config enabled for either of the alive messages
// this is so that two messages aren't immediately sent upon joining a server
if (config.stageOnJoin && !(currentStage == StageEntry.ALIVE && isAliveMessageEnabled())) {
val pluralize = StringUtils.pluralize(currentStage?.minutes ?: 0, "minute")
var message = "The Broodmother's current stage in this server is ${currentStage.toString().replace("!", "")}§e."
if (currentStage?.minutes != 0) {
message += " It will spawn within §b${currentStage?.minutes} $pluralize§e."
}
ChatUtils.chat(message)
return true
} else {
return false
}
}

private fun onBroodmotherSpawn() {
broodmotherSpawnTime = SimpleTimeMark.farPast()
if (!isAliveMessageEnabled()) return
val feature: KMutableProperty0<*>
if (config.alertOnSpawn) {
feature = config::alertOnSpawn
val alertSound = SoundUtils.createSound(spawnAlertConfig.alertSound, spawnAlertConfig.pitch)
SoundUtils.repeatSound(100, spawnAlertConfig.repeatSound, alertSound)
LorenzUtils.sendTitle(spawnAlertConfig.text.replace("&", "§"), 3.seconds)
} else {
feature = config::stages
}
ChatUtils.clickToActionOrDisable(
"The Broodmother has spawned!",
feature,
actionName = "warp to the Top of the Nest",
action = { HypixelCommands.warp("nest") },
)
}

private fun playImminentWarning() {
SoundUtils.repeatSound(100, 2, SoundUtils.createSound("note.pling", 0.5f))
ChatUtils.chat("The Broodmother is §4Imminent§e! It will spawn in §b60 seconds§e!")
}

private fun onBroodmotherSlain() {
broodmotherSpawnTime = SimpleTimeMark.now() + 10.minutes
if (!(config.hideSlainWhenNearby && SpidersDenAPI.isAtTopOfNest())) {
ChatUtils.chat("The Broodmother was killed!")
}
}

@SubscribeEvent
fun onWorldChange(event: LorenzWorldChangeEvent) {
broodmotherSpawnTime = SimpleTimeMark.farPast()
lastStage = null
currentStage = null
display = ""
}

@SubscribeEvent
fun onRenderOverlay(event: GuiRenderEvent.GuiOverlayRenderEvent) {
if (!isCountdownEnabled()) return
if (display.isEmpty()) return

config.countdownPosition.renderString(display, posLabel = "Broodmother Countdown")
}

@SubscribeEvent
fun onSecondPassed(event: SecondPassedEvent) {
if (!isCountdownEnabled()) return

if (broodmotherSpawnTime.isFarPast()) {
if (lastStage != null) {
display = "§4Broodmother spawned!"
}
} else {
val countdown = broodmotherSpawnTime.timeUntil().format()
display = "§4Broodmother spawning in §b$countdown"
}
}

@JvmStatic
fun playTestSound() {
with(spawnAlertConfig) {
SoundUtils.createSound(alertSound, pitch).playSound()
}
}

private fun inSpidersDen() = IslandType.SPIDER_DEN.isInIsland()
private fun isCountdownEnabled() = inSpidersDen() && config.countdown
private fun isAliveMessageEnabled() = config.alertOnSpawn || config.stages.contains(StageEntry.ALIVE)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package at.hannibal2.skyhanni.features.combat

import at.hannibal2.skyhanni.features.gui.customscoreboard.CustomScoreboard
import at.hannibal2.skyhanni.features.gui.customscoreboard.ScoreboardPattern
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.RegexUtils.matches

@SkyHanniModule
object SpidersDenAPI {
private fun getSbLines(): List<String> = CustomScoreboard.activeLines
fun isAtTopOfNest(): Boolean = getSbLines().any { ScoreboardPattern.broodmotherPattern.matches(it) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ enum class BossType(
DUMMY("Dummy", Type.DUMMY),
ARACHNE_SMALL("§cSmall Arachne", Type.ARACHNE),
ARACHNE_BIG("§4Big Arachne", Type.ARACHNE),
BROODMOTHER("§cBroodmother", Type.BROODMOTHER),

// The Rift
LEECH_SUPREME("§cLeech Supreme", Type.THE_RIFT_BOSSES),
Expand All @@ -119,8 +120,6 @@ enum class BossType(
GARDEN_PEST_SLUG("§cSlug", Type.GARDEN_PESTS),
GARDEN_PEST_EARTHWORM("§cEarthworm", Type.GARDEN_PESTS),

// TODO arachne

// TODO Corleone
// TODO bal

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,11 @@ class MobFinder {
entity.hasMaxHealth(2_400_000, true) -> return EntityResult(bossType = BossType.SLAYER_SPIDER_4)
}
}
if (entity.hasNameTagWith(1, "[§7Lv12§8] §4Broodmother")) {
if (entity.hasMaxHealth(6000)) {
return EntityResult(bossType = BossType.BROODMOTHER)
}
}
checkArachne(entity as EntitySpider)?.let { return it }
return null
}
Expand Down
Loading

0 comments on commit 4e23db7

Please sign in to comment.