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.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
import net.minecraft.text.Text import net.minecraft.text.Text
import moe.nea.firmament.apis.UrsaManager 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.buttons.InventoryButtons
import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen
import moe.nea.firmament.features.world.FairySouls 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: 2023 Linnea Gräf <nea@nea.moe>
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
* *
* SPDX-License-Identifier: GPL-3.0-or-later * 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.CaseInsensitiveLiteralCommandNode
import moe.nea.firmament.commands.DefaultSource import moe.nea.firmament.commands.DefaultSource
import moe.nea.firmament.commands.literal import moe.nea.firmament.commands.literal
import moe.nea.firmament.commands.thenLiteral
data class CommandEvent( data class CommandEvent(
val dispatcher: CommandDispatcher<DefaultSource>, val dispatcher: CommandDispatcher<DefaultSource>,
@@ -20,6 +22,20 @@ data class CommandEvent(
) : FirmamentEvent() { ) : FirmamentEvent() {
companion object : FirmamentEventBus<CommandEvent>() 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) { fun deleteCommand(name: String) {
dispatcher.root.children.removeIf { it.name.equals(name, ignoreCase = false) } dispatcher.root.children.removeIf { it.name.equals(name, ignoreCase = false) }
serverCommands?.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 package moe.nea.firmament.features.diana
import org.joml.Vector3f
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
import net.minecraft.particle.ParticleTypes import net.minecraft.particle.ParticleTypes
import net.minecraft.sound.SoundEvents import net.minecraft.sound.SoundEvents
@@ -33,6 +32,7 @@ object AncestralSpadeSolver {
fun isEnabled() = fun isEnabled() =
DianaWaypoints.TConfig.ancestralSpadeSolver && SBData.skyblockLocation == "hub" DianaWaypoints.TConfig.ancestralSpadeSolver && SBData.skyblockLocation == "hub"
fun onKeyBind(event: WorldKeyboardEvent) { fun onKeyBind(event: WorldKeyboardEvent) {
if (!isEnabled()) return if (!isEnabled()) return
if (!event.matches(DianaWaypoints.TConfig.ancestralSpadeTeleport)) return if (!event.matches(DianaWaypoints.TConfig.ancestralSpadeTeleport)) return
@@ -99,8 +99,7 @@ object AncestralSpadeSolver {
color(1f, 1f, 0f, 0.5f) color(1f, 1f, 0f, 0.5f)
tinyBlock(it, 1f) tinyBlock(it, 1f)
color(1f, 1f, 0f, 1f) color(1f, 1f, 0f, 1f)
val cameraForward = Vector3f(0f, 0f, 1f).rotate(event.camera.rotation) tracer(it, lineWidth = 3f)
line(event.camera.pos.add(Vec3d(cameraForward)), it, lineWidth = 3f)
} }
if (particlePositions.size > 2 && lastDing.passedTime() < 10.seconds && nextGuess != null) { if (particlePositions.size > 2 && lastDing.passedTime() < 10.seconds && nextGuess != null) {
color(0f, 1f, 0f, 0.7f) color(0f, 1f, 0f, 0.7f)

View File

@@ -1,20 +1,38 @@
/* /*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe> * 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 * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
package moe.nea.firmament.features.world 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.hours
import kotlin.time.Duration.Companion.seconds 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.text.Text
import net.minecraft.util.math.BlockPos 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.ProcessChatEvent
import moe.nea.firmament.events.TickEvent
import moe.nea.firmament.events.WorldReadyEvent import moe.nea.firmament.events.WorldReadyEvent
import moe.nea.firmament.events.WorldRenderLastEvent import moe.nea.firmament.events.WorldRenderLastEvent
import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.util.ClipboardUtils
import moe.nea.firmament.util.MC import moe.nea.firmament.util.MC
import moe.nea.firmament.util.TimeMark import moe.nea.firmament.util.TimeMark
import moe.nea.firmament.util.render.RenderInWorldContext import moe.nea.firmament.util.render.RenderInWorldContext
@@ -34,19 +52,34 @@ object Waypoints : FirmamentFeature {
override val config get() = TConfig override val config get() = TConfig
val temporaryWaypointList = mutableMapOf<String, TemporaryWaypoint>() val temporaryPlayerWaypointList = mutableMapOf<String, TemporaryWaypoint>()
val temporaryWaypointMatcher = "(?i)x: (-?[0-9]+),? y: (-?[0-9]+),? z: (-?[0-9]+)".toPattern() 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() { override fun onLoad() {
WorldRenderLastEvent.subscribe { event -> WorldRenderLastEvent.subscribe { event ->
temporaryWaypointList.entries.removeIf { it.value.postedAt.passedTime() > TConfig.tempWaypointDuration } temporaryPlayerWaypointList.entries.removeIf { it.value.postedAt.passedTime() > TConfig.tempWaypointDuration }
if (temporaryWaypointList.isNotEmpty()) if (temporaryPlayerWaypointList.isNotEmpty())
RenderInWorldContext.renderInWorld(event) { RenderInWorldContext.renderInWorld(event) {
color(1f, 1f, 0f, 1f) color(1f, 1f, 0f, 1f)
temporaryWaypointList.forEach { (player, waypoint) -> temporaryPlayerWaypointList.forEach { (player, waypoint) ->
block(waypoint.pos) block(waypoint.pos)
} }
color(1f, 1f, 1f, 1f) color(1f, 1f, 1f, 1f)
temporaryWaypointList.forEach { (player, waypoint) -> temporaryPlayerWaypointList.forEach { (player, waypoint) ->
val skin = val skin =
MC.networkHandler?.listedPlayerListEntries?.find { it.profile.name == player } MC.networkHandler?.listedPlayerListEntries?.find { it.profile.name == player }
?.skinTextures ?.skinTextures
@@ -73,12 +106,106 @@ object Waypoints : FirmamentFeature {
} }
} }
WorldReadyEvent.subscribe { 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 { ProcessChatEvent.subscribe {
val matcher = temporaryWaypointMatcher.matcher(it.unformattedString) val matcher = temporaryPlayerWaypointMatcher.matcher(it.unformattedString)
if (it.nameHeuristic != null && TConfig.tempWaypointDuration > 0.seconds && matcher.find()) { if (it.nameHeuristic != null && TConfig.tempWaypointDuration > 0.seconds && matcher.find()) {
temporaryWaypointList[it.nameHeuristic] = TemporaryWaypoint( temporaryPlayerWaypointList[it.nameHeuristic] = TemporaryWaypoint(
BlockPos( BlockPos(
matcher.group(1).toInt(), matcher.group(1).toInt(),
matcher.group(2).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: 2023 Linnea Gräf <nea@nea.moe>
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
* *
* SPDX-License-Identifier: GPL-3.0-or-later * 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 effectiveFov = (MC.instance.gameRenderer as AccessorGameRenderer).getFov_firmament(camera, tickDelta, true)
val effectiveFovScaleFactor = 1 / tan(toRadians(effectiveFov) / 2) 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) { fun color(red: Float, green: Float, blue: Float, alpha: Float) {
RenderSystem.setShaderColor(red, green, blue, alpha) RenderSystem.setShaderColor(red, green, blue, alpha)
} }
@@ -140,6 +145,11 @@ class RenderInWorldContext private constructor(
line(points.toList(), lineWidth) 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) { fun line(points: List<Vec3d>, lineWidth: Float = 10F) {
RenderSystem.setShader(GameRenderer::getRenderTypeLinesProgram) RenderSystem.setShader(GameRenderer::getRenderTypeLinesProgram)
RenderSystem.lineWidth(lineWidth / pow(camera.pos.squaredDistanceTo(points.first()), 0.25).toFloat()) RenderSystem.lineWidth(lineWidth / pow(camera.pos.squaredDistanceTo(points.first()), 0.25).toFloat())

View File

@@ -5,6 +5,10 @@
"firmament.command.toggle.no-property-found": "Could not find property %s", "firmament.command.toggle.no-property-found": "Could not find property %s",
"firmament.command.toggle.not-a-toggle": "Property %s is not a toggle", "firmament.command.toggle.not-a-toggle": "Property %s is not a toggle",
"firmament.command.toggle.toggled": "Toggled %s / %s %s", "firmament.command.toggle.toggled": "Toggled %s / %s %s",
"firmament.command.waypoint.import": "Imported %s waypoints from clipboard.",
"firmament.command.waypoint.clear": "Cleared waypoints.",
"firmament.command.waypoint.added": "Added waypoint %s %s %s.",
"firmament.command.waypoint.import.error": "Could not import waypoints. Make sure they are on ColeWeight format:\n[{\"x\": 69, \"y\":420, \"z\": 36}]",
"firmament.pristine-profit.collection": "Collection: %s/h", "firmament.pristine-profit.collection": "Collection: %s/h",
"firmament.pristine-profit.money": "Money: %s/h", "firmament.pristine-profit.money": "Money: %s/h",
"firmament.toggle.true": "On", "firmament.toggle.true": "On",