aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/combat/CombatConfig.java5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/combat/FlareConfig.java119
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/combat/FlareDisplay.kt199
4 files changed, 325 insertions, 0 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
index 54a69f87b..3af43afb0 100644
--- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
+++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
@@ -90,6 +90,7 @@ import at.hannibal2.skyhanni.features.chat.playerchat.PlayerChatModifier
import at.hannibal2.skyhanni.features.chroma.ChromaManager
import at.hannibal2.skyhanni.features.combat.BestiaryData
import at.hannibal2.skyhanni.features.combat.FerocityDisplay
+import at.hannibal2.skyhanni.features.combat.FlareDisplay
import at.hannibal2.skyhanni.features.combat.HideDamageSplash
import at.hannibal2.skyhanni.features.combat.damageindicator.DamageIndicatorManager
import at.hannibal2.skyhanni.features.combat.endernodetracker.EnderNodeTracker
@@ -943,6 +944,7 @@ class SkyHanniMod {
loadModule(ColdOverlay())
loadModule(QuiverDisplay())
loadModule(QuiverWarning())
+ loadModule(FlareDisplay)
init()
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/combat/CombatConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/combat/CombatConfig.java
index bf8e795d7..1c1ae14ae 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/combat/CombatConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/combat/CombatConfig.java
@@ -50,6 +50,11 @@ public class CombatConfig {
public FerocityDisplayConfig ferocityDisplay = new FerocityDisplayConfig();
@Expose
+ @ConfigOption(name = "Flare", desc = "")
+ @Accordion
+ public FlareConfig flare = new FlareConfig();
+
+ @Expose
@ConfigOption(name = "Hide Damage Splash", desc = "Hide all damage splashes anywhere in SkyBlock.")
@ConfigEditorBoolean
@FeatureToggle
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/combat/FlareConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/combat/FlareConfig.java
new file mode 100644
index 000000000..b685f2942
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/combat/FlareConfig.java
@@ -0,0 +1,119 @@
+package at.hannibal2.skyhanni.config.features.combat;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import at.hannibal2.skyhanni.config.core.config.Position;
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorColour;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDropdown;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigLink;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+
+public class FlareConfig {
+
+ @Expose
+ @ConfigOption(name = "Enable", desc = "Show current active flares.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean enabled = false;
+
+ @Expose
+ @ConfigOption(name = "Alert Type", desc = "What type of alert should be sent when a flare is about to expire.")
+ @ConfigEditorDropdown
+ public AlertType alertType = AlertType.CHAT;
+
+ public enum AlertType {
+ NONE("No alert"),
+ CHAT("Chat"),
+ TITLE("Title"),
+ CHAT_TITLE("Chat & Title"),
+ ;
+
+ private final String str;
+
+ AlertType(String str) {
+ this.str = str;
+ }
+
+ @Override
+ public String toString() {
+ return str;
+ }
+ }
+
+ @Expose
+ @ConfigOption(name = "Display Type", desc = "Where to show the timer, as GUI element or in the world")
+ @ConfigEditorDropdown
+ public DisplayType displayType = DisplayType.GUI;
+
+ public enum DisplayType {
+ GUI("GUI Element"),
+ WORLD("In World"),
+ BOTH("Both"),
+ ;
+
+ private final String str;
+
+ DisplayType(String str) {
+ this.str = str;
+ }
+
+ @Override
+ public String toString() {
+ return str;
+ }
+ }
+
+ @Expose
+ @ConfigOption(name = "Show Effective Area", desc = "Show the effective area of the Flare.")
+ @ConfigEditorDropdown
+ public OutlineType outlineType = OutlineType.NONE;
+
+ public enum OutlineType {
+ NONE("No Outline"),
+ FILLED("Filled"),
+ WIREFRAME("Wireframe"),
+ CIRCLE("Circle")
+ ;
+
+ private final String str;
+
+ OutlineType(String str) {
+ this.str = str;
+ }
+
+ @Override
+ public String toString() {
+ return str;
+ }
+ }
+
+ @Expose
+ @ConfigOption(name = "Warning Flare Color", desc = "Color for Warning Flare.")
+ @ConfigEditorColour
+ public String warningColor = "0:153:29:255:136";
+
+ @Expose
+ @ConfigOption(name = "Alert Flare Color", desc = "Color for Alert Flare.")
+ @ConfigEditorColour
+ public String alertColor = "0:153:0:159:137";
+
+ @Expose
+ @ConfigOption(name = "SOS Flare Color", desc = "Color for SOS Flare.")
+ @ConfigEditorColour
+ public String sosColor = "0:153:159:0:5";
+
+ @Expose
+ @ConfigLink(owner = FlareConfig.class, field = "enabled")
+ public Position position = new Position(150, 200, false, true);
+
+ @Expose
+ @ConfigOption(name = "Show Buff", desc = "Show the mana regen buff next to the flare name.")
+ @ConfigEditorBoolean
+ public boolean showManaBuff = false;
+
+ @Expose
+ @ConfigOption(name = "Hide particles", desc = "Hide flame particles spawning around the flare.")
+ @ConfigEditorBoolean
+ public boolean hideParticles = false;
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/FlareDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/FlareDisplay.kt
new file mode 100644
index 000000000..4dcadb3a5
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/combat/FlareDisplay.kt
@@ -0,0 +1,199 @@
+package at.hannibal2.skyhanni.features.combat
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.config.features.combat.FlareConfig
+import at.hannibal2.skyhanni.events.GuiRenderEvent
+import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent
+import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent
+import at.hannibal2.skyhanni.events.ReceiveParticleEvent
+import at.hannibal2.skyhanni.events.SecondPassedEvent
+import at.hannibal2.skyhanni.utils.ChatUtils
+import at.hannibal2.skyhanni.utils.ColorUtils.toChromaColor
+import at.hannibal2.skyhanni.utils.EntityUtils
+import at.hannibal2.skyhanni.utils.EntityUtils.canBeSeen
+import at.hannibal2.skyhanni.utils.EntityUtils.hasSkullTexture
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzVec
+import at.hannibal2.skyhanni.utils.RenderUtils
+import at.hannibal2.skyhanni.utils.RenderUtils.drawDynamicText
+import at.hannibal2.skyhanni.utils.RenderUtils.drawSphereInWorld
+import at.hannibal2.skyhanni.utils.RenderUtils.drawSphereWireframeInWorld
+import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables
+import at.hannibal2.skyhanni.utils.TimeUtils.format
+import at.hannibal2.skyhanni.utils.TimeUtils.ticks
+import at.hannibal2.skyhanni.utils.getLorenzVec
+import at.hannibal2.skyhanni.utils.renderables.Renderable
+import net.minecraft.entity.item.EntityArmorStand
+import net.minecraft.util.EnumParticleTypes
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.minutes
+import kotlin.time.Duration.Companion.seconds
+
+object FlareDisplay {
+
+ private val config get() = SkyHanniMod.feature.combat.flare
+ private var display = emptyList<Renderable>()
+ private var flares = mutableListOf<Flare>()
+
+ class Flare(val type: FlareType, val entity: EntityArmorStand, val location: LorenzVec = entity.getLorenzVec())
+
+ private val MAX_FLARE_TIME = 3.minutes
+
+ private val flareSkins = mapOf(
+ "ewogICJ0aW1lc3RhbXAiIDogMTY0NjY4NzMwNjIyMywKICAicHJvZmlsZUlkIiA6ICI0MWQzYWJjMmQ3NDk0MDBjOTA5MGQ1NDM0ZDAzODMxYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZWdha2xvb24iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMjJlMmJmNmMxZWMzMzAyNDc5MjdiYTYzNDc5ZTU4NzJhYzY2YjA2OTAzYzg2YzgyYjUyZGFjOWYxYzk3MTQ1OCIKICAgIH0KICB9Cn0="
+ to FlareType.WARNING,
+ "ewogICJ0aW1lc3RhbXAiIDogMTY0NjY4NzMyNjQzMiwKICAicHJvZmlsZUlkIiA6ICI0MWQzYWJjMmQ3NDk0MDBjOTA5MGQ1NDM0ZDAzODMxYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZWdha2xvb24iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWQyYmY5ODY0NzIwZDg3ZmQwNmI4NGVmYTgwYjc5NWM0OGVkNTM5YjE2NTIzYzNiMWYxOTkwYjQwYzAwM2Y2YiIKICAgIH0KICB9Cn0="
+ to FlareType.ALERT,
+ "ewogICJ0aW1lc3RhbXAiIDogMTY0NjY4NzM0NzQ4OSwKICAicHJvZmlsZUlkIiA6ICI0MWQzYWJjMmQ3NDk0MDBjOTA5MGQ1NDM0ZDAzODMxYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZWdha2xvb24iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzAwNjJjYzk4ZWJkYTcyYTZhNGI4OTc4M2FkY2VmMjgxNWI0ODNhMDFkNzNlYTg3YjNkZjc2MDcyYTg5ZDEzYiIKICAgIH0KICB9Cn0="
+ to FlareType.SOS
+ )
+
+ @SubscribeEvent
+ fun onRenderOverlay(event: GuiRenderEvent.GuiOverlayRenderEvent) {
+ if (!isEnabled()) return
+ if (config.displayType == FlareConfig.DisplayType.WORLD) return
+ config.position.renderRenderables(display, posLabel = "Flare Timer")
+ }
+
+ @SubscribeEvent
+ fun onRenderWorld(event: LorenzRenderWorldEvent) {
+ if (!isEnabled()) return
+ if (config.displayType == FlareConfig.DisplayType.GUI) return
+
+ for (flare in flares) {
+ val location = flare.location.add(-0.5, 0.0, -0.5)
+ val name = flare.type.displayName
+ val time = "§b${getRemainingTime(flare).format()}"
+ event.drawDynamicText(location, name, 1.5, ignoreBlocks = false)
+ event.drawDynamicText(location, time, 1.5, yOff = 10f, ignoreBlocks = false)
+ }
+ }
+
+ @SubscribeEvent
+ fun onSecondsPassed(event: SecondPassedEvent) {
+ if (!isEnabled()) return
+ flares.removeIf { !it.entity.isEntityAlive }
+ for (entity in EntityUtils.getAllEntities().filterIsInstance<EntityArmorStand>()) {
+ if (!entity.canBeSeen()) continue
+ if (entity.ticksExisted.ticks > MAX_FLARE_TIME) continue
+ if (isAlreadyKnownFlare(entity)) continue
+ getFlareTypeForTexuture(entity)?.let {
+ flares.add(Flare(it, entity))
+ }
+ }
+ var newDisplay: List<Renderable>? = null
+ for (type in FlareType.entries) {
+ val flare = getFlareForType(type) ?: continue
+ val remainingTime = getRemainingTime(flare)
+
+ val name = type.displayName
+ if (newDisplay == null) {
+ newDisplay = buildList {
+ add(Renderable.string("$name: §b${remainingTime.format()}"))
+ if (config.showManaBuff) {
+ type.manaBuff?.let {
+ add(Renderable.string(" §b$it §7mana regen"))
+ }
+ }
+ }
+ }
+ if (remainingTime > 5.seconds) continue
+ val message = "$name §eexpires in: §b${remainingTime.inWholeSeconds}s"
+ when (config.alertType) {
+ FlareConfig.AlertType.CHAT -> {
+ ChatUtils.chat(message)
+ }
+
+ FlareConfig.AlertType.TITLE -> {
+ LorenzUtils.sendTitle(message, 1.seconds)
+ }
+
+ FlareConfig.AlertType.CHAT_TITLE -> {
+ ChatUtils.chat(message)
+ LorenzUtils.sendTitle(message, 1.seconds)
+ }
+
+ else -> {}
+ }
+ }
+ display = newDisplay ?: emptyList()
+ }
+
+ private fun getRemainingTime(flare: Flare): Duration {
+ val entity = flare.entity
+ val aliveTime = entity.ticksExisted.ticks
+ val remainingTime = (MAX_FLARE_TIME - aliveTime)
+ return remainingTime
+ }
+
+ private fun getFlareForType(type: FlareType): Flare? = flares.firstOrNull { it.type == type }
+
+ private fun getFlareTypeForTexuture(entity: EntityArmorStand): FlareType? =
+ flareSkins.entries.firstOrNull { entity.hasSkullTexture(it.key) }?.value
+
+ private fun isAlreadyKnownFlare(entity: EntityArmorStand): Boolean =
+ flares.any { it.entity.entityId == entity.entityId }
+
+ @SubscribeEvent
+ fun onWorldChange(event: LorenzWorldChangeEvent) {
+ flares.clear()
+ display = emptyList()
+ }
+
+ @SubscribeEvent
+ fun onRender(event: LorenzRenderWorldEvent) {
+ if (!isEnabled()) return
+ if (config.outlineType == FlareConfig.OutlineType.NONE) return
+
+ for (flare in flares) {
+ val entity = flare.entity
+ val location = flare.location
+
+ val color = when (flare.type) {
+ FlareType.WARNING -> config.warningColor
+ FlareType.ALERT -> config.alertColor
+ FlareType.SOS -> config.sosColor
+ }.toChromaColor()
+
+ when (config.outlineType) {
+ FlareConfig.OutlineType.FILLED -> {
+ event.drawSphereInWorld(color, location, 40f)
+ }
+
+ FlareConfig.OutlineType.WIREFRAME -> {
+ event.drawSphereWireframeInWorld(color, location, 40f)
+ }
+
+ FlareConfig.OutlineType.CIRCLE -> {
+ RenderUtils.drawCircle(entity, event.partialTicks, 40.0, color)
+ }
+
+ else -> {}
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onReceiveParticle(event: ReceiveParticleEvent) {
+ if (!isEnabled()) return
+ if (!config.hideParticles) return
+
+ val location = event.location
+ val distance = flares.minOfOrNull { it.location.distance(location) } ?: return
+ if (distance < 2.5) {
+ if (event.type == EnumParticleTypes.FLAME) {
+ event.cancel()
+ }
+ }
+ }
+
+ enum class FlareType(val displayName: String, val manaBuff: String?) {
+ SOS("§5SOS Flare", "+125%"),
+ ALERT("§9Alert Flare", "+50%"),
+ WARNING("§aWarning Flare", null),
+ ;
+ }
+
+ private fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled
+}