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
|
/*
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.features.diana
import kotlin.time.Duration.Companion.seconds
import net.minecraft.particle.ParticleTypes
import net.minecraft.sound.SoundEvents
import net.minecraft.util.math.Vec3d
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.ParticleSpawnEvent
import moe.nea.firmament.events.SoundReceiveEvent
import moe.nea.firmament.events.WorldKeyboardEvent
import moe.nea.firmament.events.WorldReadyEvent
import moe.nea.firmament.events.WorldRenderLastEvent
import moe.nea.firmament.events.subscription.SubscriptionOwner
import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.util.SBData
import moe.nea.firmament.util.TimeMark
import moe.nea.firmament.util.WarpUtil
import moe.nea.firmament.util.render.RenderInWorldContext
object AncestralSpadeSolver : SubscriptionOwner {
var lastDing = TimeMark.farPast()
private set
private val pitches = mutableListOf<Float>()
val particlePositions = mutableListOf<Vec3d>()
var nextGuess: Vec3d? = null
private set
private var lastTeleportAttempt = TimeMark.farPast()
fun isEnabled() =
DianaWaypoints.TConfig.ancestralSpadeSolver && SBData.skyblockLocation == "hub"
@Subscribe
fun onKeyBind(event: WorldKeyboardEvent) {
if (!isEnabled()) return
if (!event.matches(DianaWaypoints.TConfig.ancestralSpadeTeleport)) return
if (lastTeleportAttempt.passedTime() < 3.seconds) return
WarpUtil.teleportToNearestWarp("hub", nextGuess ?: return)
lastTeleportAttempt = TimeMark.now()
}
@Subscribe
fun onParticleSpawn(event: ParticleSpawnEvent) {
if (!isEnabled()) return
if (event.particleEffect != ParticleTypes.DRIPPING_LAVA) return
if (event.offset.x != 0.0F || event.offset.y != 0F || event.offset.z != 0F)
return
particlePositions.add(event.position)
if (particlePositions.size > 20) {
particlePositions.removeFirst()
}
}
@Subscribe
fun onPlaySound(event: SoundReceiveEvent) {
if (!isEnabled()) return
if (!SoundEvents.BLOCK_NOTE_BLOCK_HARP.matchesId(event.sound.value().id)) return
if (lastDing.passedTime() > 1.seconds) {
particlePositions.clear()
pitches.clear()
}
lastDing = TimeMark.now()
pitches.add(event.pitch)
if (pitches.size > 20) {
pitches.removeFirst()
}
if (particlePositions.size < 3) {
return
}
val averagePitchDelta =
if (pitches.isEmpty()) return
else pitches
.zipWithNext { a, b -> b - a }
.average()
val soundDistanceEstimate = (Math.E / averagePitchDelta) - particlePositions.first().distanceTo(event.position)
if (soundDistanceEstimate > 1000) {
return
}
val lastParticleDirection = particlePositions
.takeLast(3)
.let { (a, _, b) -> b.subtract(a) }
.normalize()
nextGuess = event.position.add(lastParticleDirection.multiply(soundDistanceEstimate))
}
@Subscribe
fun onWorldRender(event: WorldRenderLastEvent) {
if (!isEnabled()) return
RenderInWorldContext.renderInWorld(event) {
nextGuess?.let {
color(1f, 1f, 0f, 0.5f)
tinyBlock(it, 1f)
color(1f, 1f, 0f, 1f)
tracer(it, lineWidth = 3f)
}
if (particlePositions.size > 2 && lastDing.passedTime() < 10.seconds && nextGuess != null) {
color(0f, 1f, 0f, 0.7f)
line(particlePositions)
}
}
}
@Subscribe
fun onSwapWorld(event: WorldReadyEvent) {
nextGuess = null
particlePositions.clear()
pitches.clear()
lastDing = TimeMark.farPast()
}
override val delegateFeature: FirmamentFeature
get() = DianaWaypoints
}
|