From d725572d7a3a41d0c7a588f16f6c9b6bf6aa9ad8 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal002@users.noreply.github.com> Date: Sat, 2 Sep 2023 09:18:48 +0200 Subject: Following Line (#428) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt | 2 + .../skyhanni/config/features/MiscConfig.java | 42 +++++++ .../features/cosmetics/CosmeticFollowingLine.kt | 129 +++++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 src/main/java/at/hannibal2/skyhanni/features/cosmetics/CosmeticFollowingLine.kt (limited to 'src') 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 @@ -807,6 +807,48 @@ public class MiscConfig { public boolean crystalHollowsNamesInCore = false; } + @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 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() + private var latestLocations = mapOf() + + 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) + } + } + } +} -- cgit