aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-03-24 00:43:16 +0100
committerLinnea Gräf <nea@nea.moe>2024-03-24 00:43:16 +0100
commita84da715497215848797719fe528c2f5c1d4aed3 (patch)
tree3be839c8cf6fb080b7307919d94bb82666f79a7b
parent55586a64d7037cdc557b8b2c337d284ddb1ac981 (diff)
downloadFirmament-a84da715497215848797719fe528c2f5c1d4aed3.tar.gz
Firmament-a84da715497215848797719fe528c2f5c1d4aed3.tar.bz2
Firmament-a84da715497215848797719fe528c2f5c1d4aed3.zip
Add ordered waypoints (ColeWeight compat)
-rw-r--r--src/main/kotlin/moe/nea/firmament/commands/rome.kt2
-rw-r--r--src/main/kotlin/moe/nea/firmament/events/CommandEvent.kt16
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/diana/AncestralSpadeSolver.kt5
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/world/Waypoints.kt172
-rw-r--r--src/main/kotlin/moe/nea/firmament/util/render/RenderInWorldContext.kt10
-rw-r--r--src/main/resources/assets/firmament/lang/en_us.json4
6 files changed, 197 insertions, 12 deletions
diff --git a/src/main/kotlin/moe/nea/firmament/commands/rome.kt b/src/main/kotlin/moe/nea/firmament/commands/rome.kt
index 7df39b3..74391a6 100644
--- a/src/main/kotlin/moe/nea/firmament/commands/rome.kt
+++ b/src/main/kotlin/moe/nea/firmament/commands/rome.kt
@@ -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))
}
diff --git a/src/main/kotlin/moe/nea/firmament/events/CommandEvent.kt b/src/main/kotlin/moe/nea/firmament/events/CommandEvent.kt
index 5710245..1d1f8d3 100644
--- a/src/main/kotlin/moe/nea/firmament/events/CommandEvent.kt
+++ b/src/main/kotlin/moe/nea/firmament/events/CommandEvent.kt
@@ -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) }
diff --git a/src/main/kotlin/moe/nea/firmament/features/diana/AncestralSpadeSolver.kt b/src/main/kotlin/moe/nea/firmament/features/diana/AncestralSpadeSolver.kt
index cd399b3..b9c83e6 100644
--- a/src/main/kotlin/moe/nea/firmament/features/diana/AncestralSpadeSolver.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/diana/AncestralSpadeSolver.kt
@@ -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)
diff --git a/src/main/kotlin/moe/nea/firmament/features/world/Waypoints.kt b/src/main/kotlin/moe/nea/firmament/features/world/Waypoints.kt
index 05b1ba5..279be6f 100644
--- a/src/main/kotlin/moe/nea/firmament/features/world/Waypoints.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/world/Waypoints.kt
@@ -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
+ )
+}
diff --git a/src/main/kotlin/moe/nea/firmament/util/render/RenderInWorldContext.kt b/src/main/kotlin/moe/nea/firmament/util/render/RenderInWorldContext.kt
index 897fabb..ecaf3f9 100644
--- a/src/main/kotlin/moe/nea/firmament/util/render/RenderInWorldContext.kt
+++ b/src/main/kotlin/moe/nea/firmament/util/render/RenderInWorldContext.kt
@@ -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())
diff --git a/src/main/resources/assets/firmament/lang/en_us.json b/src/main/resources/assets/firmament/lang/en_us.json
index 3c2df22..bc0e205 100644
--- a/src/main/resources/assets/firmament/lang/en_us.json
+++ b/src/main/resources/assets/firmament/lang/en_us.json
@@ -5,6 +5,10 @@
"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.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.money": "Money: %s/h",
"firmament.toggle.true": "On",