Replace references to NEU with Firmament
This commit is contained in:
53
src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt
Normal file
53
src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt
Normal file
@@ -0,0 +1,53 @@
|
||||
package moe.nea.firmament.features
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.serializer
|
||||
import moe.nea.firmament.Firmament
|
||||
import moe.nea.firmament.features.fishing.FishingWarning
|
||||
import moe.nea.firmament.features.world.FairySouls
|
||||
import moe.nea.firmament.util.data.DataHolder
|
||||
|
||||
object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "features", ::Config) {
|
||||
@Serializable
|
||||
data class Config(
|
||||
val enabledFeatures: MutableMap<String, Boolean> = mutableMapOf()
|
||||
)
|
||||
|
||||
private val features = mutableMapOf<String, NEUFeature>()
|
||||
|
||||
private var hasAutoloaded = false
|
||||
|
||||
init {
|
||||
autoload()
|
||||
}
|
||||
|
||||
fun autoload() {
|
||||
synchronized(this) {
|
||||
if (hasAutoloaded) return
|
||||
loadFeature(FairySouls)
|
||||
loadFeature(FishingWarning)
|
||||
hasAutoloaded = true
|
||||
}
|
||||
}
|
||||
|
||||
fun loadFeature(feature: NEUFeature) {
|
||||
synchronized(features) {
|
||||
if (feature.identifier in features) {
|
||||
Firmament.logger.error("Double registering feature ${feature.identifier}. Ignoring second instance $feature")
|
||||
return
|
||||
}
|
||||
features[feature.identifier] = feature
|
||||
feature.onLoad()
|
||||
}
|
||||
}
|
||||
|
||||
fun isEnabled(identifier: String): Boolean? =
|
||||
data.enabledFeatures[identifier]
|
||||
|
||||
|
||||
fun setEnabled(identifier: String, value: Boolean) {
|
||||
data.enabledFeatures[identifier] = value
|
||||
markDirty()
|
||||
}
|
||||
|
||||
}
|
||||
18
src/main/kotlin/moe/nea/firmament/features/NEUFeature.kt
Normal file
18
src/main/kotlin/moe/nea/firmament/features/NEUFeature.kt
Normal file
@@ -0,0 +1,18 @@
|
||||
package moe.nea.firmament.features
|
||||
|
||||
import moe.nea.firmament.util.config.ManagedConfig
|
||||
|
||||
interface NEUFeature {
|
||||
val name: String
|
||||
val identifier: String
|
||||
val defaultEnabled: Boolean
|
||||
get() = true
|
||||
var isEnabled: Boolean
|
||||
get() = FeatureManager.isEnabled(identifier) ?: defaultEnabled
|
||||
set(value) {
|
||||
FeatureManager.setEnabled(identifier, value)
|
||||
}
|
||||
val config: ManagedConfig? get() = null
|
||||
fun onLoad()
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package moe.nea.firmament.features.fishing
|
||||
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.acos
|
||||
import kotlin.math.asin
|
||||
import kotlin.math.atan2
|
||||
import kotlin.math.sqrt
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import net.minecraft.entity.projectile.FishingBobberEntity
|
||||
import net.minecraft.particle.ParticleTypes
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import moe.nea.firmament.events.ParticleSpawnEvent
|
||||
import moe.nea.firmament.events.WorldReadyEvent
|
||||
import moe.nea.firmament.events.WorldRenderLastEvent
|
||||
import moe.nea.firmament.features.NEUFeature
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.TimeMark
|
||||
import moe.nea.firmament.util.config.ManagedConfig
|
||||
import moe.nea.firmament.util.render.RenderBlockContext.Companion.renderBlocks
|
||||
|
||||
object FishingWarning : NEUFeature {
|
||||
override val name: String
|
||||
get() = "Fishing Warning"
|
||||
override val identifier: String
|
||||
get() = "fishing-warning"
|
||||
|
||||
object TConfig : ManagedConfig("fishing-warning") {
|
||||
// Display a warning when you are about to hook a fish
|
||||
val displayWarning by toggle("display-warning") { false }
|
||||
val highlightWakeChain by toggle("highlight-wake-chain") { false }
|
||||
}
|
||||
|
||||
override val config: ManagedConfig get() = TConfig
|
||||
|
||||
|
||||
data class WakeChain(
|
||||
val delta: Vec3d,
|
||||
val momentum: Vec3d,
|
||||
val lastContinued: TimeMark,
|
||||
)
|
||||
|
||||
|
||||
val chains = mutableListOf<WakeChain>()
|
||||
|
||||
private fun areAnglesClose(a: Double, b: Double, tolerance: Double): Boolean {
|
||||
var dist = (a - b).absoluteValue
|
||||
if (180 < dist) dist = 360 - dist;
|
||||
return dist <= tolerance
|
||||
}
|
||||
|
||||
private fun calculateAngleFromOffsets(xOffset: Double, zOffset: Double): Double {
|
||||
// See also: Vanilla 1.8.9 Fishing particle code.
|
||||
var angleX = Math.toDegrees(acos(xOffset / 0.04))
|
||||
var angleZ = Math.toDegrees(asin(zOffset / 0.04))
|
||||
if (xOffset < 0) {
|
||||
// Old: angleZ = 180 - angleZ;
|
||||
angleZ = 180 - angleZ
|
||||
}
|
||||
if (zOffset < 0) {
|
||||
angleX = 360 - angleX
|
||||
}
|
||||
angleX %= 360.0
|
||||
angleZ %= 360.0
|
||||
if (angleX < 0) angleX += 360.0
|
||||
if (angleZ < 0) angleZ += 360.0
|
||||
var dist = angleX - angleZ
|
||||
if (dist < -180) dist += 360.0
|
||||
if (dist > 180) dist -= 360.0
|
||||
return angleZ + dist / 2
|
||||
}
|
||||
|
||||
private fun toDegrees(d: Double) = d * 180 / Math.PI
|
||||
|
||||
fun isHookPossible(hook: FishingBobberEntity, particlePos: Vec3d, angle1: Double, angle2: Double): Boolean {
|
||||
val dx = particlePos.x - hook.pos.x
|
||||
val dz = particlePos.z - hook.pos.z
|
||||
val dist = sqrt(dx * dx + dz * dz)
|
||||
|
||||
if (dist < 0.2) return true
|
||||
val tolerance = toDegrees(atan2(0.03125, dist)) * 1.5
|
||||
var angleToHook = toDegrees(atan2(dx, dz)) % 360
|
||||
if (angleToHook < 0) angleToHook += 360
|
||||
return areAnglesClose(angle1, angleToHook, tolerance) || areAnglesClose(angle2, angleToHook, tolerance)
|
||||
}
|
||||
|
||||
val recentParticles = mutableListOf<Pair<Vec3d, TimeMark>>()
|
||||
|
||||
private fun onParticleSpawn(event: ParticleSpawnEvent) {
|
||||
if (event.particleEffect.type != ParticleTypes.FISHING) return
|
||||
if (!(abs(event.offset.y - 0.01f) < 0.001f)) return
|
||||
val hook = MC.player?.fishHook ?: return
|
||||
val actualOffset = event.offset
|
||||
val candidate1 = calculateAngleFromOffsets(actualOffset.x, -actualOffset.z)
|
||||
val candidate2 = calculateAngleFromOffsets(-actualOffset.x, actualOffset.z)
|
||||
|
||||
if (isHookPossible(hook, event.position, candidate1, candidate2)) {
|
||||
recentParticles.add(Pair(event.position, TimeMark.now()))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLoad() {
|
||||
ParticleSpawnEvent.subscribe(::onParticleSpawn)
|
||||
WorldReadyEvent.subscribe {
|
||||
recentParticles.clear()
|
||||
}
|
||||
WorldRenderLastEvent.subscribe {
|
||||
recentParticles.removeIf { it.second.passedTime() > 5.seconds }
|
||||
renderBlocks(it.matrices, it.camera) {
|
||||
color(0f, 0f, 1f, 1f)
|
||||
recentParticles.forEach {
|
||||
tinyBlock(it.first, 0.1F)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
120
src/main/kotlin/moe/nea/firmament/features/world/FairySouls.kt
Normal file
120
src/main/kotlin/moe/nea/firmament/features/world/FairySouls.kt
Normal file
@@ -0,0 +1,120 @@
|
||||
package moe.nea.firmament.features.world
|
||||
|
||||
import io.github.moulberry.repo.data.Coordinate
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.serializer
|
||||
import moe.nea.firmament.events.ServerChatLineReceivedEvent
|
||||
import moe.nea.firmament.events.SkyblockServerUpdateEvent
|
||||
import moe.nea.firmament.events.WorldRenderLastEvent
|
||||
import moe.nea.firmament.features.NEUFeature
|
||||
import moe.nea.firmament.repo.RepoManager
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.SBData
|
||||
import moe.nea.firmament.util.blockPos
|
||||
import moe.nea.firmament.util.config.ManagedConfig
|
||||
import moe.nea.firmament.util.data.ProfileSpecificDataHolder
|
||||
import moe.nea.firmament.util.render.RenderBlockContext.Companion.renderBlocks
|
||||
import moe.nea.firmament.util.unformattedString
|
||||
|
||||
|
||||
object FairySouls : NEUFeature {
|
||||
|
||||
|
||||
@Serializable
|
||||
data class Data(
|
||||
val foundSouls: MutableMap<String, MutableSet<Int>> = mutableMapOf()
|
||||
)
|
||||
|
||||
override val config: ManagedConfig
|
||||
get() = TConfig
|
||||
|
||||
object DConfig : ProfileSpecificDataHolder<Data>(serializer(), "found-fairysouls", ::Data)
|
||||
|
||||
|
||||
object TConfig : ManagedConfig("fairy-souls") {
|
||||
|
||||
val displaySouls by toggle("show") { false }
|
||||
val resetSouls by button("reset") {
|
||||
DConfig.data?.foundSouls?.clear() != null
|
||||
updateMissingSouls()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override val name: String get() = "Fairy Souls"
|
||||
override val identifier: String get() = "fairy-souls"
|
||||
|
||||
val playerReach = 5
|
||||
val playerReachSquared = playerReach * playerReach
|
||||
|
||||
var currentLocationName: String? = null
|
||||
var currentLocationSouls: List<Coordinate> = emptyList()
|
||||
var currentMissingSouls: List<Coordinate> = emptyList()
|
||||
|
||||
fun updateMissingSouls() {
|
||||
currentMissingSouls = emptyList()
|
||||
val c = DConfig.data ?: return
|
||||
val fi = c.foundSouls[currentLocationName] ?: setOf()
|
||||
val cms = currentLocationSouls.toMutableList()
|
||||
fi.asSequence().sortedDescending().filter { it in cms.indices }.forEach { cms.removeAt(it) }
|
||||
currentMissingSouls = cms
|
||||
}
|
||||
|
||||
fun updateWorldSouls() {
|
||||
currentLocationSouls = emptyList()
|
||||
val loc = currentLocationName ?: return
|
||||
currentLocationSouls = RepoManager.neuRepo.constants.fairySouls.soulLocations[loc] ?: return
|
||||
}
|
||||
|
||||
fun findNearestClickableSoul(): Coordinate? {
|
||||
val player = MC.player ?: return null
|
||||
val pos = player.pos
|
||||
val location = SBData.skyblockLocation ?: return null
|
||||
val soulLocations: List<Coordinate> =
|
||||
RepoManager.neuRepo.constants.fairySouls.soulLocations[location] ?: return null
|
||||
return soulLocations
|
||||
.map { it to it.blockPos.getSquaredDistance(pos) }
|
||||
.filter { it.second < playerReachSquared }
|
||||
.minByOrNull { it.second }
|
||||
?.first
|
||||
}
|
||||
|
||||
private fun markNearestSoul() {
|
||||
val nearestSoul = findNearestClickableSoul() ?: return
|
||||
val c = DConfig.data ?: return
|
||||
val loc = currentLocationName ?: return
|
||||
val idx = currentLocationSouls.indexOf(nearestSoul)
|
||||
c.foundSouls.computeIfAbsent(loc) { mutableSetOf() }.add(idx)
|
||||
DConfig.markDirty()
|
||||
updateMissingSouls()
|
||||
}
|
||||
|
||||
|
||||
override fun onLoad() {
|
||||
SkyblockServerUpdateEvent.subscribe {
|
||||
currentLocationName = it.newLocraw?.skyblockLocation
|
||||
updateWorldSouls()
|
||||
updateMissingSouls()
|
||||
}
|
||||
ServerChatLineReceivedEvent.subscribe {
|
||||
when (it.text.unformattedString) {
|
||||
"You have already found that Fairy Soul!" -> {
|
||||
markNearestSoul()
|
||||
}
|
||||
|
||||
"SOUL! You found a Fairy Soul!" -> {
|
||||
markNearestSoul()
|
||||
}
|
||||
}
|
||||
}
|
||||
WorldRenderLastEvent.subscribe {
|
||||
if (!TConfig.displaySouls) return@subscribe
|
||||
renderBlocks(it.matrices, it.camera) {
|
||||
color(1F, 1F, 0F, 0.8F)
|
||||
currentMissingSouls.forEach {
|
||||
block(it.blockPos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user