1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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)
}
}
}
}
|