Add ordered waypoints (ColeWeight compat)

This commit is contained in:
Linnea Gräf
2024-03-24 00:43:16 +01:00
parent 55586a64d7
commit a84da71549
6 changed files with 197 additions and 12 deletions

View File

@@ -13,6 +13,7 @@ import io.ktor.client.statement.*
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
import net.minecraft.text.Text
import moe.nea.firmament.apis.UrsaManager
import moe.nea.firmament.events.CommandEvent
import moe.nea.firmament.features.inventory.buttons.InventoryButtons
import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen
import moe.nea.firmament.features.world.FairySouls
@@ -218,6 +219,7 @@ fun firmamentCommand() = literal("firmament") {
}
}
}
CommandEvent.SubCommand.publish(CommandEvent.SubCommand(this@literal))
}

View File

@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
@@ -12,6 +13,7 @@ import net.minecraft.command.CommandRegistryAccess
import moe.nea.firmament.commands.CaseInsensitiveLiteralCommandNode
import moe.nea.firmament.commands.DefaultSource
import moe.nea.firmament.commands.literal
import moe.nea.firmament.commands.thenLiteral
data class CommandEvent(
val dispatcher: CommandDispatcher<DefaultSource>,
@@ -20,6 +22,20 @@ data class CommandEvent(
) : FirmamentEvent() {
companion object : FirmamentEventBus<CommandEvent>()
/**
* Register subcommands to `/firm`. For new top level commands use [CommandEvent]. Cannot be used to register
* subcommands to other commands.
*/
data class SubCommand(
val builder: CaseInsensitiveLiteralCommandNode.Builder<DefaultSource>,
) : FirmamentEvent() {
companion object : FirmamentEventBus<SubCommand>()
fun subcommand(name: String, block: CaseInsensitiveLiteralCommandNode.Builder<DefaultSource>.() -> Unit) {
builder.thenLiteral(name, block)
}
}
fun deleteCommand(name: String) {
dispatcher.root.children.removeIf { it.name.equals(name, ignoreCase = false) }
serverCommands?.root?.children?.removeIf { it.name.equals(name, ignoreCase = false) }

View File

@@ -6,7 +6,6 @@
package moe.nea.firmament.features.diana
import org.joml.Vector3f
import kotlin.time.Duration.Companion.seconds
import net.minecraft.particle.ParticleTypes
import net.minecraft.sound.SoundEvents
@@ -33,6 +32,7 @@ object AncestralSpadeSolver {
fun isEnabled() =
DianaWaypoints.TConfig.ancestralSpadeSolver && SBData.skyblockLocation == "hub"
fun onKeyBind(event: WorldKeyboardEvent) {
if (!isEnabled()) return
if (!event.matches(DianaWaypoints.TConfig.ancestralSpadeTeleport)) return
@@ -99,8 +99,7 @@ object AncestralSpadeSolver {
color(1f, 1f, 0f, 0.5f)
tinyBlock(it, 1f)
color(1f, 1f, 0f, 1f)
val cameraForward = Vector3f(0f, 0f, 1f).rotate(event.camera.rotation)
line(event.camera.pos.add(Vec3d(cameraForward)), it, lineWidth = 3f)
tracer(it, lineWidth = 3f)
}
if (particlePositions.size > 2 && lastDing.passedTime() < 10.seconds && nextGuess != null) {
color(0f, 1f, 0f, 0.7f)

View File

@@ -1,20 +1,38 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.features.world
import me.shedaniel.math.Color
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.set
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.seconds
import net.minecraft.command.argument.BlockPosArgumentType
import net.minecraft.server.command.ServerCommandSource
import net.minecraft.text.Text
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Vec3d
import moe.nea.firmament.Firmament
import moe.nea.firmament.commands.thenArgument
import moe.nea.firmament.commands.thenExecute
import moe.nea.firmament.commands.thenLiteral
import moe.nea.firmament.events.CommandEvent
import moe.nea.firmament.events.ProcessChatEvent
import moe.nea.firmament.events.TickEvent
import moe.nea.firmament.events.WorldReadyEvent
import moe.nea.firmament.events.WorldRenderLastEvent
import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.util.ClipboardUtils
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.TimeMark
import moe.nea.firmament.util.render.RenderInWorldContext
@@ -34,19 +52,34 @@ object Waypoints : FirmamentFeature {
override val config get() = TConfig
val temporaryWaypointList = mutableMapOf<String, TemporaryWaypoint>()
val temporaryWaypointMatcher = "(?i)x: (-?[0-9]+),? y: (-?[0-9]+),? z: (-?[0-9]+)".toPattern()
val temporaryPlayerWaypointList = mutableMapOf<String, TemporaryWaypoint>()
val temporaryPlayerWaypointMatcher = "(?i)x: (-?[0-9]+),? y: (-?[0-9]+),? z: (-?[0-9]+)".toPattern()
val waypoints = mutableListOf<BlockPos>()
var ordered = false
var orderedIndex = 0
@Serializable
data class ColeWeightWaypoint(
val x: Int,
val y: Int,
val z: Int,
val r: Int = 0,
val g: Int = 0,
val b: Int = 0,
)
override fun onLoad() {
WorldRenderLastEvent.subscribe { event ->
temporaryWaypointList.entries.removeIf { it.value.postedAt.passedTime() > TConfig.tempWaypointDuration }
if (temporaryWaypointList.isNotEmpty())
temporaryPlayerWaypointList.entries.removeIf { it.value.postedAt.passedTime() > TConfig.tempWaypointDuration }
if (temporaryPlayerWaypointList.isNotEmpty())
RenderInWorldContext.renderInWorld(event) {
color(1f, 1f, 0f, 1f)
temporaryWaypointList.forEach { (player, waypoint) ->
temporaryPlayerWaypointList.forEach { (player, waypoint) ->
block(waypoint.pos)
}
color(1f, 1f, 1f, 1f)
temporaryWaypointList.forEach { (player, waypoint) ->
temporaryPlayerWaypointList.forEach { (player, waypoint) ->
val skin =
MC.networkHandler?.listedPlayerListEntries?.find { it.profile.name == player }
?.skinTextures
@@ -73,12 +106,106 @@ object Waypoints : FirmamentFeature {
}
}
WorldReadyEvent.subscribe {
temporaryWaypointList.clear()
temporaryPlayerWaypointList.clear()
}
CommandEvent.SubCommand.subscribe { event ->
event.subcommand("waypoint") {
thenArgument("pos", BlockPosArgumentType.blockPos()) { pos ->
thenExecute {
val position = pos.get(this).toAbsoluteBlockPos(source.asFakeServer())
waypoints.add(position)
source.sendFeedback(
Text.stringifiedTranslatable(
"firmament.command.waypoint.added",
position.x,
position.y,
position.z
)
)
}
}
}
event.subcommand("waypoints") {
thenLiteral("clear") {
thenExecute {
waypoints.clear()
source.sendFeedback(Text.translatable("firmament.command.waypoint.clear"))
}
}
thenLiteral("toggleordered") {
thenExecute {
ordered = !ordered
if (ordered) {
val p = MC.player?.pos ?: Vec3d.ZERO
orderedIndex =
waypoints.withIndex().minByOrNull { it.value.getSquaredDistance(p) }?.index ?: 0
}
source.sendFeedback(Text.translatable("firmament.command.waypoint.ordered.toggle.$ordered"))
}
}
thenLiteral("import") {
thenExecute {
val contents = ClipboardUtils.getTextContents()
val data = try {
Firmament.json.decodeFromString<List<ColeWeightWaypoint>>(contents)
} catch (ex: Exception) {
Firmament.logger.error("Could not load waypoints from clipboard", ex)
source.sendError(Text.translatable("firmament.command.waypoint.import.error"))
return@thenExecute
}
waypoints.clear()
data.mapTo(waypoints) { BlockPos(it.x, it.y, it.z) }
source.sendFeedback(
Text.stringifiedTranslatable(
"firmament.command.waypoint.import",
data.size
)
)
}
}
}
}
WorldRenderLastEvent.subscribe { event ->
if (waypoints.isEmpty()) return@subscribe
RenderInWorldContext.renderInWorld(event) {
if (!ordered) {
color(0f, 0.3f, 0.7f, 0.5f)
waypoints.forEach {
block(it)
}
} else {
orderedIndex %= waypoints.size
val firstColor = Color.ofRGBA(0, 200, 40, 180)
color(firstColor)
tracer(waypoints[orderedIndex].toCenterPos(), lineWidth = 3f)
waypoints.wrappingWindow(orderedIndex, 3)
.zip(
listOf(
firstColor,
Color.ofRGBA(180, 200, 40, 150),
Color.ofRGBA(180, 80, 20, 140),
)
)
.reversed()
.forEach { (pos, col) ->
color(col)
block(pos)
}
}
}
}
TickEvent.subscribe {
if (waypoints.isEmpty() || !ordered) return@subscribe
orderedIndex %= waypoints.size
val p = MC.player?.pos ?: return@subscribe
if (waypoints[orderedIndex].isWithinDistance(p, 3.0)) {
orderedIndex = (orderedIndex + 1) % waypoints.size
}
}
ProcessChatEvent.subscribe {
val matcher = temporaryWaypointMatcher.matcher(it.unformattedString)
val matcher = temporaryPlayerWaypointMatcher.matcher(it.unformattedString)
if (it.nameHeuristic != null && TConfig.tempWaypointDuration > 0.seconds && matcher.find()) {
temporaryWaypointList[it.nameHeuristic] = TemporaryWaypoint(
temporaryPlayerWaypointList[it.nameHeuristic] = TemporaryWaypoint(
BlockPos(
matcher.group(1).toInt(),
matcher.group(2).toInt(),
@@ -90,3 +217,30 @@ object Waypoints : FirmamentFeature {
}
}
}
fun <E> List<E>.wrappingWindow(startIndex: Int, windowSize: Int): List<E> {
val result = ArrayList<E>(windowSize)
if (startIndex + windowSize < size) {
result.addAll(subList(startIndex, startIndex + windowSize))
} else {
result.addAll(subList(startIndex, size))
result.addAll(subList(0, minOf(size - startIndex - windowSize, startIndex)))
}
return result
}
fun FabricClientCommandSource.asFakeServer(): ServerCommandSource {
val source = this
return ServerCommandSource(
source.player,
source.position,
source.rotation,
null,
0,
"FakeServerCommandSource",
Text.literal("FakeServerCommandSource"),
null,
source.player
)
}

View File

@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
@@ -44,6 +45,10 @@ class RenderInWorldContext private constructor(
val effectiveFov = (MC.instance.gameRenderer as AccessorGameRenderer).getFov_firmament(camera, tickDelta, true)
val effectiveFovScaleFactor = 1 / tan(toRadians(effectiveFov) / 2)
fun color(color: me.shedaniel.math.Color) {
color(color.red / 255F, color.green / 255f, color.blue / 255f, color.alpha / 255f)
}
fun color(red: Float, green: Float, blue: Float, alpha: Float) {
RenderSystem.setShaderColor(red, green, blue, alpha)
}
@@ -140,6 +145,11 @@ class RenderInWorldContext private constructor(
line(points.toList(), lineWidth)
}
fun tracer(toWhere: Vec3d, lineWidth: Float = 3f) {
val cameraForward = Vector3f(0f, 0f, 1f).rotate(camera.rotation)
line(camera.pos.add(Vec3d(cameraForward)), toWhere, lineWidth = lineWidth)
}
fun line(points: List<Vec3d>, lineWidth: Float = 10F) {
RenderSystem.setShader(GameRenderer::getRenderTypeLinesProgram)
RenderSystem.lineWidth(lineWidth / pow(camera.pos.squaredDistanceTo(points.first()), 0.25).toFloat())