diff options
author | Linnea Gräf <nea@nea.moe> | 2024-03-24 00:43:16 +0100 |
---|---|---|
committer | Linnea Gräf <nea@nea.moe> | 2024-03-24 00:43:16 +0100 |
commit | a84da715497215848797719fe528c2f5c1d4aed3 (patch) | |
tree | 3be839c8cf6fb080b7307919d94bb82666f79a7b | |
parent | 55586a64d7037cdc557b8b2c337d284ddb1ac981 (diff) | |
download | Firmament-a84da715497215848797719fe528c2f5c1d4aed3.tar.gz Firmament-a84da715497215848797719fe528c2f5c1d4aed3.tar.bz2 Firmament-a84da715497215848797719fe528c2f5c1d4aed3.zip |
Add ordered waypoints (ColeWeight compat)
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", |