Refactor source layout
Introduce compat source sets and move all kotlin sources to the main directory [no changelog]
This commit is contained in:
131
src/main/kotlin/features/world/FairySouls.kt
Normal file
131
src/main/kotlin/features/world/FairySouls.kt
Normal file
@@ -0,0 +1,131 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.features.world
|
||||
|
||||
import io.github.moulberry.repo.data.Coordinate
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.serializer
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.events.ProcessChatEvent
|
||||
import moe.nea.firmament.events.SkyblockServerUpdateEvent
|
||||
import moe.nea.firmament.events.WorldRenderLastEvent
|
||||
import moe.nea.firmament.features.FirmamentFeature
|
||||
import moe.nea.firmament.gui.config.ManagedConfig
|
||||
import moe.nea.firmament.repo.RepoManager
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.SBData
|
||||
import moe.nea.firmament.util.SkyBlockIsland
|
||||
import moe.nea.firmament.util.blockPos
|
||||
import moe.nea.firmament.util.data.ProfileSpecificDataHolder
|
||||
import moe.nea.firmament.util.render.RenderInWorldContext
|
||||
import moe.nea.firmament.util.render.RenderInWorldContext.Companion.renderInWorld
|
||||
import moe.nea.firmament.util.unformattedString
|
||||
|
||||
|
||||
object FairySouls : FirmamentFeature {
|
||||
|
||||
|
||||
@Serializable
|
||||
data class Data(
|
||||
val foundSouls: MutableMap<SkyBlockIsland, 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 identifier: String get() = "fairy-souls"
|
||||
|
||||
val playerReach = 5
|
||||
val playerReachSquared = playerReach * playerReach
|
||||
|
||||
var currentLocationName: SkyBlockIsland? = 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.locrawMode] ?: 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.locrawMode] ?: 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()
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onWorldRender(it: WorldRenderLastEvent) {
|
||||
if (!TConfig.displaySouls) return
|
||||
renderInWorld(it) {
|
||||
color(1F, 1F, 0F, 0.8F)
|
||||
currentMissingSouls.forEach {
|
||||
block(it.blockPos)
|
||||
}
|
||||
color(1f, 0f, 1f, 1f)
|
||||
currentLocationSouls.forEach {
|
||||
wireframeCube(it.blockPos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onProcessChat(it: ProcessChatEvent) {
|
||||
when (it.text.unformattedString) {
|
||||
"You have already found that Fairy Soul!" -> {
|
||||
markNearestSoul()
|
||||
}
|
||||
|
||||
"SOUL! You found a Fairy Soul!" -> {
|
||||
markNearestSoul()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onLocationChange(it: SkyblockServerUpdateEvent) {
|
||||
currentLocationName = it.newLocraw?.skyblockLocation
|
||||
updateWorldSouls()
|
||||
updateMissingSouls()
|
||||
}
|
||||
}
|
||||
40
src/main/kotlin/features/world/NPCWaypoints.kt
Normal file
40
src/main/kotlin/features/world/NPCWaypoints.kt
Normal file
@@ -0,0 +1,40 @@
|
||||
package moe.nea.firmament.features.world
|
||||
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.commands.thenExecute
|
||||
import moe.nea.firmament.events.CommandEvent
|
||||
import moe.nea.firmament.events.ReloadRegistrationEvent
|
||||
import moe.nea.firmament.util.MoulConfigUtils
|
||||
import moe.nea.firmament.util.ScreenUtil
|
||||
|
||||
object NPCWaypoints {
|
||||
|
||||
var allNpcWaypoints = listOf<NavigableWaypoint>()
|
||||
|
||||
@Subscribe
|
||||
fun onRepoReloadRegistration(event: ReloadRegistrationEvent) {
|
||||
event.repo.registerReloadListener {
|
||||
allNpcWaypoints = it.items.items.values
|
||||
.asSequence()
|
||||
.filter { !it.island.isNullOrBlank() }
|
||||
.map {
|
||||
NavigableWaypoint.NPCWaypoint(it)
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onOpenGui(event: CommandEvent.SubCommand) {
|
||||
event.subcommand("npcs") {
|
||||
thenExecute {
|
||||
ScreenUtil.setScreenLater(MoulConfigUtils.loadScreen(
|
||||
"npc_waypoints",
|
||||
NpcWaypointGui(allNpcWaypoints),
|
||||
null))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
22
src/main/kotlin/features/world/NavigableWaypoint.kt
Normal file
22
src/main/kotlin/features/world/NavigableWaypoint.kt
Normal file
@@ -0,0 +1,22 @@
|
||||
package moe.nea.firmament.features.world
|
||||
|
||||
import io.github.moulberry.repo.data.NEUItem
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import moe.nea.firmament.util.SkyBlockIsland
|
||||
|
||||
abstract class NavigableWaypoint {
|
||||
abstract val name: String
|
||||
abstract val position: BlockPos
|
||||
abstract val island: SkyBlockIsland
|
||||
|
||||
data class NPCWaypoint(
|
||||
val item: NEUItem,
|
||||
) : NavigableWaypoint() {
|
||||
override val name: String
|
||||
get() = item.displayName
|
||||
override val position: BlockPos
|
||||
get() = BlockPos(item.x, item.y, item.z)
|
||||
override val island: SkyBlockIsland
|
||||
get() = SkyBlockIsland.forMode(item.island)
|
||||
}
|
||||
}
|
||||
121
src/main/kotlin/features/world/NavigationHelper.kt
Normal file
121
src/main/kotlin/features/world/NavigationHelper.kt
Normal file
@@ -0,0 +1,121 @@
|
||||
package moe.nea.firmament.features.world
|
||||
|
||||
import io.github.moulberry.repo.constants.Islands
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Position
|
||||
import net.minecraft.util.math.Vec3i
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.events.SkyblockServerUpdateEvent
|
||||
import moe.nea.firmament.events.TickEvent
|
||||
import moe.nea.firmament.events.WorldRenderLastEvent
|
||||
import moe.nea.firmament.repo.RepoManager
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.SBData
|
||||
import moe.nea.firmament.util.SkyBlockIsland
|
||||
import moe.nea.firmament.util.WarpUtil
|
||||
import moe.nea.firmament.util.render.RenderInWorldContext
|
||||
|
||||
object NavigationHelper {
|
||||
var targetWaypoint: NavigableWaypoint? = null
|
||||
set(value) {
|
||||
field = value
|
||||
recalculateRoute()
|
||||
}
|
||||
|
||||
var nextTeleporter: Islands.Teleporter? = null
|
||||
private set
|
||||
|
||||
val Islands.Teleporter.toIsland get() = SkyBlockIsland.forMode(this.getTo())
|
||||
val Islands.Teleporter.fromIsland get() = SkyBlockIsland.forMode(this.getFrom())
|
||||
val Islands.Teleporter.blockPos get() = BlockPos(x.toInt(), y.toInt(), z.toInt())
|
||||
|
||||
@Subscribe
|
||||
fun onWorldSwitch(event: SkyblockServerUpdateEvent) {
|
||||
recalculateRoute()
|
||||
}
|
||||
|
||||
fun recalculateRoute() {
|
||||
val tp = targetWaypoint
|
||||
val currentIsland = SBData.skyblockLocation
|
||||
if (tp == null || currentIsland == null) {
|
||||
nextTeleporter = null
|
||||
return
|
||||
}
|
||||
val route = findRoute(currentIsland, tp.island, mutableSetOf())
|
||||
nextTeleporter = route?.get(0)
|
||||
}
|
||||
|
||||
private fun findRoute(
|
||||
fromIsland: SkyBlockIsland,
|
||||
targetIsland: SkyBlockIsland,
|
||||
visitedIslands: MutableSet<SkyBlockIsland>
|
||||
): MutableList<Islands.Teleporter>? {
|
||||
var shortestChain: MutableList<Islands.Teleporter>? = null
|
||||
for (it in RepoManager.neuRepo.constants.islands.teleporters) {
|
||||
if (it.toIsland in visitedIslands) continue
|
||||
if (it.fromIsland != fromIsland) continue
|
||||
if (it.toIsland == targetIsland) return mutableListOf(it)
|
||||
visitedIslands.add(fromIsland)
|
||||
val nextRoute = findRoute(it.toIsland, targetIsland, visitedIslands) ?: continue
|
||||
nextRoute.add(0, it)
|
||||
if (shortestChain == null || shortestChain.size > nextRoute.size) {
|
||||
shortestChain = nextRoute
|
||||
}
|
||||
visitedIslands.remove(fromIsland)
|
||||
}
|
||||
return shortestChain
|
||||
}
|
||||
|
||||
|
||||
@Subscribe
|
||||
fun onMovement(event: TickEvent) { // TODO: add a movement tick event maybe?
|
||||
val tp = targetWaypoint ?: return
|
||||
val p = MC.player ?: return
|
||||
if (p.squaredDistanceTo(tp.position.toCenterPos()) < 5 * 5) {
|
||||
targetWaypoint = null
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun drawWaypoint(event: WorldRenderLastEvent) {
|
||||
val tp = targetWaypoint ?: return
|
||||
val nt = nextTeleporter
|
||||
RenderInWorldContext.renderInWorld(event) {
|
||||
if (nt != null) {
|
||||
waypoint(nt.blockPos,
|
||||
Text.literal("Teleporter to " + nt.toIsland.userFriendlyName),
|
||||
Text.literal("(towards " + tp.name + "§f)"))
|
||||
} else if (tp.island == SBData.skyblockLocation) {
|
||||
waypoint(tp.position,
|
||||
Text.literal(tp.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun tryWarpNear() {
|
||||
val tp = targetWaypoint
|
||||
if (tp == null) {
|
||||
MC.sendChat(Text.literal("Could not find a waypoint to warp you to. Select one first."))
|
||||
return
|
||||
}
|
||||
WarpUtil.teleportToNearestWarp(tp.island, tp.position.asPositionView())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun Vec3i.asPositionView(): Position {
|
||||
return object : Position {
|
||||
override fun getX(): Double {
|
||||
return this@asPositionView.x.toDouble()
|
||||
}
|
||||
|
||||
override fun getY(): Double {
|
||||
return this@asPositionView.y.toDouble()
|
||||
}
|
||||
|
||||
override fun getZ(): Double {
|
||||
return this@asPositionView.z.toDouble()
|
||||
}
|
||||
}
|
||||
}
|
||||
68
src/main/kotlin/features/world/NpcWaypointGui.kt
Normal file
68
src/main/kotlin/features/world/NpcWaypointGui.kt
Normal file
@@ -0,0 +1,68 @@
|
||||
package moe.nea.firmament.features.world
|
||||
|
||||
import io.github.notenoughupdates.moulconfig.observer.ObservableList
|
||||
import io.github.notenoughupdates.moulconfig.xml.Bind
|
||||
import moe.nea.firmament.features.events.anniversity.AnniversaryFeatures.atOnce
|
||||
import moe.nea.firmament.keybindings.SavedKeyBinding
|
||||
|
||||
class NpcWaypointGui(
|
||||
val allWaypoints: List<NavigableWaypoint>,
|
||||
) {
|
||||
|
||||
data class NavigableWaypointW(val waypoint: NavigableWaypoint) {
|
||||
@Bind
|
||||
fun name() = waypoint.name
|
||||
|
||||
@Bind
|
||||
fun isSelected() = NavigationHelper.targetWaypoint == waypoint
|
||||
|
||||
@Bind
|
||||
fun click() {
|
||||
if (SavedKeyBinding.isShiftDown()) {
|
||||
NavigationHelper.targetWaypoint = waypoint
|
||||
NavigationHelper.tryWarpNear()
|
||||
} else if (isSelected()) {
|
||||
NavigationHelper.targetWaypoint = null
|
||||
} else {
|
||||
NavigationHelper.targetWaypoint = waypoint
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmField
|
||||
@field:Bind
|
||||
var search: String = ""
|
||||
var lastSearch: String? = null
|
||||
|
||||
@Bind("results")
|
||||
fun results(): ObservableList<NavigableWaypointW> {
|
||||
return results
|
||||
}
|
||||
|
||||
@Bind
|
||||
fun tick() {
|
||||
if (search != lastSearch) {
|
||||
updateSearch()
|
||||
lastSearch = search
|
||||
}
|
||||
}
|
||||
|
||||
val results: ObservableList<NavigableWaypointW> = ObservableList(mutableListOf())
|
||||
|
||||
fun updateSearch() {
|
||||
val split = search.split(" +".toRegex())
|
||||
results.atOnce {
|
||||
results.clear()
|
||||
allWaypoints.filter { waypoint ->
|
||||
if (search.isBlank()) {
|
||||
true
|
||||
} else {
|
||||
split.all { waypoint.name.contains(it, ignoreCase = true) }
|
||||
}
|
||||
}.mapTo(results) {
|
||||
NavigableWaypointW(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
297
src/main/kotlin/features/world/Waypoints.kt
Normal file
297
src/main/kotlin/features/world/Waypoints.kt
Normal file
@@ -0,0 +1,297 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.features.world
|
||||
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType
|
||||
import me.shedaniel.math.Color
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
|
||||
import kotlinx.serialization.Serializable
|
||||
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.annotations.Subscribe
|
||||
import moe.nea.firmament.commands.get
|
||||
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
|
||||
|
||||
object Waypoints : FirmamentFeature {
|
||||
override val identifier: String
|
||||
get() = "waypoints"
|
||||
|
||||
object TConfig : ManagedConfig(identifier) {
|
||||
val tempWaypointDuration by duration("temp-waypoint-duration", 0.seconds, 1.hours) { 30.seconds }
|
||||
val showIndex by toggle("show-index") { true }
|
||||
val skipToNearest by toggle("skip-to-nearest") { false }
|
||||
// TODO: look ahead size
|
||||
}
|
||||
|
||||
data class TemporaryWaypoint(
|
||||
val pos: BlockPos,
|
||||
val postedAt: TimeMark,
|
||||
)
|
||||
|
||||
override val config get() = TConfig
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
@Subscribe
|
||||
fun onRenderOrderedWaypoints(event: WorldRenderLastEvent) {
|
||||
if (waypoints.isEmpty()) return
|
||||
RenderInWorldContext.renderInWorld(event) {
|
||||
if (!ordered) {
|
||||
waypoints.withIndex().forEach {
|
||||
color(0f, 0.3f, 0.7f, 0.5f)
|
||||
block(it.value)
|
||||
color(1f, 1f, 1f, 1f)
|
||||
if (TConfig.showIndex)
|
||||
withFacingThePlayer(it.value.toCenterPos()) {
|
||||
text(Text.literal(it.index.toString()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
orderedIndex %= waypoints.size
|
||||
val firstColor = Color.ofRGBA(0, 200, 40, 180)
|
||||
color(firstColor)
|
||||
tracer(waypoints[orderedIndex].toCenterPos(), lineWidth = 3f)
|
||||
waypoints.withIndex().toList()
|
||||
.wrappingWindow(orderedIndex, 3)
|
||||
.zip(
|
||||
listOf(
|
||||
firstColor,
|
||||
Color.ofRGBA(180, 200, 40, 150),
|
||||
Color.ofRGBA(180, 80, 20, 140),
|
||||
)
|
||||
)
|
||||
.reversed()
|
||||
.forEach { (waypoint, col) ->
|
||||
val (index, pos) = waypoint
|
||||
color(col)
|
||||
block(pos)
|
||||
color(1f, 1f, 1f, 1f)
|
||||
if (TConfig.showIndex)
|
||||
withFacingThePlayer(pos.toCenterPos()) {
|
||||
text(Text.literal(index.toString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onTick(event: TickEvent) {
|
||||
if (waypoints.isEmpty() || !ordered) return
|
||||
orderedIndex %= waypoints.size
|
||||
val p = MC.player?.pos ?: return
|
||||
if (TConfig.skipToNearest) {
|
||||
orderedIndex =
|
||||
(waypoints.withIndex().minBy { it.value.getSquaredDistance(p) }.index + 1) % waypoints.size
|
||||
} else {
|
||||
if (waypoints[orderedIndex].isWithinDistance(p, 3.0)) {
|
||||
orderedIndex = (orderedIndex + 1) % waypoints.size
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onProcessChat(it: ProcessChatEvent) {
|
||||
val matcher = temporaryPlayerWaypointMatcher.matcher(it.unformattedString)
|
||||
if (it.nameHeuristic != null && TConfig.tempWaypointDuration > 0.seconds && matcher.find()) {
|
||||
temporaryPlayerWaypointList[it.nameHeuristic] = TemporaryWaypoint(
|
||||
BlockPos(
|
||||
matcher.group(1).toInt(),
|
||||
matcher.group(2).toInt(),
|
||||
matcher.group(3).toInt(),
|
||||
),
|
||||
TimeMark.now()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onCommand(event: CommandEvent.SubCommand) {
|
||||
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("skip") {
|
||||
thenExecute {
|
||||
if (ordered && waypoints.isNotEmpty()) {
|
||||
orderedIndex = (orderedIndex + 1) % waypoints.size
|
||||
source.sendFeedback(Text.translatable("firmament.command.waypoint.skip"))
|
||||
} else {
|
||||
source.sendError(Text.translatable("firmament.command.waypoint.skip.error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
thenLiteral("remove") {
|
||||
thenArgument("index", IntegerArgumentType.integer(0)) { indexArg ->
|
||||
thenExecute {
|
||||
val index = get(indexArg)
|
||||
if (index in waypoints.indices) {
|
||||
waypoints.removeAt(index)
|
||||
source.sendFeedback(Text.stringifiedTranslatable(
|
||||
"firmament.command.waypoint.remove",
|
||||
index))
|
||||
} else {
|
||||
source.sendError(Text.stringifiedTranslatable("firmament.command.waypoint.remove.error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onRenderTemporaryWaypoints(event: WorldRenderLastEvent) {
|
||||
temporaryPlayerWaypointList.entries.removeIf { it.value.postedAt.passedTime() > TConfig.tempWaypointDuration }
|
||||
if (temporaryPlayerWaypointList.isEmpty()) return
|
||||
RenderInWorldContext.renderInWorld(event) {
|
||||
color(1f, 1f, 0f, 1f)
|
||||
temporaryPlayerWaypointList.forEach { (player, waypoint) ->
|
||||
block(waypoint.pos)
|
||||
}
|
||||
color(1f, 1f, 1f, 1f)
|
||||
temporaryPlayerWaypointList.forEach { (player, waypoint) ->
|
||||
val skin =
|
||||
MC.networkHandler?.listedPlayerListEntries?.find { it.profile.name == player }
|
||||
?.skinTextures
|
||||
?.texture
|
||||
withFacingThePlayer(waypoint.pos.toCenterPos()) {
|
||||
waypoint(waypoint.pos, Text.stringifiedTranslatable("firmament.waypoint.temporary", player))
|
||||
if (skin != null) {
|
||||
matrixStack.translate(0F, -20F, 0F)
|
||||
// Head front
|
||||
texture(
|
||||
skin, 16, 16,
|
||||
1 / 8f, 1 / 8f,
|
||||
2 / 8f, 2 / 8f,
|
||||
)
|
||||
// Head overlay
|
||||
texture(
|
||||
skin, 16, 16,
|
||||
5 / 8f, 1 / 8f,
|
||||
6 / 8f, 2 / 8f,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onWorldReady(event: WorldReadyEvent) {
|
||||
temporaryPlayerWaypointList.clear()
|
||||
}
|
||||
}
|
||||
|
||||
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(windowSize - (size - startIndex), 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
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user