aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java42
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/cosmetics/CosmeticFollowingLine.kt129
3 files changed, 173 insertions, 0 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
index bf56838ac..e508cdf0e 100644
--- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
+++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
@@ -20,6 +20,7 @@ import at.hannibal2.skyhanni.features.commands.PartyTransferCommand
import at.hannibal2.skyhanni.features.commands.SendCoordinatedCommand
import at.hannibal2.skyhanni.features.commands.WarpIsCommand
import at.hannibal2.skyhanni.features.commands.WikiCommand
+import at.hannibal2.skyhanni.features.cosmetics.CosmeticFollowingLine
import at.hannibal2.skyhanni.features.damageindicator.DamageIndicatorManager
import at.hannibal2.skyhanni.features.dungeon.*
import at.hannibal2.skyhanni.features.event.diana.*
@@ -386,6 +387,7 @@ class SkyHanniMod {
loadModule(PetExpTooltip())
loadModule(Translator())
loadModule(GardenPlotBorders())
+ loadModule(CosmeticFollowingLine())
init()
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java
index 9600eb699..9a4d7565a 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java
@@ -808,6 +808,48 @@ public class MiscConfig {
}
@Expose
+ @ConfigOption(name = "Cosmetic", desc = "")
+ @Accordion
+ public CosmeticConfig cosmeticConfig = new CosmeticConfig();
+
+ public static class CosmeticConfig {
+
+ @Expose
+ @ConfigOption(name = "Following Line", desc = "")
+ @Accordion
+ public FollowingLineConfig followingLineConfig = new FollowingLineConfig();
+
+ public static class FollowingLineConfig {
+
+ @Expose
+ @ConfigOption(name = "Enabled", desc = "Draw a colored line behind the player.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean enabled = false;
+
+ @Expose
+ @ConfigOption(name = "Line color", desc = "Color of the line.")
+ @ConfigEditorColour
+ public String lineColor = "0:255:255:255:255";
+
+ @Expose
+ @ConfigOption(name = "Time Alive", desc = "Time in seconds until the line fades out.")
+ @ConfigEditorSlider(minStep = 1, minValue = 1, maxValue = 30)
+ public int secondsAlive = 3;
+
+ @Expose
+ @ConfigOption(name = "Max Line Width", desc = "Max width of the line.")
+ @ConfigEditorSlider(minStep = 1, minValue = 1, maxValue = 10)
+ public int lineWidth = 4;
+
+ @Expose
+ @ConfigOption(name = "Behind Blocks", desc = "Show behind blocks.")
+ @ConfigEditorBoolean
+ public boolean behindBlocks = false;
+ }
+ }
+
+ @Expose
@ConfigOption(name = "Exp Bottles", desc = "Hides all the experience orbs lying on the ground.")
@ConfigEditorBoolean
@FeatureToggle
diff --git a/src/main/java/at/hannibal2/skyhanni/features/cosmetics/CosmeticFollowingLine.kt b/src/main/java/at/hannibal2/skyhanni/features/cosmetics/CosmeticFollowingLine.kt
new file mode 100644
index 000000000..1f84e9e7b
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/cosmetics/CosmeticFollowingLine.kt
@@ -0,0 +1,129 @@
+package at.hannibal2.skyhanni.features.cosmetics
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.LorenzTickEvent
+import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent
+import at.hannibal2.skyhanni.utils.LocationUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.editCopy
+import at.hannibal2.skyhanni.utils.LorenzUtils.toChromaColor
+import at.hannibal2.skyhanni.utils.LorenzVec
+import at.hannibal2.skyhanni.utils.RenderUtils.draw3DLine
+import at.hannibal2.skyhanni.utils.RenderUtils.exactLocation
+import at.hannibal2.skyhanni.utils.SimpleTimeMark
+import net.minecraft.client.Minecraft
+import net.minecraftforge.client.event.RenderWorldLastEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.awt.Color
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
+
+class CosmeticFollowingLine {
+ private val config get() = SkyHanniMod.feature.misc.cosmeticConfig.followingLineConfig
+
+ private var locations = mapOf<LorenzVec, LocationSpot>()
+ private var latestLocations = mapOf<LorenzVec, LocationSpot>()
+
+ class LocationSpot(val time: SimpleTimeMark, val onGround: Boolean)
+
+ @SubscribeEvent
+ fun onWorldChange(event: LorenzWorldChangeEvent) {
+ locations = emptyMap()
+ }
+
+ @SubscribeEvent
+ fun onRenderWorld(event: RenderWorldLastEvent) {
+ if (!LorenzUtils.inSkyBlock) return
+ if (!config.enabled) return
+
+ updateClose(event)
+
+ val firstPerson = Minecraft.getMinecraft().gameSettings.thirdPersonView == 0
+ val color = config.lineColor.toChromaColor()
+
+ renderClose(event, firstPerson, color)
+ renderFar(event, firstPerson, color)
+ }
+
+ private fun renderFar(
+ event: RenderWorldLastEvent,
+ firstPerson: Boolean,
+ color: Color
+ ) {
+ val last7 = locations.keys.toList().takeLast(7)
+ val last2 = locations.keys.toList().takeLast(2)
+
+ for ((a, b) in locations.keys.zipWithNext()) {
+ val locationSpot = locations[b]!!
+ if (firstPerson) {
+ if (!locationSpot.onGround) {
+ if (b in last7) {
+ // Do not render the line in the face, keep more distance while the line is in the air
+ continue
+ }
+ }
+ }
+ if (b in last2) {
+ if (locationSpot.time.passedSince() < 400.milliseconds) {
+ // Do not render the line directly next to the player, prevent laggy design
+ continue
+ }
+ }
+ event.draw3DLine(a, b, color, locationSpot.getWidth(), !config.behindBlocks)
+ }
+ }
+
+ private fun updateClose(event: RenderWorldLastEvent) {
+ val playerLocation = event.exactLocation(Minecraft.getMinecraft().thePlayer).add(0.0, 0.3, 0.0)
+
+ latestLocations = latestLocations.editCopy {
+ val locationSpot = LocationSpot(SimpleTimeMark.now(), Minecraft.getMinecraft().thePlayer.onGround)
+ this[playerLocation] = locationSpot
+ values.removeIf { it.time.passedSince() > 600.milliseconds }
+ }
+ }
+
+ private fun renderClose(event: RenderWorldLastEvent, firstPerson: Boolean, color: Color) {
+ if (firstPerson && latestLocations.any { !it.value.onGround }) return
+
+ for ((a, b) in latestLocations.keys.zipWithNext()) {
+ val locationSpot = latestLocations[b]!!
+ event.draw3DLine(a, b, color, locationSpot.getWidth(), !config.behindBlocks)
+ }
+ }
+
+ private fun LocationSpot.getWidth(): Int {
+ val millis = time.passedSince().inWholeMilliseconds
+ val percentage = millis.toDouble() / (config.secondsAlive * 1000.0)
+ val maxWidth = config.lineWidth
+ val lineWidth = 1 + maxWidth - percentage * maxWidth
+ return lineWidth.toInt().coerceAtLeast(1)
+ }
+
+ @SubscribeEvent
+ fun onTick(event: LorenzTickEvent) {
+ if (!LorenzUtils.inSkyBlock) return
+ if (!config.enabled) return
+
+ if (event.isMod(5)) {
+ locations = locations.editCopy { values.removeIf { it.time.passedSince() > config.secondsAlive.seconds } }
+
+ // Safety check to not cause lags
+ while (locations.size > 5_000) {
+ locations = locations.editCopy { remove(keys.first()) }
+ }
+ }
+
+ if (event.isMod(2)) {
+ val playerLocation = LocationUtils.playerLocation().add(0.0, 0.3, 0.0)
+
+ locations.keys.lastOrNull()?.let {
+ if (it.distance(playerLocation) < 0.1) return
+ }
+
+ locations = locations.editCopy {
+ this[playerLocation] = LocationSpot(SimpleTimeMark.now(), Minecraft.getMinecraft().thePlayer.onGround)
+ }
+ }
+ }
+}