Add /firm npcs command

This commit is contained in:
Linnea Gräf
2024-08-07 21:34:16 +02:00
parent 9b277bd897
commit b4f9ca21ef
21 changed files with 538 additions and 32 deletions

View File

@@ -23,7 +23,7 @@ notenoughanimations = "WaI2x21x"
devauth = "1.2.0" devauth = "1.2.0"
ktor = "2.3.0" ktor = "2.3.0"
neurepoparser = "1.4.0" neurepoparser = "1.5.0"
hotswap_agent = "1.4.2-SNAPSHOT" hotswap_agent = "1.4.2-SNAPSHOT"
mixinextras = "0.3.5" mixinextras = "0.3.5"
jarvis = "1.1.3" jarvis = "1.1.3"

View File

@@ -112,6 +112,7 @@ object Firmament {
@JvmStatic @JvmStatic
fun onClientInitialize() { fun onClientInitialize() {
FeatureManager.subscribeEvents()
var tick = 0 var tick = 0
ClientTickEvents.END_CLIENT_TICK.register(ClientTickEvents.EndTick { instance -> ClientTickEvents.END_CLIENT_TICK.register(ClientTickEvents.EndTick { instance ->
TickEvent.publish(TickEvent(tick++)) TickEvent.publish(TickEvent(tick++))

View File

@@ -0,0 +1,7 @@
package moe.nea.firmament.events
import io.github.moulberry.repo.NEURepository
data class ReloadRegistrationEvent(val repo: NEURepository) : FirmamentEvent() {
companion object : FirmamentEventBus<ReloadRegistrationEvent>()
}

View File

@@ -87,13 +87,12 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature
loadFeature(DebugView) loadFeature(DebugView)
} }
allFeatures.forEach { it.config } allFeatures.forEach { it.config }
subscribeEvents()
FeaturesInitializedEvent.publish(FeaturesInitializedEvent(allFeatures.toList())) FeaturesInitializedEvent.publish(FeaturesInitializedEvent(allFeatures.toList()))
hasAutoloaded = true hasAutoloaded = true
} }
} }
private fun subscribeEvents() { fun subscribeEvents() {
AllSubscriptions.provideSubscriptions { AllSubscriptions.provideSubscriptions {
subscribeSingleEvent(it) subscribeSingleEvent(it)
} }

View 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))
}
}
}
}

View 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)
}
}

View 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()
}
}
}

View 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)
}
}
}
}

View File

@@ -18,8 +18,9 @@ import io.github.notenoughupdates.moulconfig.observer.GetSetter
open class FirmButtonComponent( open class FirmButtonComponent(
child: GuiComponent, child: GuiComponent,
val isEnabled: GetSetter<Boolean> = GetSetter.constant(true), val isEnabled: GetSetter<Boolean> = GetSetter.constant(true),
val noBackground: Boolean = false,
val action: Runnable, val action: Runnable,
) : PanelComponent(child) { ) : PanelComponent(child, if (noBackground) 0 else 2, DefaultBackgroundRenderer.TRANSPARENT) {
/* TODO: make use of vanillas built in nine slicer */ /* TODO: make use of vanillas built in nine slicer */
val hoveredBg = val hoveredBg =
@@ -44,7 +45,6 @@ open class FirmButtonComponent(
.cornerUv(5 / 200F, 5 / 20F) .cornerUv(5 / 200F, 5 / 20F)
.mode(NinePatch.Mode.STRETCHING) .mode(NinePatch.Mode.STRETCHING)
.build() .build()
var isClicking = false var isClicking = false
override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean { override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean {
if (!isEnabled.get()) return false if (!isEnabled.get()) return false
@@ -54,12 +54,14 @@ open class FirmButtonComponent(
if (context.isHovered) { if (context.isHovered) {
action.run() action.run()
} }
blur()
return true return true
} }
} }
if (!context.isHovered) return false if (!context.isHovered) return false
if (mouseEvent !is MouseEvent.Click) return false if (mouseEvent !is MouseEvent.Click) return false
if (mouseEvent.mouseState && mouseEvent.mouseButton == 0) { if (mouseEvent.mouseState && mouseEvent.mouseButton == 0) {
requestFocus()
isClicking = true isClicking = true
return true return true
} }
@@ -73,10 +75,11 @@ open class FirmButtonComponent(
override fun render(context: GuiImmediateContext) { override fun render(context: GuiImmediateContext) {
context.renderContext.pushMatrix() context.renderContext.pushMatrix()
context.renderContext.drawNinePatch( if (!noBackground)
getBackground(context), context.renderContext.drawNinePatch(
0f, 0f, context.width, context.height getBackground(context),
) 0f, 0f, context.width, context.height
)
context.renderContext.translate(insets.toFloat(), insets.toFloat(), 0f) context.renderContext.translate(insets.toFloat(), insets.toFloat(), 0f)
element.render(getChildContext(context)) element.render(getChildContext(context))
context.renderContext.popMatrix() context.renderContext.popMatrix()

View File

@@ -0,0 +1,59 @@
package moe.nea.firmament.gui
import io.github.notenoughupdates.moulconfig.gui.GuiComponent
import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext
import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent
import io.github.notenoughupdates.moulconfig.gui.MouseEvent
import java.util.function.BiFunction
import java.util.function.Supplier
import kotlin.time.Duration
import moe.nea.firmament.util.TimeMark
class FirmHoverComponent(
val child: GuiComponent,
val hoverLines: Supplier<List<String>>,
val hoverDelay: Duration,
) : GuiComponent() {
override fun getWidth(): Int {
return child.width
}
override fun getHeight(): Int {
return child.height
}
override fun <T : Any?> foldChildren(
initial: T,
visitor: BiFunction<GuiComponent, T, T>
): T {
return visitor.apply(child, initial)
}
override fun render(context: GuiImmediateContext) {
if (context.isHovered && (permaHover || lastMouseMove.passedTime() > hoverDelay)) {
context.renderContext.scheduleDrawTooltip(hoverLines.get())
permaHover = true
} else {
permaHover = false
}
if (!context.isHovered) {
lastMouseMove = TimeMark.now()
}
child.render(context)
}
var permaHover = false
var lastMouseMove = TimeMark.farPast()
override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean {
if (mouseEvent is MouseEvent.Move) {
lastMouseMove = TimeMark.now()
}
return child.mouseEvent(mouseEvent, context)
}
override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean {
return child.keyboardEvent(event, context)
}
}

View File

@@ -0,0 +1,33 @@
package moe.nea.firmament.gui
import io.github.notenoughupdates.moulconfig.common.MyResourceLocation
import io.github.notenoughupdates.moulconfig.gui.GuiComponent
import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext
import java.util.function.Supplier
class ImageComponent(
private val width: Int,
private val height: Int,
val resourceLocation: Supplier<MyResourceLocation>,
val u1: Float,
val u2: Float,
val v1: Float,
val v2: Float,
) : GuiComponent() {
override fun getWidth(): Int {
return width
}
override fun getHeight(): Int {
return height
}
override fun render(context: GuiImmediateContext) {
context.renderContext.bindTexture(resourceLocation.get())
context.renderContext.drawTexturedRect(
0f, 0f,
context.width.toFloat(), context.height.toFloat(),
u1, v1, u2, v2
)
}
}

View File

@@ -0,0 +1,18 @@
package moe.nea.firmament.gui
import io.github.notenoughupdates.moulconfig.gui.GuiComponent
import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext
class TickComponent(val onTick: Runnable) : GuiComponent() {
override fun getWidth(): Int {
return 0
}
override fun getHeight(): Int {
return 0
}
override fun render(context: GuiImmediateContext) {
onTick.run()
}
}

View File

@@ -47,8 +47,7 @@ data class SavedKeyBinding(
|| InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_SUPER) || InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_SUPER)
} else InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_CONTROL) } else InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_CONTROL)
|| InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_CONTROL) || InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_CONTROL)
val shift = InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_SHIFT) val shift = isShiftDown()
|| InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_SHIFT)
val alt = InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_ALT) val alt = InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_ALT)
|| InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_ALT) || InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_ALT)
var mods = 0 var mods = 0
@@ -57,6 +56,11 @@ data class SavedKeyBinding(
if (alt) mods = mods or GLFW.GLFW_MOD_ALT if (alt) mods = mods or GLFW.GLFW_MOD_ALT
return mods return mods
} }
private val h get() = MC.window.handle
fun isShiftDown() = InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_SHIFT)
|| InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_SHIFT)
} }
fun isPressed(atLeast: Boolean = false): Boolean { fun isPressed(atLeast: Boolean = false): Boolean {
@@ -69,8 +73,7 @@ data class SavedKeyBinding(
|| InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_SUPER) || InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_SUPER)
} else InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_CONTROL) } else InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_CONTROL)
|| InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_CONTROL) || InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_CONTROL)
val shift = InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_SHIFT) val shift = isShiftDown()
|| InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_SHIFT)
val alt = InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_ALT) val alt = InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_ALT)
|| InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_ALT) || InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_ALT)
if (atLeast) if (atLeast)

View File

@@ -7,7 +7,6 @@
package moe.nea.firmament.repo package moe.nea.firmament.repo
import io.github.moulberry.repo.NEURecipeCache
import io.github.moulberry.repo.NEURepository import io.github.moulberry.repo.NEURepository
import io.github.moulberry.repo.NEURepositoryException import io.github.moulberry.repo.NEURepositoryException
import io.github.moulberry.repo.data.NEUItem import io.github.moulberry.repo.data.NEUItem
@@ -20,6 +19,7 @@ import net.minecraft.network.packet.s2c.play.SynchronizeRecipesS2CPacket
import net.minecraft.text.Text import net.minecraft.text.Text
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
import moe.nea.firmament.Firmament.logger import moe.nea.firmament.Firmament.logger
import moe.nea.firmament.events.ReloadRegistrationEvent
import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.rei.PetData import moe.nea.firmament.rei.PetData
import moe.nea.firmament.util.MinecraftDispatcher import moe.nea.firmament.util.MinecraftDispatcher
@@ -57,6 +57,7 @@ object RepoManager {
registerReloadListener(ItemCache) registerReloadListener(ItemCache)
registerReloadListener(ExpLadders) registerReloadListener(ExpLadders)
registerReloadListener(ItemNameLookup) registerReloadListener(ItemNameLookup)
ReloadRegistrationEvent.publish(ReloadRegistrationEvent(this))
registerReloadListener { registerReloadListener {
Firmament.coroutineScope.launch(MinecraftDispatcher) { Firmament.coroutineScope.launch(MinecraftDispatcher) {
if (!trySendClientboundUpdateRecipesPacket()) { if (!trySendClientboundUpdateRecipesPacket()) {
@@ -69,9 +70,10 @@ object RepoManager {
val essenceRecipeProvider = EssenceRecipeProvider() val essenceRecipeProvider = EssenceRecipeProvider()
val recipeCache = BetterRepoRecipeCache(essenceRecipeProvider) val recipeCache = BetterRepoRecipeCache(essenceRecipeProvider)
init { init {
neuRepo.registerReloadListener(essenceRecipeProvider) neuRepo.registerReloadListener(essenceRecipeProvider)
neuRepo.registerReloadListener(recipeCache) neuRepo.registerReloadListener(recipeCache)
} }
fun getAllRecipes() = neuRepo.items.items.values.asSequence().flatMap { it.recipes } fun getAllRecipes() = neuRepo.items.items.values.asSequence().flatMap { it.recipes }
@@ -110,7 +112,9 @@ object RepoManager {
fun reload() { fun reload() {
try { try {
ItemCache.ReloadProgressHud.reportProgress("Reloading from Disk", 0, -1) // TODO: replace with a proper boundy bar ItemCache.ReloadProgressHud.reportProgress("Reloading from Disk",
0,
-1) // TODO: replace with a proper boundy bar
ItemCache.ReloadProgressHud.isEnabled = true ItemCache.ReloadProgressHud.isEnabled = true
neuRepo.reload() neuRepo.reload()
} catch (exc: NEURepositoryException) { } catch (exc: NEURepositoryException) {

View File

@@ -18,13 +18,19 @@ import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader
import io.github.notenoughupdates.moulconfig.xml.XMLUniverse import io.github.notenoughupdates.moulconfig.xml.XMLUniverse
import io.github.notenoughupdates.moulconfig.xml.XSDGenerator import io.github.notenoughupdates.moulconfig.xml.XSDGenerator
import java.io.File import java.io.File
import java.util.function.Supplier
import javax.xml.namespace.QName import javax.xml.namespace.QName
import me.shedaniel.math.Color import me.shedaniel.math.Color
import org.w3c.dom.Element import org.w3c.dom.Element
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import net.minecraft.client.gui.screen.Screen import net.minecraft.client.gui.screen.Screen
import moe.nea.firmament.gui.BarComponent import moe.nea.firmament.gui.BarComponent
import moe.nea.firmament.gui.FirmButtonComponent import moe.nea.firmament.gui.FirmButtonComponent
import moe.nea.firmament.gui.FirmHoverComponent
import moe.nea.firmament.gui.FixedComponent import moe.nea.firmament.gui.FixedComponent
import moe.nea.firmament.gui.ImageComponent
import moe.nea.firmament.gui.TickComponent
object MoulConfigUtils { object MoulConfigUtils {
val firmUrl = "http://firmament.nea.moe/moulconfig" val firmUrl = "http://firmament.nea.moe/moulconfig"
@@ -69,6 +75,31 @@ object MoulConfigUtils {
return mapOf("progress" to true, "total" to true, "emptyColor" to true, "fillColor" to true) return mapOf("progress" to true, "total" to true, "emptyColor" to true, "fillColor" to true)
} }
}) })
uni.registerLoader(object : XMLGuiLoader.Basic<FirmHoverComponent> {
override fun createInstance(context: XMLContext<*>, element: Element): FirmHoverComponent {
return FirmHoverComponent(
context.getChildFragment(element),
context.getPropertyFromAttribute(element, QName("lines"), List::class.java) as Supplier<List<String>>,
context.getPropertyFromAttribute(element, QName("delay"), Duration::class.java, 0.6.seconds),
)
}
override fun getName(): QName {
return QName(firmUrl, "Hover")
}
override fun getChildCount(): ChildCount {
return ChildCount.ONE
}
override fun getAttributeNames(): Map<String, Boolean> {
return mapOf(
"lines" to true,
"delay" to false,
)
}
})
uni.registerLoader(object : XMLGuiLoader.Basic<FirmButtonComponent> { uni.registerLoader(object : XMLGuiLoader.Basic<FirmButtonComponent> {
override fun getName(): QName { override fun getName(): QName {
return QName(firmUrl, "Button") return QName(firmUrl, "Button")
@@ -79,6 +110,7 @@ object MoulConfigUtils {
context.getChildFragment(element), context.getChildFragment(element),
context.getPropertyFromAttribute(element, QName("enabled"), Boolean::class.java) context.getPropertyFromAttribute(element, QName("enabled"), Boolean::class.java)
?: GetSetter.constant(true), ?: GetSetter.constant(true),
context.getPropertyFromAttribute(element, QName("noBackground"), Boolean::class.java, false),
context.getMethodFromAttribute(element, QName("onClick")), context.getMethodFromAttribute(element, QName("onClick")),
) )
} }
@@ -88,7 +120,56 @@ object MoulConfigUtils {
} }
override fun getAttributeNames(): Map<String, Boolean> { override fun getAttributeNames(): Map<String, Boolean> {
return mapOf("onClick" to true, "enabled" to false) return mapOf("onClick" to true, "enabled" to false, "noBackground" to false)
}
})
uni.registerLoader(object : XMLGuiLoader.Basic<ImageComponent> {
override fun createInstance(context: XMLContext<*>, element: Element): ImageComponent {
return ImageComponent(
context.getPropertyFromAttribute(element, QName("width"), Int::class.java)!!.get(),
context.getPropertyFromAttribute(element, QName("height"), Int::class.java)!!.get(),
context.getPropertyFromAttribute(element, QName("resource"), MyResourceLocation::class.java)!!,
context.getPropertyFromAttribute(element, QName("u1"), Float::class.java, 0f),
context.getPropertyFromAttribute(element, QName("u2"), Float::class.java, 1f),
context.getPropertyFromAttribute(element, QName("v1"), Float::class.java, 0f),
context.getPropertyFromAttribute(element, QName("v2"), Float::class.java, 1f),
)
}
override fun getName(): QName {
return QName(firmUrl, "Image")
}
override fun getChildCount(): ChildCount {
return ChildCount.NONE
}
override fun getAttributeNames(): Map<String, Boolean> {
return mapOf(
"width" to true, "height" to true,
"resource" to true,
"u1" to false,
"u2" to false,
"v1" to false,
"v2" to false,
)
}
})
uni.registerLoader(object : XMLGuiLoader.Basic<TickComponent> {
override fun createInstance(context: XMLContext<*>, element: Element): TickComponent {
return TickComponent(context.getMethodFromAttribute(element, QName("tick")))
}
override fun getName(): QName {
return QName(firmUrl, "Tick")
}
override fun getChildCount(): ChildCount {
return ChildCount.NONE
}
override fun getAttributeNames(): Map<String, Boolean> {
return mapOf("tick" to true)
} }
}) })
uni.registerLoader(object : XMLGuiLoader.Basic<FixedComponent> { uni.registerLoader(object : XMLGuiLoader.Basic<FixedComponent> {

View File

@@ -31,8 +31,8 @@ object WarpUtil {
private var lastAttemptedWarp = "" private var lastAttemptedWarp = ""
private var lastWarpAttempt = TimeMark.farPast() private var lastWarpAttempt = TimeMark.farPast()
fun findNearestWarp(island: SkyBlockIsland, pos: Position): Islands.Warp? { fun findNearestWarp(island: SkyBlockIsland, pos: Position): Islands.Warp? {
return warps.minByOrNull { return warps.asSequence().filter { it.mode == island.locrawMode }.minByOrNull {
if (island.locrawMode != it.mode || (DConfig.data?.excludedWarps?.contains(it.warp) == true)) { if (DConfig.data?.excludedWarps?.contains(it.warp) == true) {
return@minByOrNull Double.MAX_VALUE return@minByOrNull Double.MAX_VALUE
} else { } else {
return@minByOrNull squaredDist(pos, it) return@minByOrNull squaredDist(pos, it)
@@ -48,8 +48,11 @@ object WarpUtil {
} }
fun teleportToNearestWarp(island: SkyBlockIsland, pos: Position) { fun teleportToNearestWarp(island: SkyBlockIsland, pos: Position) {
val nearestWarp = findNearestWarp(island, pos) ?: return val nearestWarp = findNearestWarp(island, pos)
if (nearestWarp == null) {
MC.sendChat(Text.literal("Could not find an unlocked warp in ${island.userFriendlyName}"))
return
}
if (island == SBData.skyblockLocation if (island == SBData.skyblockLocation
&& sqrt(squaredDist(pos, nearestWarp)) > 1.1 * sqrt(squaredDist((MC.player ?: return).pos, nearestWarp)) && sqrt(squaredDist(pos, nearestWarp)) > 1.1 * sqrt(squaredDist((MC.player ?: return).pos, nearestWarp))
) { ) {

View File

@@ -37,7 +37,8 @@ class FacingThePlayerContext(val worldContext: RenderInWorldContext) {
fun text( fun text(
vararg texts: Text, vararg texts: Text,
verticalAlign: RenderInWorldContext.VerticalAlign = RenderInWorldContext.VerticalAlign.CENTER verticalAlign: RenderInWorldContext.VerticalAlign = RenderInWorldContext.VerticalAlign.CENTER,
background: Int = 0x70808080,
) { ) {
assertTrueOr(texts.isNotEmpty()) { return@text } assertTrueOr(texts.isNotEmpty()) { return@text }
for ((index, text) in texts.withIndex()) { for ((index, text) in texts.withIndex()) {
@@ -47,14 +48,14 @@ class FacingThePlayerContext(val worldContext: RenderInWorldContext) {
val vertexConsumer: VertexConsumer = val vertexConsumer: VertexConsumer =
worldContext.vertexConsumers.getBuffer(RenderLayer.getTextBackgroundSeeThrough()) worldContext.vertexConsumers.getBuffer(RenderLayer.getTextBackgroundSeeThrough())
val matrix4f = worldContext.matrixStack.peek().positionMatrix val matrix4f = worldContext.matrixStack.peek().positionMatrix
vertexConsumer.vertex(matrix4f, -1.0f, -1.0f, 0.0f).color(0x70808080) vertexConsumer.vertex(matrix4f, -1.0f, -1.0f, 0.0f).color(background)
.light(LightmapTextureManager.MAX_BLOCK_LIGHT_COORDINATE).next() .light(LightmapTextureManager.MAX_BLOCK_LIGHT_COORDINATE).next()
vertexConsumer.vertex(matrix4f, -1.0f, MC.font.fontHeight.toFloat(), 0.0f).color(0x70808080) vertexConsumer.vertex(matrix4f, -1.0f, MC.font.fontHeight.toFloat(), 0.0f).color(background)
.light(LightmapTextureManager.MAX_BLOCK_LIGHT_COORDINATE).next() .light(LightmapTextureManager.MAX_BLOCK_LIGHT_COORDINATE).next()
vertexConsumer.vertex(matrix4f, width.toFloat(), MC.font.fontHeight.toFloat(), 0.0f) vertexConsumer.vertex(matrix4f, width.toFloat(), MC.font.fontHeight.toFloat(), 0.0f)
.color(0x70808080) .color(background)
.light(LightmapTextureManager.MAX_BLOCK_LIGHT_COORDINATE).next() .light(LightmapTextureManager.MAX_BLOCK_LIGHT_COORDINATE).next()
vertexConsumer.vertex(matrix4f, width.toFloat(), -1.0f, 0.0f).color(0x70808080) vertexConsumer.vertex(matrix4f, width.toFloat(), -1.0f, 0.0f).color(background)
.light(LightmapTextureManager.MAX_BLOCK_LIGHT_COORDINATE).next() .light(LightmapTextureManager.MAX_BLOCK_LIGHT_COORDINATE).next()
worldContext.matrixStack.translate(0F, 0F, 0.01F) worldContext.matrixStack.translate(0F, 0F, 0.01F)

View File

@@ -83,11 +83,12 @@ class RenderInWorldContext private constructor(
} }
} }
fun waypoint(position: BlockPos, label: Text) { fun waypoint(position: BlockPos, vararg label: Text) {
text( text(
position.toCenterPos(), position.toCenterPos(),
label, *label,
Text.literal("§e${FirmFormatters.formatDistance(MC.player?.pos?.distanceTo(position.toCenterPos()) ?: 42069.0)}") Text.literal("§e${FirmFormatters.formatDistance(MC.player?.pos?.distanceTo(position.toCenterPos()) ?: 42069.0)}"),
background = 0xAA202020.toInt()
) )
} }
@@ -123,9 +124,9 @@ class RenderInWorldContext private constructor(
} }
} }
fun text(position: Vec3d, vararg texts: Text, verticalAlign: VerticalAlign = VerticalAlign.CENTER) { fun text(position: Vec3d, vararg texts: Text, verticalAlign: VerticalAlign = VerticalAlign.CENTER, background: Int = 0x70808080) {
withFacingThePlayer(position) { withFacingThePlayer(position) {
text(*texts, verticalAlign = verticalAlign) text(*texts, verticalAlign = verticalAlign, background = background)
} }
} }

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
SPDX-License-Identifier: GPL-3.0-or-later
-->
<Root xmlns="http://notenoughupdates.org/moulconfig" xmlns:firm="http://firmament.nea.moe/moulconfig">
<Center>
<Panel background="VANILLA" insets="5">
<Column>
<Center>
<TextField value="@search" width="200"/>
</Center>
<firm:Tick tick="@tick"/>
<Spacer height="5"/>
<Panel background="TRANSPARENT" insets="4">
<ScrollPanel width="200" height="300">
<Array data="@results">
<Row>
<Text text="@name" width="180"/>
<firm:Hover lines="Click to set this waypoint as your destination;Shift-Click to warp to the nearest warp point and set this as your destination">
<firm:Button onClick="@click" noBackground="true">
<When condition="@isSelected">
<firm:Image resource="firmament:textures/gui/waypoint_selected.png"
width="16"
height="16"/>
<firm:Image resource="firmament:textures/gui/waypoint_unselected.png"
width="16"
height="16"/>
</When>
</firm:Button>
</firm:Hover>
</Row>
</Array>
</ScrollPanel>
</Panel>
</Column>
</Panel>
</Center>
</Root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B