Skip to content

Commit

Permalink
Feature: Totem Of Corruption Overlay & Effective Area (#1139)
Browse files Browse the repository at this point in the history
Co-authored-by: hannibal2 <24389977+hannibal002@users.noreply.github.com>
  • Loading branch information
j10a1n15 and hannibal002 authored Mar 23, 2024
1 parent 54aba6c commit 44014aa
Show file tree
Hide file tree
Showing 5 changed files with 284 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ import at.hannibal2.skyhanni.features.misc.ServerRestartTitle
import at.hannibal2.skyhanni.features.misc.SkyBlockKickDuration
import at.hannibal2.skyhanni.features.misc.SuperpairsClicksAlert
import at.hannibal2.skyhanni.features.misc.TimeFeatures
import at.hannibal2.skyhanni.features.fishing.TotemOfCorruption
import at.hannibal2.skyhanni.features.misc.TpsCounter
import at.hannibal2.skyhanni.features.misc.compacttablist.AdvancedPlayerList
import at.hannibal2.skyhanni.features.misc.compacttablist.TabListReader
Expand Down Expand Up @@ -785,6 +786,7 @@ class SkyHanniMod {
loadModule(PresentWaypoints())
loadModule(MiningEventTracker())
loadModule(JyrreTimer())
loadModule(TotemOfCorruption())
loadModule(NewYearCakeReminder())
loadModule(SulphurSkitterBox())
loadModule(HighlightInquisitors())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ public class FishingConfig {
@Accordion
public FishingProfitTrackerConfig fishingProfitTracker = new FishingProfitTrackerConfig();

@Expose
@ConfigOption(name = "Totem of Corruption", desc = "")
@Accordion
public TotemOfCorruptionConfig totemOfCorruption = new TotemOfCorruptionConfig();

@Expose
@ConfigOption(name = "Sea Creature Tracker", desc = "")
@Accordion
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package at.hannibal2.skyhanni.config.features.fishing;

import at.hannibal2.skyhanni.config.FeatureToggle;
import at.hannibal2.skyhanni.config.core.config.Position;
import com.google.gson.annotations.Expose;
import io.github.moulberry.moulconfig.annotations.ConfigEditorBoolean;
import io.github.moulberry.moulconfig.annotations.ConfigEditorColour;
import io.github.moulberry.moulconfig.annotations.ConfigEditorSlider;
import io.github.moulberry.moulconfig.annotations.ConfigOption;

public class TotemOfCorruptionConfig {

@Expose
@ConfigOption(name = "Show Overlay", desc = "Show the Totem of Corruption overlay." +
"\nShows the totem, in which effective area you are in, with the longest time left." +
"\n§cThis needs to be enabled for the other options to work.")
@ConfigEditorBoolean
@FeatureToggle
public boolean showOverlay = true;

@Expose
@ConfigOption(name = "Distance Threshold", desc = "The minimum distance to the Totem of Corruption for the overlay." +
"\nThe effective distance of the totem is 16." +
"\n§cLimited by how far you can see the nametags.")
@ConfigEditorSlider(minValue = 0, maxValue = 100, minStep = 1)
public int distanceThreshold = 16;

@Expose
@ConfigOption(name = "Hide Particles", desc = "Hide the particles of the Totem of Corruption.")
@ConfigEditorBoolean
public boolean hideParticles = true;

@Expose
@ConfigOption(name = "Show Effective Area", desc = "Show the effective area (16 blocks) of the Totem of Corruption.")
@ConfigEditorBoolean
public boolean showEffectiveArea = true;

@Expose
@ConfigOption(name = "Color of the area", desc = "The color of the area of the Totem of Corruption.")
@ConfigEditorColour
public String color = "0:153:18:159:85";

@Expose
@ConfigOption(name = "Warn when about to expire", desc = "Select the time in seconds when the totem is about to expire to warn you." +
"\nSelect 0 to disable.")
@ConfigEditorSlider(minValue = 0, maxValue = 60, minStep = 1)
public int warnWhenAboutToExpire = 5;

@Expose
public Position position = new Position(50, 20, false, true);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package at.hannibal2.skyhanni.features.fishing

import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.events.GuiRenderEvent
import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent
import at.hannibal2.skyhanni.events.LorenzTickEvent
import at.hannibal2.skyhanni.events.ReceiveParticleEvent
import at.hannibal2.skyhanni.utils.ColorUtils.toChromaColor
import at.hannibal2.skyhanni.utils.EntityUtils
import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.LorenzUtils.sendTitle
import at.hannibal2.skyhanni.utils.LorenzVec
import at.hannibal2.skyhanni.utils.RenderUtils.drawSphereInWorld
import at.hannibal2.skyhanni.utils.RenderUtils.renderStrings
import at.hannibal2.skyhanni.utils.SoundUtils.playPlingSound
import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher
import at.hannibal2.skyhanni.utils.StringUtils.matches
import at.hannibal2.skyhanni.utils.TimeUnit
import at.hannibal2.skyhanni.utils.TimeUtils.format
import at.hannibal2.skyhanni.utils.getLorenzVec
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
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.seconds

class TotemOfCorruption {

private val config get() = SkyHanniMod.feature.fishing.totemOfCorruption

private var display = emptyList<String>()
private var totems: List<Totem> = emptyList()

private val group = RepoPattern.group("fishing.totemofcorruption")
private val totemNamePattern by group.pattern(
"totemname",
"§5§lTotem of Corruption"
)
private val timeRemainingPattern by group.pattern(
"timeremaining",
"§7Remaining: §e(?:(?<min>\\d+)m )?(?<sec>\\d+)s"
)
private val ownerPattern by group.pattern(
"owner",
"§7Owner: §e(?<owner>.+)"
)

@SubscribeEvent
fun onRender(event: GuiRenderEvent.GuiOverlayRenderEvent) {
if (!isOverlayEnabled() || display.isEmpty()) return
config.position.renderStrings(display, posLabel = "Totem of Corruption")
}

@SubscribeEvent
fun onTick(event: LorenzTickEvent) {
if (!event.repeatSeconds(2)) return
if (!isOverlayEnabled()) return

totems = getTotems()
display = createDisplay()
}

@SubscribeEvent
fun onChatPacket(event: ReceiveParticleEvent) {
if (!isHideParticlesEnabled()) return

for (totem in totems) {
if (event.type == EnumParticleTypes.SPELL_WITCH && event.speed == 0.0f) {
if (totem.location.distance(event.location) < 4.0) {
event.isCanceled = true
}
}
}
}

@SubscribeEvent
fun onRenderWorld(event: LorenzRenderWorldEvent) {
if (!isEffectiveAreaEnabled()) return

val color = config.color.toChromaColor()
for (totem in totems) {
// The center of the totem is the upper part
event.drawSphereInWorld(color, totem.location.add(y = 1), 16f)
}
}

private fun getTimeRemaining(totem: EntityArmorStand): Duration? =
EntityUtils.getEntitiesNearby<EntityArmorStand>(totem.getLorenzVec(), 2.0)
.firstNotNullOfOrNull { entity ->
timeRemainingPattern.matchMatcher(entity.name) {
val minutes = group("min")?.toIntOrNull() ?: 0
val seconds = group("sec")?.toInt() ?: 0
(minutes * 60 + seconds).seconds
}
}

private fun getOwner(totem: EntityArmorStand): String? =
EntityUtils.getEntitiesNearby<EntityArmorStand>(totem.getLorenzVec(), 2.0)
.firstNotNullOfOrNull { entity ->
ownerPattern.matchMatcher(entity.name) {
group("owner")
}
}


private fun createDisplay() = buildList {
val totem = getTotemToShow() ?: return@buildList
add("§5§lTotem of Corruption")
add("§7Remaining: §e${totem.timeRemaining.format(TimeUnit.MINUTE)}")
add("§7Owner: §e${totem.ownerName}")
}

private fun getTotemToShow(): Totem? = totems
.filter { it.distance < config.distanceThreshold }
.maxByOrNull { it.timeRemaining }

private fun getTotems(): List<Totem> = EntityUtils.getEntitiesNextToPlayer<EntityArmorStand>(100.0)
.filter { totemNamePattern.matches(it.name) }.toList()
.mapNotNull { totem ->
val timeRemaining = getTimeRemaining(totem) ?: return@mapNotNull null
val owner = getOwner(totem) ?: return@mapNotNull null

val timeToWarn = config.warnWhenAboutToExpire.seconds
if (timeToWarn > 0.seconds && timeRemaining == timeToWarn) {
playPlingSound()
sendTitle("§c§lTotem of Corruption §eabout to expire!", 5.seconds)
}
Totem(totem.getLorenzVec(), timeRemaining, owner)
}

private fun isOverlayEnabled() = LorenzUtils.inSkyBlock && config.showOverlay
private fun isHideParticlesEnabled() = LorenzUtils.inSkyBlock && config.hideParticles
private fun isEffectiveAreaEnabled() = LorenzUtils.inSkyBlock && config.showEffectiveArea
}

class Totem(
val location: LorenzVec,
val timeRemaining: Duration,
val ownerName: String,
val distance: Double = location.distanceToPlayer()
)
83 changes: 83 additions & 0 deletions src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,89 @@ object RenderUtils {
GlStateManager.popMatrix()
}

fun LorenzRenderWorldEvent.drawSphereInWorld(
color: Color,
location: LorenzVec,
radius: Float,
) {
drawSphereInWorld(color, location.x, location.y, location.z, radius)
}

fun LorenzRenderWorldEvent.drawSphereInWorld(
color: Color,
x: Double,
y: Double,
z: Double,
radius: Float,
) {
GlStateManager.pushMatrix()
GL11.glNormal3f(0.0f, 1.0f, 0.0f)

GlStateManager.enableDepth()
GlStateManager.enableBlend()
GlStateManager.depthFunc(GL11.GL_LEQUAL)
GlStateManager.disableCull()
GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0)
GlStateManager.enableAlpha()
GlStateManager.disableTexture2D()
color.bindColor()

var x1 = x
var y1 = y
var z1 = z
val renderViewEntity = Minecraft.getMinecraft().renderViewEntity
val viewX =
renderViewEntity.prevPosX + (renderViewEntity.posX - renderViewEntity.prevPosX) * partialTicks.toDouble()
val viewY =
renderViewEntity.prevPosY + (renderViewEntity.posY - renderViewEntity.prevPosY) * partialTicks.toDouble()
val viewZ =
renderViewEntity.prevPosZ + (renderViewEntity.posZ - renderViewEntity.prevPosZ) * partialTicks.toDouble()
x1 -= viewX
y1 -= viewY
z1 -= viewZ

val tessellator = Tessellator.getInstance()
val worldrenderer = tessellator.worldRenderer
worldrenderer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION)

val segments = 32

for (phi in 0 until segments) {
for (theta in 0 until segments * 2) {
val x2 = x1 + radius * sin(Math.PI * phi / segments) * cos(2.0 * Math.PI * theta / (segments * 2))
val y2 = y1 + radius * cos(Math.PI * phi / segments)
val z2 = z1 + radius * sin(Math.PI * phi / segments) * sin(2.0 * Math.PI * theta / (segments * 2))

val x3 = x1 + radius * sin(Math.PI * (phi + 1) / segments) * cos(2.0 * Math.PI * theta / (segments * 2))
val y3 = y1 + radius * cos(Math.PI * (phi + 1) / segments)
val z3 = z1 + radius * sin(Math.PI * (phi + 1) / segments) * sin(2.0 * Math.PI * theta / (segments * 2))

worldrenderer.pos(x2, y2, z2).endVertex()
worldrenderer.pos(x3, y3, z3).endVertex()

val x4 = x1 + radius * sin(Math.PI * (phi + 1) / segments) * cos(2.0 * Math.PI * (theta + 1) / (segments * 2))
val y4 = y1 + radius * cos(Math.PI * (phi + 1) / segments)
val z4 = z1 + radius * sin(Math.PI * (phi + 1) / segments) * sin(2.0 * Math.PI * (theta + 1) / (segments * 2))

val x5 = x1 + radius * sin(Math.PI * phi / segments) * cos(2.0 * Math.PI * (theta + 1) / (segments * 2))
val y5 = y1 + radius * cos(Math.PI * phi / segments)
val z5 = z1 + radius * sin(Math.PI * phi / segments) * sin(2.0 * Math.PI * (theta + 1) / (segments * 2))

worldrenderer.pos(x4, y4, z4).endVertex()
worldrenderer.pos(x5, y5, z5).endVertex()
}
}

tessellator.draw()

GlStateManager.enableCull()
GlStateManager.enableTexture2D()
GlStateManager.enableDepth()
GlStateManager.disableBlend()
GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f)
GlStateManager.popMatrix()
}

private fun Color.bindColor() =
GlStateManager.color(this.red / 255f, this.green / 255f, this.blue / 255f, this.alpha / 255f)

Expand Down

0 comments on commit 44014aa

Please sign in to comment.