Refactor source layout
Introduce compat source sets and move all kotlin sources to the main directory [no changelog]
This commit is contained in:
125
src/main/kotlin/gui/BarComponent.kt
Normal file
125
src/main/kotlin/gui/BarComponent.kt
Normal file
@@ -0,0 +1,125 @@
|
||||
|
||||
package moe.nea.firmament.gui
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import io.github.notenoughupdates.moulconfig.common.MyResourceLocation
|
||||
import io.github.notenoughupdates.moulconfig.common.RenderContext
|
||||
import io.github.notenoughupdates.moulconfig.gui.GuiComponent
|
||||
import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext
|
||||
import io.github.notenoughupdates.moulconfig.observer.GetSetter
|
||||
import io.github.notenoughupdates.moulconfig.platform.ModernRenderContext
|
||||
import me.shedaniel.math.Color
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.util.Identifier
|
||||
import moe.nea.firmament.Firmament
|
||||
|
||||
class BarComponent(
|
||||
val progress: GetSetter<Double>, val total: GetSetter<Double>,
|
||||
val fillColor: Color,
|
||||
val emptyColor: Color,
|
||||
) : GuiComponent() {
|
||||
override fun getWidth(): Int {
|
||||
return 80
|
||||
}
|
||||
|
||||
override fun getHeight(): Int {
|
||||
return 8
|
||||
}
|
||||
|
||||
data class Texture(
|
||||
val identifier: Identifier,
|
||||
val u1: Float, val v1: Float,
|
||||
val u2: Float, val v2: Float,
|
||||
) {
|
||||
fun draw(context: DrawContext, x: Int, y: Int, width: Int, height: Int, color: Color) {
|
||||
context.drawTexturedQuad(
|
||||
identifier,
|
||||
x, y, x + width, x + height, 0,
|
||||
u1, u2, v1, v2,
|
||||
color.red / 255F,
|
||||
color.green / 255F,
|
||||
color.blue / 255F,
|
||||
color.alpha / 255F,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val resource = Firmament.identifier("textures/gui/bar.png")
|
||||
val left = Texture(resource, 0 / 64F, 0 / 64F, 4 / 64F, 8 / 64F)
|
||||
val middle = Texture(resource, 4 / 64F, 0 / 64F, 8 / 64F, 8 / 64F)
|
||||
val right = Texture(resource, 8 / 64F, 0 / 64F, 12 / 64F, 8 / 64F)
|
||||
val segmentOverlay = Texture(resource, 12 / 64F, 0 / 64F, 15 / 64F, 8 / 64F)
|
||||
}
|
||||
|
||||
private fun drawSection(
|
||||
context: DrawContext,
|
||||
texture: Texture,
|
||||
x: Int,
|
||||
y: Int,
|
||||
width: Int,
|
||||
sectionStart: Double,
|
||||
sectionEnd: Double
|
||||
) {
|
||||
if (sectionEnd < progress.get() && width == 4) {
|
||||
texture.draw(context, x, y, 4, 8, fillColor)
|
||||
return
|
||||
}
|
||||
if (sectionStart > progress.get() && width == 4) {
|
||||
texture.draw(context, x, y, 4, 8, emptyColor)
|
||||
return
|
||||
}
|
||||
val increasePerPixel = (sectionEnd - sectionStart) / width
|
||||
var valueAtPixel = sectionStart
|
||||
for (i in (0 until width)) {
|
||||
val newTex =
|
||||
Texture(texture.identifier, texture.u1 + i / 64F, texture.v1, texture.u1 + (i + 1) / 64F, texture.v2)
|
||||
newTex.draw(
|
||||
context, x + i, y, 1, 8,
|
||||
if (valueAtPixel < progress.get()) fillColor else emptyColor
|
||||
)
|
||||
valueAtPixel += increasePerPixel
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(context: GuiImmediateContext) {
|
||||
val renderContext = (context.renderContext as ModernRenderContext).drawContext
|
||||
var i = 0
|
||||
val x = 0
|
||||
val y = 0
|
||||
while (i < context.width - 4) {
|
||||
drawSection(
|
||||
renderContext,
|
||||
if (i == 0) left else middle,
|
||||
x + i, y,
|
||||
(context.width - (i + 4)).coerceAtMost(4),
|
||||
i * total.get() / context.width, (i + 4) * total.get() / context.width
|
||||
)
|
||||
i += 4
|
||||
}
|
||||
drawSection(
|
||||
renderContext,
|
||||
right,
|
||||
x + context.width - 4,
|
||||
y,
|
||||
4,
|
||||
(context.width - 4) * total.get() / context.width,
|
||||
total.get()
|
||||
)
|
||||
RenderSystem.setShaderColor(1F, 1F, 1F, 1F)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun Identifier.toMoulConfig(): MyResourceLocation {
|
||||
return MyResourceLocation(this.namespace, this.path)
|
||||
}
|
||||
|
||||
fun RenderContext.color(color: Color) {
|
||||
color(color.red, color.green, color.blue, color.alpha)
|
||||
}
|
||||
|
||||
fun RenderContext.color(red: Int, green: Int, blue: Int, alpha: Int) {
|
||||
color(red / 255f, green / 255f, blue / 255f, alpha / 255f)
|
||||
}
|
||||
81
src/main/kotlin/gui/FirmButtonComponent.kt
Normal file
81
src/main/kotlin/gui/FirmButtonComponent.kt
Normal file
@@ -0,0 +1,81 @@
|
||||
|
||||
package moe.nea.firmament.gui
|
||||
|
||||
import io.github.notenoughupdates.moulconfig.common.MyResourceLocation
|
||||
import io.github.notenoughupdates.moulconfig.deps.libninepatch.NinePatch
|
||||
import io.github.notenoughupdates.moulconfig.gui.GuiComponent
|
||||
import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext
|
||||
import io.github.notenoughupdates.moulconfig.gui.MouseEvent
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.PanelComponent
|
||||
import io.github.notenoughupdates.moulconfig.observer.GetSetter
|
||||
|
||||
|
||||
open class FirmButtonComponent(
|
||||
child: GuiComponent,
|
||||
val isEnabled: GetSetter<Boolean> = GetSetter.constant(true),
|
||||
val noBackground: Boolean = false,
|
||||
val action: Runnable,
|
||||
) : PanelComponent(child, if (noBackground) 0 else 2, DefaultBackgroundRenderer.TRANSPARENT) {
|
||||
|
||||
/* TODO: make use of vanillas built in nine slicer */
|
||||
val hoveredBg =
|
||||
NinePatch.builder(MyResourceLocation("minecraft", "textures/gui/sprites/widget/button_highlighted.png"))
|
||||
.cornerSize(5)
|
||||
.cornerUv(5 / 200F, 5 / 20F)
|
||||
.mode(NinePatch.Mode.STRETCHING)
|
||||
.build()
|
||||
val unhoveredBg = NinePatch.builder(MyResourceLocation("minecraft", "textures/gui/sprites/widget/button.png"))
|
||||
.cornerSize(5)
|
||||
.cornerUv(5 / 200F, 5 / 20F)
|
||||
.mode(NinePatch.Mode.STRETCHING)
|
||||
.build()
|
||||
val disabledBg =
|
||||
NinePatch.builder(MyResourceLocation("minecraft", "textures/gui/sprites/widget/button_disabled.png"))
|
||||
.cornerSize(5)
|
||||
.cornerUv(5 / 200F, 5 / 20F)
|
||||
.mode(NinePatch.Mode.STRETCHING)
|
||||
.build()
|
||||
val activeBg = NinePatch.builder(MyResourceLocation("firmament", "textures/gui/sprites/widget/button_active.png"))
|
||||
.cornerSize(5)
|
||||
.cornerUv(5 / 200F, 5 / 20F)
|
||||
.mode(NinePatch.Mode.STRETCHING)
|
||||
.build()
|
||||
var isClicking = false
|
||||
override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean {
|
||||
if (!isEnabled.get()) return false
|
||||
if (isClicking) {
|
||||
if (mouseEvent is MouseEvent.Click && !mouseEvent.mouseState && mouseEvent.mouseButton == 0) {
|
||||
isClicking = false
|
||||
if (context.isHovered) {
|
||||
action.run()
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
if (!context.isHovered) return false
|
||||
if (mouseEvent !is MouseEvent.Click) return false
|
||||
if (mouseEvent.mouseState && mouseEvent.mouseButton == 0) {
|
||||
requestFocus()
|
||||
isClicking = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
open fun getBackground(context: GuiImmediateContext): NinePatch<MyResourceLocation> =
|
||||
if (!isEnabled.get()) disabledBg
|
||||
else if (context.isHovered || isClicking) hoveredBg
|
||||
else unhoveredBg
|
||||
|
||||
override fun render(context: GuiImmediateContext) {
|
||||
context.renderContext.pushMatrix()
|
||||
if (!noBackground)
|
||||
context.renderContext.drawNinePatch(
|
||||
getBackground(context),
|
||||
0f, 0f, context.width, context.height
|
||||
)
|
||||
context.renderContext.translate(insets.toFloat(), insets.toFloat(), 0f)
|
||||
element.render(getChildContext(context))
|
||||
context.renderContext.popMatrix()
|
||||
}
|
||||
}
|
||||
59
src/main/kotlin/gui/FirmHoverComponent.kt
Normal file
59
src/main/kotlin/gui/FirmHoverComponent.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
38
src/main/kotlin/gui/FixedComponent.kt
Normal file
38
src/main/kotlin/gui/FixedComponent.kt
Normal file
@@ -0,0 +1,38 @@
|
||||
|
||||
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 io.github.notenoughupdates.moulconfig.observer.GetSetter
|
||||
import java.util.function.BiFunction
|
||||
|
||||
class FixedComponent(
|
||||
val fixedWidth: GetSetter<Int>?,
|
||||
val fixedHeight: GetSetter<Int>?,
|
||||
val component: GuiComponent,
|
||||
) : GuiComponent() {
|
||||
override fun getWidth(): Int = fixedWidth?.get() ?: component.width
|
||||
|
||||
override fun getHeight(): Int = fixedHeight?.get() ?: component.height
|
||||
|
||||
override fun <T : Any?> foldChildren(initial: T, visitor: BiFunction<GuiComponent, T, T>): T {
|
||||
return visitor.apply(component, initial)
|
||||
}
|
||||
|
||||
fun fixContext(context: GuiImmediateContext): GuiImmediateContext =
|
||||
context.translated(0, 0, width, height)
|
||||
|
||||
override fun render(context: GuiImmediateContext) {
|
||||
component.render(fixContext(context))
|
||||
}
|
||||
|
||||
override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean {
|
||||
return component.mouseEvent(mouseEvent, fixContext(context))
|
||||
}
|
||||
|
||||
override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean {
|
||||
return component.keyboardEvent(event, fixContext(context))
|
||||
}
|
||||
}
|
||||
33
src/main/kotlin/gui/ImageComponent.kt
Normal file
33
src/main/kotlin/gui/ImageComponent.kt
Normal 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
|
||||
)
|
||||
}
|
||||
}
|
||||
18
src/main/kotlin/gui/TickComponent.kt
Normal file
18
src/main/kotlin/gui/TickComponent.kt
Normal 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()
|
||||
}
|
||||
}
|
||||
46
src/main/kotlin/gui/config/AllConfigsGui.kt
Normal file
46
src/main/kotlin/gui/config/AllConfigsGui.kt
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.gui.config
|
||||
|
||||
import io.github.notenoughupdates.moulconfig.observer.ObservableList
|
||||
import io.github.notenoughupdates.moulconfig.xml.Bind
|
||||
import net.minecraft.client.gui.screen.Screen
|
||||
import net.minecraft.text.Text
|
||||
import moe.nea.firmament.features.FeatureManager
|
||||
import moe.nea.firmament.repo.RepoManager
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.MoulConfigUtils
|
||||
import moe.nea.firmament.util.ScreenUtil.setScreenLater
|
||||
|
||||
object AllConfigsGui {
|
||||
|
||||
val allConfigs
|
||||
get() = listOf(
|
||||
RepoManager.Config
|
||||
) + FeatureManager.allFeatures.mapNotNull { it.config }
|
||||
|
||||
fun <T> List<T>.toObservableList(): ObservableList<T> = ObservableList(this)
|
||||
|
||||
class MainMapping(val allConfigs: List<ManagedConfig>) {
|
||||
@get:Bind("configs")
|
||||
val configs = allConfigs.map { EntryMapping(it) }.toObservableList()
|
||||
|
||||
class EntryMapping(val config: ManagedConfig) {
|
||||
@Bind
|
||||
fun name() = Text.translatable("firmament.config.${config.name}").string
|
||||
|
||||
@Bind
|
||||
fun openEditor() {
|
||||
config.showConfigEditor(MC.screen)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun makeScreen(parent: Screen? = null): Screen {
|
||||
return MoulConfigUtils.loadScreen("config/main", MainMapping(allConfigs), parent)
|
||||
}
|
||||
|
||||
fun showAllGuis() {
|
||||
setScreenLater(makeScreen())
|
||||
}
|
||||
}
|
||||
37
src/main/kotlin/gui/config/BooleanHandler.kt
Normal file
37
src/main/kotlin/gui/config/BooleanHandler.kt
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.gui.config
|
||||
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.CenterComponent
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.SwitchComponent
|
||||
import io.github.notenoughupdates.moulconfig.observer.GetSetter
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.boolean
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
|
||||
class BooleanHandler(val config: ManagedConfig) : ManagedConfig.OptionHandler<Boolean> {
|
||||
override fun toJson(element: Boolean): JsonElement? {
|
||||
return JsonPrimitive(element)
|
||||
}
|
||||
|
||||
override fun fromJson(element: JsonElement): Boolean {
|
||||
return element.jsonPrimitive.boolean
|
||||
}
|
||||
|
||||
override fun emitGuiElements(opt: ManagedOption<Boolean>, guiAppender: GuiAppender) {
|
||||
guiAppender.appendLabeledRow(
|
||||
opt.labelText,
|
||||
CenterComponent(SwitchComponent(object : GetSetter<Boolean> {
|
||||
override fun get(): Boolean {
|
||||
return opt.get()
|
||||
}
|
||||
|
||||
override fun set(newValue: Boolean) {
|
||||
opt.set(newValue)
|
||||
config.save()
|
||||
}
|
||||
}, 200)
|
||||
))
|
||||
}
|
||||
}
|
||||
24
src/main/kotlin/gui/config/ClickHandler.kt
Normal file
24
src/main/kotlin/gui/config/ClickHandler.kt
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.gui.config
|
||||
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import moe.nea.firmament.gui.FirmButtonComponent
|
||||
|
||||
class ClickHandler(val config: ManagedConfig, val runnable: () -> Unit) : ManagedConfig.OptionHandler<Unit> {
|
||||
override fun toJson(element: Unit): JsonElement? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun fromJson(element: JsonElement) {}
|
||||
|
||||
override fun emitGuiElements(opt: ManagedOption<Unit>, guiAppender: GuiAppender) {
|
||||
guiAppender.appendLabeledRow(
|
||||
opt.labelText,
|
||||
FirmButtonComponent(
|
||||
TextComponent(opt.labelText.string),
|
||||
action = runnable),
|
||||
)
|
||||
}
|
||||
}
|
||||
58
src/main/kotlin/gui/config/DurationHandler.kt
Normal file
58
src/main/kotlin/gui/config/DurationHandler.kt
Normal file
@@ -0,0 +1,58 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.gui.config
|
||||
|
||||
import io.github.notenoughupdates.moulconfig.common.IMinecraft
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.RowComponent
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.SliderComponent
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
|
||||
import io.github.notenoughupdates.moulconfig.observer.GetSetter
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlinx.serialization.json.long
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.DurationUnit
|
||||
import kotlin.time.toDuration
|
||||
import net.minecraft.text.Text
|
||||
import moe.nea.firmament.util.FirmFormatters
|
||||
|
||||
class DurationHandler(val config: ManagedConfig, val min: Duration, val max: Duration) :
|
||||
ManagedConfig.OptionHandler<Duration> {
|
||||
override fun toJson(element: Duration): JsonElement? {
|
||||
return JsonPrimitive(element.inWholeMilliseconds)
|
||||
}
|
||||
|
||||
override fun fromJson(element: JsonElement): Duration {
|
||||
return element.jsonPrimitive.long.toDuration(DurationUnit.MILLISECONDS)
|
||||
}
|
||||
|
||||
override fun emitGuiElements(opt: ManagedOption<Duration>, guiAppender: GuiAppender) {
|
||||
guiAppender.appendLabeledRow(
|
||||
opt.labelText,
|
||||
RowComponent(
|
||||
TextComponent(IMinecraft.instance.defaultFontRenderer,
|
||||
{ FirmFormatters.formatTimespan(opt.value) },
|
||||
40,
|
||||
TextComponent.TextAlignment.CENTER,
|
||||
true,
|
||||
false),
|
||||
SliderComponent(
|
||||
object : GetSetter<Float> {
|
||||
override fun get(): Float {
|
||||
return opt.value.toDouble(DurationUnit.SECONDS).toFloat()
|
||||
}
|
||||
|
||||
override fun set(newValue: Float) {
|
||||
opt.value = newValue.toDouble().toDuration(DurationUnit.SECONDS)
|
||||
}
|
||||
},
|
||||
min.toDouble(DurationUnit.SECONDS).toFloat(),
|
||||
max.toDouble(DurationUnit.SECONDS).toFloat(),
|
||||
0.1F,
|
||||
130
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
}
|
||||
40
src/main/kotlin/gui/config/GuiAppender.kt
Normal file
40
src/main/kotlin/gui/config/GuiAppender.kt
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.gui.config
|
||||
|
||||
import io.github.notenoughupdates.moulconfig.gui.GuiComponent
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.RowComponent
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
|
||||
import io.github.notenoughupdates.moulconfig.observer.GetSetter
|
||||
import net.minecraft.client.gui.screen.Screen
|
||||
import net.minecraft.text.Text
|
||||
import moe.nea.firmament.gui.FixedComponent
|
||||
|
||||
class GuiAppender(val width: Int, val screenAccessor: () -> Screen) {
|
||||
val panel = mutableListOf<GuiComponent>()
|
||||
internal val reloadables = mutableListOf<(() -> Unit)>()
|
||||
|
||||
fun onReload(reloadable: () -> Unit) {
|
||||
reloadables.add(reloadable)
|
||||
}
|
||||
|
||||
fun appendLabeledRow(label: Text, right: GuiComponent) {
|
||||
appendSplitRow(
|
||||
TextComponent(label.string),
|
||||
right
|
||||
)
|
||||
}
|
||||
|
||||
fun appendSplitRow(left: GuiComponent, right: GuiComponent) {
|
||||
// TODO: make this more dynamic
|
||||
// i could just make a component that allows for using half the available size
|
||||
appendFullRow(RowComponent(
|
||||
FixedComponent(GetSetter.constant(width / 2), null, left),
|
||||
FixedComponent(GetSetter.constant(width / 2), null, right),
|
||||
))
|
||||
}
|
||||
|
||||
fun appendFullRow(widget: GuiComponent) {
|
||||
panel.add(widget)
|
||||
}
|
||||
}
|
||||
39
src/main/kotlin/gui/config/HudMetaHandler.kt
Normal file
39
src/main/kotlin/gui/config/HudMetaHandler.kt
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.gui.config
|
||||
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.decodeFromJsonElement
|
||||
import kotlinx.serialization.json.encodeToJsonElement
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.text.Text
|
||||
import moe.nea.firmament.gui.FirmButtonComponent
|
||||
import moe.nea.firmament.jarvis.JarvisIntegration
|
||||
import moe.nea.firmament.util.MC
|
||||
|
||||
class HudMetaHandler(val config: ManagedConfig, val label: MutableText, val width: Int, val height: Int) :
|
||||
ManagedConfig.OptionHandler<HudMeta> {
|
||||
override fun toJson(element: HudMeta): JsonElement? {
|
||||
return Json.encodeToJsonElement(element.position)
|
||||
}
|
||||
|
||||
override fun fromJson(element: JsonElement): HudMeta {
|
||||
return HudMeta(Json.decodeFromJsonElement(element), label, width, height)
|
||||
}
|
||||
|
||||
override fun emitGuiElements(opt: ManagedOption<HudMeta>, guiAppender: GuiAppender) {
|
||||
guiAppender.appendLabeledRow(
|
||||
opt.labelText,
|
||||
FirmButtonComponent(
|
||||
TextComponent(
|
||||
Text.stringifiedTranslatable("firmament.hud.edit", label).string),
|
||||
) {
|
||||
MC.screen = JarvisIntegration.jarvis.getHudEditor(
|
||||
guiAppender.screenAccessor.invoke(),
|
||||
listOf(opt.value)
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
54
src/main/kotlin/gui/config/IntegerHandler.kt
Normal file
54
src/main/kotlin/gui/config/IntegerHandler.kt
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.gui.config
|
||||
|
||||
import io.github.notenoughupdates.moulconfig.common.IMinecraft
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.RowComponent
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.SliderComponent
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
|
||||
import io.github.notenoughupdates.moulconfig.observer.GetSetter
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.int
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import moe.nea.firmament.util.FirmFormatters
|
||||
|
||||
class IntegerHandler(val config: ManagedConfig, val min: Int, val max: Int) : ManagedConfig.OptionHandler<Int> {
|
||||
override fun toJson(element: Int): JsonElement? {
|
||||
return JsonPrimitive(element)
|
||||
}
|
||||
|
||||
override fun fromJson(element: JsonElement): Int {
|
||||
return element.jsonPrimitive.int
|
||||
}
|
||||
|
||||
override fun emitGuiElements(opt: ManagedOption<Int>, guiAppender: GuiAppender) {
|
||||
guiAppender.appendLabeledRow(
|
||||
opt.labelText,
|
||||
RowComponent(
|
||||
TextComponent(IMinecraft.instance.defaultFontRenderer,
|
||||
{ FirmFormatters.formatCommas(opt.value, 0) },
|
||||
40,
|
||||
TextComponent.TextAlignment.CENTER,
|
||||
true,
|
||||
false),
|
||||
SliderComponent(
|
||||
object : GetSetter<Float> {
|
||||
override fun get(): Float {
|
||||
return opt.value.toFloat()
|
||||
}
|
||||
|
||||
override fun set(newValue: Float) {
|
||||
opt.value = newValue.toInt()
|
||||
}
|
||||
},
|
||||
min.toFloat(),
|
||||
max.toFloat(),
|
||||
0.1F,
|
||||
130
|
||||
)
|
||||
))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
48
src/main/kotlin/gui/config/JAnyHud.kt
Normal file
48
src/main/kotlin/gui/config/JAnyHud.kt
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.gui.config
|
||||
|
||||
import moe.nea.jarvis.api.JarvisHud
|
||||
import moe.nea.jarvis.api.JarvisScalable
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.minecraft.text.Text
|
||||
|
||||
@Serializable
|
||||
data class HudPosition(
|
||||
var x: Double,
|
||||
var y: Double,
|
||||
var scale: Float,
|
||||
)
|
||||
|
||||
|
||||
data class HudMeta(
|
||||
val position: HudPosition,
|
||||
private val label: Text,
|
||||
private val width: Int,
|
||||
private val height: Int,
|
||||
) : JarvisScalable, JarvisHud {
|
||||
override fun getX(): Double = position.x
|
||||
|
||||
override fun setX(newX: Double) {
|
||||
position.x = newX
|
||||
}
|
||||
|
||||
override fun getY(): Double = position.y
|
||||
|
||||
override fun setY(newY: Double) {
|
||||
position.y = newY
|
||||
}
|
||||
|
||||
override fun getLabel(): Text = label
|
||||
|
||||
override fun getWidth(): Int = width
|
||||
|
||||
override fun getHeight(): Int = height
|
||||
|
||||
override fun getScale(): Float = position.scale
|
||||
|
||||
override fun setScale(newScale: Float) {
|
||||
position.scale = newScale
|
||||
}
|
||||
|
||||
}
|
||||
149
src/main/kotlin/gui/config/KeyBindingHandler.kt
Normal file
149
src/main/kotlin/gui/config/KeyBindingHandler.kt
Normal file
@@ -0,0 +1,149 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.gui.config
|
||||
|
||||
import io.github.notenoughupdates.moulconfig.common.IMinecraft
|
||||
import io.github.notenoughupdates.moulconfig.common.MyResourceLocation
|
||||
import io.github.notenoughupdates.moulconfig.deps.libninepatch.NinePatch
|
||||
import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext
|
||||
import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
|
||||
import org.lwjgl.glfw.GLFW
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.decodeFromJsonElement
|
||||
import kotlinx.serialization.json.encodeToJsonElement
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Formatting
|
||||
import moe.nea.firmament.gui.FirmButtonComponent
|
||||
import moe.nea.firmament.keybindings.FirmamentKeyBindings
|
||||
import moe.nea.firmament.keybindings.SavedKeyBinding
|
||||
|
||||
class KeyBindingHandler(val name: String, val managedConfig: ManagedConfig) :
|
||||
ManagedConfig.OptionHandler<SavedKeyBinding> {
|
||||
|
||||
override fun initOption(opt: ManagedOption<SavedKeyBinding>) {
|
||||
FirmamentKeyBindings.registerKeyBinding(name, opt)
|
||||
}
|
||||
|
||||
override fun toJson(element: SavedKeyBinding): JsonElement? {
|
||||
return Json.encodeToJsonElement(element)
|
||||
}
|
||||
|
||||
override fun fromJson(element: JsonElement): SavedKeyBinding {
|
||||
return Json.decodeFromJsonElement(element)
|
||||
}
|
||||
|
||||
override fun emitGuiElements(opt: ManagedOption<SavedKeyBinding>, guiAppender: GuiAppender) {
|
||||
var editing = false
|
||||
var lastPressed = 0
|
||||
var lastPressedNonModifier = 0
|
||||
var label: String = ""
|
||||
var button: FirmButtonComponent? = null
|
||||
fun updateLabel() {
|
||||
var stroke = opt.value.format()
|
||||
if (editing) {
|
||||
stroke = Text.literal("")
|
||||
val (shift, alt, ctrl) = SavedKeyBinding.getMods(SavedKeyBinding.getModInt())
|
||||
if (shift) {
|
||||
stroke.append("SHIFT + ")
|
||||
}
|
||||
if (alt) {
|
||||
stroke.append("ALT + ")
|
||||
}
|
||||
if (ctrl) {
|
||||
stroke.append("CTRL + ")
|
||||
}
|
||||
stroke.append("???")
|
||||
stroke.styled { it.withColor(Formatting.YELLOW) }
|
||||
}
|
||||
label = (stroke).string
|
||||
managedConfig.save()
|
||||
}
|
||||
button = object : FirmButtonComponent(
|
||||
TextComponent(
|
||||
IMinecraft.instance.defaultFontRenderer,
|
||||
{ label },
|
||||
130,
|
||||
TextComponent.TextAlignment.LEFT,
|
||||
false,
|
||||
false
|
||||
), action = {
|
||||
if (editing) {
|
||||
button!!.blur()
|
||||
} else {
|
||||
editing = true
|
||||
button!!.requestFocus()
|
||||
updateLabel()
|
||||
}
|
||||
}) {
|
||||
override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean {
|
||||
if (event is KeyboardEvent.KeyPressed) {
|
||||
return if (event.pressed) onKeyPressed(event.keycode, SavedKeyBinding.getModInt())
|
||||
else onKeyReleased(event.keycode, SavedKeyBinding.getModInt())
|
||||
}
|
||||
return super.keyboardEvent(event, context)
|
||||
}
|
||||
|
||||
override fun getBackground(context: GuiImmediateContext): NinePatch<MyResourceLocation> {
|
||||
if (editing) return activeBg
|
||||
return super.getBackground(context)
|
||||
}
|
||||
|
||||
fun onKeyPressed(ch: Int, modifiers: Int): Boolean {
|
||||
if (!editing) {
|
||||
return false
|
||||
}
|
||||
if (ch == GLFW.GLFW_KEY_ESCAPE) {
|
||||
lastPressedNonModifier = 0
|
||||
editing = false
|
||||
lastPressed = 0
|
||||
opt.value = SavedKeyBinding(GLFW.GLFW_KEY_UNKNOWN)
|
||||
updateLabel()
|
||||
blur()
|
||||
return true
|
||||
}
|
||||
if (ch == GLFW.GLFW_KEY_LEFT_SHIFT || ch == GLFW.GLFW_KEY_RIGHT_SHIFT
|
||||
|| ch == GLFW.GLFW_KEY_LEFT_ALT || ch == GLFW.GLFW_KEY_RIGHT_ALT
|
||||
|| ch == GLFW.GLFW_KEY_LEFT_CONTROL || ch == GLFW.GLFW_KEY_RIGHT_CONTROL
|
||||
) {
|
||||
lastPressed = ch
|
||||
} else {
|
||||
opt.value = SavedKeyBinding(
|
||||
ch, modifiers
|
||||
)
|
||||
editing = false
|
||||
blur()
|
||||
lastPressed = 0
|
||||
lastPressedNonModifier = 0
|
||||
}
|
||||
updateLabel()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onLostFocus() {
|
||||
lastPressedNonModifier = 0
|
||||
editing = false
|
||||
lastPressed = 0
|
||||
updateLabel()
|
||||
}
|
||||
|
||||
fun onKeyReleased(ch: Int, modifiers: Int): Boolean {
|
||||
if (!editing)
|
||||
return false
|
||||
if (lastPressedNonModifier == ch || (lastPressedNonModifier == 0 && ch == lastPressed)) {
|
||||
opt.value = SavedKeyBinding(ch, modifiers)
|
||||
editing = false
|
||||
blur()
|
||||
lastPressed = 0
|
||||
lastPressedNonModifier = 0
|
||||
}
|
||||
updateLabel()
|
||||
return true
|
||||
}
|
||||
}
|
||||
updateLabel()
|
||||
guiAppender.appendLabeledRow(opt.labelText, button)
|
||||
}
|
||||
|
||||
}
|
||||
181
src/main/kotlin/gui/config/ManagedConfig.kt
Normal file
181
src/main/kotlin/gui/config/ManagedConfig.kt
Normal file
@@ -0,0 +1,181 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.gui.config
|
||||
|
||||
import io.github.notenoughupdates.moulconfig.gui.CloseEventListener
|
||||
import io.github.notenoughupdates.moulconfig.gui.GuiComponentWrapper
|
||||
import io.github.notenoughupdates.moulconfig.gui.GuiContext
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.CenterComponent
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.ColumnComponent
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.PanelComponent
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.RowComponent
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.ScrollPanelComponent
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
|
||||
import moe.nea.jarvis.api.Point
|
||||
import org.lwjgl.glfw.GLFW
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlin.io.path.createDirectories
|
||||
import kotlin.io.path.readText
|
||||
import kotlin.io.path.writeText
|
||||
import kotlin.time.Duration
|
||||
import net.minecraft.client.gui.screen.Screen
|
||||
import net.minecraft.text.Text
|
||||
import moe.nea.firmament.Firmament
|
||||
import moe.nea.firmament.gui.FirmButtonComponent
|
||||
import moe.nea.firmament.keybindings.SavedKeyBinding
|
||||
import moe.nea.firmament.util.ScreenUtil.setScreenLater
|
||||
|
||||
abstract class ManagedConfig(override val name: String) : ManagedConfigElement() {
|
||||
|
||||
interface OptionHandler<T : Any> {
|
||||
fun initOption(opt: ManagedOption<T>) {}
|
||||
fun toJson(element: T): JsonElement?
|
||||
fun fromJson(element: JsonElement): T
|
||||
fun emitGuiElements(opt: ManagedOption<T>, guiAppender: GuiAppender)
|
||||
}
|
||||
|
||||
val file = Firmament.CONFIG_DIR.resolve("$name.json")
|
||||
val data: JsonObject by lazy {
|
||||
try {
|
||||
Firmament.json.decodeFromString(
|
||||
file.readText()
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Firmament.logger.info("Could not read config $name. Loading empty config.")
|
||||
JsonObject(mutableMapOf())
|
||||
}
|
||||
}
|
||||
|
||||
fun save() {
|
||||
val data = JsonObject(allOptions.mapNotNull { (key, value) ->
|
||||
value.toJson()?.let {
|
||||
key to it
|
||||
}
|
||||
}.toMap())
|
||||
file.parent.createDirectories()
|
||||
file.writeText(Firmament.json.encodeToString(data))
|
||||
}
|
||||
|
||||
|
||||
val allOptions = mutableMapOf<String, ManagedOption<*>>()
|
||||
val sortedOptions = mutableListOf<ManagedOption<*>>()
|
||||
|
||||
private var latestGuiAppender: GuiAppender? = null
|
||||
|
||||
protected fun <T : Any> option(
|
||||
propertyName: String,
|
||||
default: () -> T,
|
||||
handler: OptionHandler<T>
|
||||
): ManagedOption<T> {
|
||||
if (propertyName in allOptions) error("Cannot register the same name twice")
|
||||
return ManagedOption(this, propertyName, default, handler).also {
|
||||
it.handler.initOption(it)
|
||||
it.load(data)
|
||||
allOptions[propertyName] = it
|
||||
sortedOptions.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun toggle(propertyName: String, default: () -> Boolean): ManagedOption<Boolean> {
|
||||
return option(propertyName, default, BooleanHandler(this))
|
||||
}
|
||||
|
||||
protected fun duration(
|
||||
propertyName: String,
|
||||
min: Duration,
|
||||
max: Duration,
|
||||
default: () -> Duration,
|
||||
): ManagedOption<Duration> {
|
||||
return option(propertyName, default, DurationHandler(this, min, max))
|
||||
}
|
||||
|
||||
|
||||
protected fun position(
|
||||
propertyName: String,
|
||||
width: Int,
|
||||
height: Int,
|
||||
default: () -> Point,
|
||||
): ManagedOption<HudMeta> {
|
||||
val label = Text.translatable("firmament.config.${name}.${propertyName}")
|
||||
return option(propertyName, {
|
||||
val p = default()
|
||||
HudMeta(HudPosition(p.x, p.y, 1F), label, width, height)
|
||||
}, HudMetaHandler(this, label, width, height))
|
||||
}
|
||||
|
||||
protected fun keyBinding(
|
||||
propertyName: String,
|
||||
default: () -> Int,
|
||||
): ManagedOption<SavedKeyBinding> = keyBindingWithOutDefaultModifiers(propertyName) { SavedKeyBinding(default()) }
|
||||
|
||||
protected fun keyBindingWithOutDefaultModifiers(
|
||||
propertyName: String,
|
||||
default: () -> SavedKeyBinding,
|
||||
): ManagedOption<SavedKeyBinding> {
|
||||
return option(propertyName, default, KeyBindingHandler("firmament.config.${name}.${propertyName}", this))
|
||||
}
|
||||
|
||||
protected fun keyBindingWithDefaultUnbound(
|
||||
propertyName: String,
|
||||
): ManagedOption<SavedKeyBinding> {
|
||||
return keyBindingWithOutDefaultModifiers(propertyName) { SavedKeyBinding(GLFW.GLFW_KEY_UNKNOWN) }
|
||||
}
|
||||
|
||||
protected fun integer(
|
||||
propertyName: String,
|
||||
min: Int,
|
||||
max: Int,
|
||||
default: () -> Int,
|
||||
): ManagedOption<Int> {
|
||||
return option(propertyName, default, IntegerHandler(this, min, max))
|
||||
}
|
||||
|
||||
protected fun button(propertyName: String, runnable: () -> Unit): ManagedOption<Unit> {
|
||||
return option(propertyName, { }, ClickHandler(this, runnable))
|
||||
}
|
||||
|
||||
protected fun string(propertyName: String, default: () -> String): ManagedOption<String> {
|
||||
return option(propertyName, default, StringHandler(this))
|
||||
}
|
||||
|
||||
|
||||
fun reloadGui() {
|
||||
latestGuiAppender?.reloadables?.forEach { it() }
|
||||
}
|
||||
|
||||
val labelText = Text.translatable("firmament.config.${name}")
|
||||
|
||||
fun getConfigEditor(parent: Screen? = null): Screen {
|
||||
var screen: Screen? = null
|
||||
val guiapp = GuiAppender(400) { requireNotNull(screen) { "Screen Accessor called too early" } }
|
||||
latestGuiAppender = guiapp
|
||||
guiapp.appendFullRow(RowComponent(
|
||||
FirmButtonComponent(TextComponent("←")) {
|
||||
if (parent != null) {
|
||||
save()
|
||||
setScreenLater(parent)
|
||||
} else {
|
||||
AllConfigsGui.showAllGuis()
|
||||
}
|
||||
}
|
||||
))
|
||||
sortedOptions.forEach { it.appendToGui(guiapp) }
|
||||
guiapp.reloadables.forEach { it() }
|
||||
val component = CenterComponent(PanelComponent(ScrollPanelComponent(400, 300, ColumnComponent(guiapp.panel)), 10, PanelComponent.DefaultBackgroundRenderer.VANILLA))
|
||||
screen = object : GuiComponentWrapper(GuiContext(component)) {
|
||||
override fun close() {
|
||||
if (context.onBeforeClose() == CloseEventListener.CloseAction.NO_OBJECTIONS_TO_CLOSE) {
|
||||
client!!.setScreen(parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
return screen
|
||||
}
|
||||
|
||||
fun showConfigEditor(parent: Screen? = null) {
|
||||
setScreenLater(getConfigEditor(parent))
|
||||
}
|
||||
|
||||
}
|
||||
8
src/main/kotlin/gui/config/ManagedConfigElement.kt
Normal file
8
src/main/kotlin/gui/config/ManagedConfigElement.kt
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.gui.config
|
||||
|
||||
abstract class ManagedConfigElement {
|
||||
abstract val name: String
|
||||
|
||||
}
|
||||
62
src/main/kotlin/gui/config/ManagedOption.kt
Normal file
62
src/main/kotlin/gui/config/ManagedOption.kt
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.gui.config
|
||||
|
||||
import io.github.notenoughupdates.moulconfig.observer.GetSetter
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
import net.minecraft.text.Text
|
||||
import moe.nea.firmament.Firmament
|
||||
|
||||
class ManagedOption<T : Any>(
|
||||
val element: ManagedConfigElement,
|
||||
val propertyName: String,
|
||||
val default: () -> T,
|
||||
val handler: ManagedConfig.OptionHandler<T>
|
||||
) : ReadWriteProperty<Any?, T>, GetSetter<T> {
|
||||
override fun set(newValue: T) {
|
||||
this.value = newValue
|
||||
}
|
||||
|
||||
override fun get(): T {
|
||||
return this.value
|
||||
}
|
||||
|
||||
val rawLabelText = "firmament.config.${element.name}.${propertyName}"
|
||||
val labelText = Text.translatable(rawLabelText)
|
||||
|
||||
lateinit var value: T
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||
this.value = value
|
||||
}
|
||||
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||
return value
|
||||
}
|
||||
|
||||
fun load(root: JsonElement) {
|
||||
if (root is JsonObject && root.containsKey(propertyName)) {
|
||||
try {
|
||||
value = handler.fromJson(root[propertyName]!!)
|
||||
return
|
||||
} catch (e: Exception) {
|
||||
Firmament.logger.error(
|
||||
"Exception during loading of config file ${element.name}. This will reset this config.",
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
value = default()
|
||||
}
|
||||
|
||||
fun toJson(): JsonElement? {
|
||||
return handler.toJson(value)
|
||||
}
|
||||
|
||||
fun appendToGui(guiapp: GuiAppender) {
|
||||
handler.emitGuiElements(this, guiapp)
|
||||
}
|
||||
}
|
||||
36
src/main/kotlin/gui/config/StringHandler.kt
Normal file
36
src/main/kotlin/gui/config/StringHandler.kt
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.gui.config
|
||||
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.TextFieldComponent
|
||||
import io.github.notenoughupdates.moulconfig.observer.GetSetter
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import net.minecraft.text.Text
|
||||
|
||||
class StringHandler(val config: ManagedConfig) : ManagedConfig.OptionHandler<String> {
|
||||
override fun toJson(element: String): JsonElement? {
|
||||
return JsonPrimitive(element)
|
||||
}
|
||||
|
||||
override fun fromJson(element: JsonElement): String {
|
||||
return element.jsonPrimitive.content
|
||||
}
|
||||
|
||||
override fun emitGuiElements(opt: ManagedOption<String>, guiAppender: GuiAppender) {
|
||||
guiAppender.appendLabeledRow(
|
||||
opt.labelText,
|
||||
TextFieldComponent(
|
||||
object : GetSetter<String> by opt {
|
||||
override fun set(newValue: String) {
|
||||
opt.set(newValue)
|
||||
config.save()
|
||||
}
|
||||
},
|
||||
130,
|
||||
suggestion = Text.translatableWithFallback(opt.rawLabelText + ".hint", "").string
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
9
src/main/kotlin/gui/entity/EntityModifier.kt
Normal file
9
src/main/kotlin/gui/entity/EntityModifier.kt
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
package moe.nea.firmament.gui.entity
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.entity.LivingEntity
|
||||
|
||||
fun interface EntityModifier {
|
||||
fun apply(entity: LivingEntity, info: JsonObject): LivingEntity
|
||||
}
|
||||
197
src/main/kotlin/gui/entity/EntityRenderer.kt
Normal file
197
src/main/kotlin/gui/entity/EntityRenderer.kt
Normal file
@@ -0,0 +1,197 @@
|
||||
|
||||
package moe.nea.firmament.gui.entity
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.atan
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.screen.ingame.InventoryScreen
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.EntityType
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.util.Identifier
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.assertNotNullOr
|
||||
import moe.nea.firmament.util.iterate
|
||||
import moe.nea.firmament.util.openFirmamentResource
|
||||
import moe.nea.firmament.util.render.enableScissorWithTranslation
|
||||
|
||||
object EntityRenderer {
|
||||
val fakeWorld = FakeWorld()
|
||||
private fun <T : Entity> t(entityType: EntityType<T>): () -> T {
|
||||
return { entityType.create(fakeWorld)!! }
|
||||
}
|
||||
|
||||
val entityIds: Map<String, () -> LivingEntity> = mapOf(
|
||||
"Zombie" to t(EntityType.ZOMBIE),
|
||||
"Chicken" to t(EntityType.CHICKEN),
|
||||
"Slime" to t(EntityType.SLIME),
|
||||
"Wolf" to t(EntityType.WOLF),
|
||||
"Skeleton" to t(EntityType.SKELETON),
|
||||
"Creeper" to t(EntityType.CREEPER),
|
||||
"Ocelot" to t(EntityType.OCELOT),
|
||||
"Blaze" to t(EntityType.BLAZE),
|
||||
"Rabbit" to t(EntityType.RABBIT),
|
||||
"Sheep" to t(EntityType.SHEEP),
|
||||
"Horse" to t(EntityType.HORSE),
|
||||
"Eisengolem" to t(EntityType.IRON_GOLEM),
|
||||
"Silverfish" to t(EntityType.SILVERFISH),
|
||||
"Witch" to t(EntityType.WITCH),
|
||||
"Endermite" to t(EntityType.ENDERMITE),
|
||||
"Snowman" to t(EntityType.SNOW_GOLEM),
|
||||
"Villager" to t(EntityType.VILLAGER),
|
||||
"Guardian" to t(EntityType.GUARDIAN),
|
||||
"ArmorStand" to t(EntityType.ARMOR_STAND),
|
||||
"Squid" to t(EntityType.SQUID),
|
||||
"Bat" to t(EntityType.BAT),
|
||||
"Spider" to t(EntityType.SPIDER),
|
||||
"CaveSpider" to t(EntityType.CAVE_SPIDER),
|
||||
"Pigman" to t(EntityType.ZOMBIFIED_PIGLIN),
|
||||
"Ghast" to t(EntityType.GHAST),
|
||||
"MagmaCube" to t(EntityType.MAGMA_CUBE),
|
||||
"Wither" to t(EntityType.WITHER),
|
||||
"Enderman" to t(EntityType.ENDERMAN),
|
||||
"Mooshroom" to t(EntityType.MOOSHROOM),
|
||||
"WitherSkeleton" to t(EntityType.WITHER_SKELETON),
|
||||
"Cow" to t(EntityType.COW),
|
||||
"Dragon" to t(EntityType.ENDER_DRAGON),
|
||||
"Player" to { makeGuiPlayer(fakeWorld) },
|
||||
"Pig" to t(EntityType.PIG),
|
||||
"Giant" to t(EntityType.GIANT),
|
||||
)
|
||||
val entityModifiers: Map<String, EntityModifier> = mapOf(
|
||||
"playerdata" to ModifyPlayerSkin,
|
||||
"equipment" to ModifyEquipment,
|
||||
"riding" to ModifyRiding,
|
||||
"charged" to ModifyCharged,
|
||||
"witherdata" to ModifyWither,
|
||||
"invisible" to ModifyInvisible,
|
||||
"age" to ModifyAge,
|
||||
"horse" to ModifyHorse,
|
||||
"name" to ModifyName,
|
||||
)
|
||||
|
||||
val logger = LogManager.getLogger("Firmament.Entity")
|
||||
fun applyModifiers(entityId: String, modifiers: List<JsonObject>): LivingEntity? {
|
||||
val entityType = assertNotNullOr(entityIds[entityId]) {
|
||||
logger.error("Could not create entity with id $entityId")
|
||||
return null
|
||||
}
|
||||
var entity = entityType()
|
||||
for (modifierJson in modifiers) {
|
||||
val modifier = assertNotNullOr(modifierJson["type"]?.asString?.let(entityModifiers::get)) {
|
||||
logger.error("Unknown modifier $modifierJson")
|
||||
return null
|
||||
}
|
||||
entity = modifier.apply(entity, modifierJson)
|
||||
}
|
||||
return entity
|
||||
}
|
||||
|
||||
fun constructEntity(info: JsonObject): LivingEntity? {
|
||||
val modifiers = (info["modifiers"] as JsonArray?)?.map { it.asJsonObject } ?: emptyList()
|
||||
val entityType = assertNotNullOr(info["entity"]?.asString) {
|
||||
logger.error("Missing entity type on entity object")
|
||||
return null
|
||||
}
|
||||
return applyModifiers(entityType, modifiers)
|
||||
}
|
||||
|
||||
private val gson = Gson()
|
||||
fun constructEntity(location: Identifier): LivingEntity? {
|
||||
return constructEntity(
|
||||
gson.fromJson(
|
||||
location.openFirmamentResource().bufferedReader(), JsonObject::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun renderEntity(
|
||||
entity: LivingEntity,
|
||||
renderContext: DrawContext,
|
||||
posX: Int,
|
||||
posY: Int,
|
||||
mouseX: Float,
|
||||
mouseY: Float
|
||||
) {
|
||||
var bottomOffset = 0.0F
|
||||
var currentEntity = entity
|
||||
val maxSize = entity.iterate { it.firstPassenger as? LivingEntity }
|
||||
.map { it.height }
|
||||
.sum()
|
||||
while (true) {
|
||||
currentEntity.age = MC.player?.age ?: 0
|
||||
drawEntity(
|
||||
renderContext,
|
||||
posX,
|
||||
posY,
|
||||
posX + 50,
|
||||
posY + 80,
|
||||
minOf(2F / maxSize, 1F) * 30,
|
||||
-bottomOffset,
|
||||
mouseX,
|
||||
mouseY,
|
||||
currentEntity
|
||||
)
|
||||
val next = currentEntity.firstPassenger as? LivingEntity ?: break
|
||||
bottomOffset += currentEntity.getPassengerRidingPos(next).y.toFloat() * 0.75F
|
||||
currentEntity = next
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun drawEntity(
|
||||
context: DrawContext,
|
||||
x1: Int,
|
||||
y1: Int,
|
||||
x2: Int,
|
||||
y2: Int,
|
||||
size: Float,
|
||||
bottomOffset: Float,
|
||||
mouseX: Float,
|
||||
mouseY: Float,
|
||||
entity: LivingEntity
|
||||
) {
|
||||
context.enableScissorWithTranslation(x1.toFloat(), y1.toFloat(), x2.toFloat(), y2.toFloat())
|
||||
val centerX = (x1 + x2) / 2f
|
||||
val centerY = (y1 + y2) / 2f
|
||||
val targetYaw = atan(((centerX - mouseX) / 40.0f).toDouble()).toFloat()
|
||||
val targetPitch = atan(((centerY - mouseY) / 40.0f).toDouble()).toFloat()
|
||||
val rotateToFaceTheFront = Quaternionf().rotateZ(Math.PI.toFloat())
|
||||
val rotateToFaceTheCamera = Quaternionf().rotateX(targetPitch * 20.0f * (Math.PI.toFloat() / 180))
|
||||
rotateToFaceTheFront.mul(rotateToFaceTheCamera)
|
||||
val oldBodyYaw = entity.bodyYaw
|
||||
val oldYaw = entity.yaw
|
||||
val oldPitch = entity.pitch
|
||||
val oldPrevHeadYaw = entity.prevHeadYaw
|
||||
val oldHeadYaw = entity.headYaw
|
||||
entity.bodyYaw = 180.0f + targetYaw * 20.0f
|
||||
entity.yaw = 180.0f + targetYaw * 40.0f
|
||||
entity.pitch = -targetPitch * 20.0f
|
||||
entity.headYaw = entity.yaw
|
||||
entity.prevHeadYaw = entity.yaw
|
||||
val vector3f = Vector3f(0.0f, entity.height / 2.0f + bottomOffset, 0.0f)
|
||||
InventoryScreen.drawEntity(
|
||||
context,
|
||||
centerX,
|
||||
centerY,
|
||||
size,
|
||||
vector3f,
|
||||
rotateToFaceTheFront,
|
||||
rotateToFaceTheCamera,
|
||||
entity
|
||||
)
|
||||
entity.bodyYaw = oldBodyYaw
|
||||
entity.yaw = oldYaw
|
||||
entity.pitch = oldPitch
|
||||
entity.prevHeadYaw = oldPrevHeadYaw
|
||||
entity.headYaw = oldHeadYaw
|
||||
context.disableScissor()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
35
src/main/kotlin/gui/entity/EntityWidget.kt
Normal file
35
src/main/kotlin/gui/entity/EntityWidget.kt
Normal file
@@ -0,0 +1,35 @@
|
||||
|
||||
package moe.nea.firmament.gui.entity
|
||||
|
||||
import me.shedaniel.math.Dimension
|
||||
import me.shedaniel.math.Point
|
||||
import me.shedaniel.math.Rectangle
|
||||
import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.Element
|
||||
import net.minecraft.entity.LivingEntity
|
||||
|
||||
class EntityWidget(val entity: LivingEntity, val point: Point) : WidgetWithBounds() {
|
||||
override fun children(): List<Element> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
var hasErrored = false
|
||||
|
||||
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
try {
|
||||
if (!hasErrored)
|
||||
EntityRenderer.renderEntity(entity, context, point.x, point.y, mouseX.toFloat(), mouseY.toFloat())
|
||||
} catch (ex: Exception) {
|
||||
EntityRenderer.logger.error("Failed to render constructed entity: $entity", ex)
|
||||
hasErrored = true
|
||||
}
|
||||
if (hasErrored) {
|
||||
context.fill(point.x, point.y, point.x + 50, point.y + 80, 0xFFAA2222.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
override fun getBounds(): Rectangle {
|
||||
return Rectangle(point, Dimension(50, 80))
|
||||
}
|
||||
}
|
||||
488
src/main/kotlin/gui/entity/FakeWorld.kt
Normal file
488
src/main/kotlin/gui/entity/FakeWorld.kt
Normal file
@@ -0,0 +1,488 @@
|
||||
|
||||
package moe.nea.firmament.gui.entity
|
||||
|
||||
import com.mojang.datafixers.util.Pair
|
||||
import com.mojang.serialization.Lifecycle
|
||||
import java.util.*
|
||||
import java.util.function.BooleanSupplier
|
||||
import java.util.function.Consumer
|
||||
import java.util.stream.Stream
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import kotlin.streams.asSequence
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.component.type.MapIdComponent
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.fluid.Fluid
|
||||
import net.minecraft.item.map.MapState
|
||||
import net.minecraft.recipe.BrewingRecipeRegistry
|
||||
import net.minecraft.recipe.Ingredient
|
||||
import net.minecraft.recipe.RecipeManager
|
||||
import net.minecraft.registry.BuiltinRegistries
|
||||
import net.minecraft.registry.DynamicRegistryManager
|
||||
import net.minecraft.registry.Registry
|
||||
import net.minecraft.registry.RegistryKey
|
||||
import net.minecraft.registry.RegistryKeys
|
||||
import net.minecraft.registry.RegistryWrapper
|
||||
import net.minecraft.registry.entry.RegistryEntry
|
||||
import net.minecraft.registry.entry.RegistryEntryInfo
|
||||
import net.minecraft.registry.entry.RegistryEntryList
|
||||
import net.minecraft.registry.entry.RegistryEntryOwner
|
||||
import net.minecraft.registry.tag.TagKey
|
||||
import net.minecraft.resource.featuretoggle.FeatureFlags
|
||||
import net.minecraft.resource.featuretoggle.FeatureSet
|
||||
import net.minecraft.scoreboard.Scoreboard
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.sound.SoundEvent
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.TypeFilter
|
||||
import net.minecraft.util.function.LazyIterationConsumer
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Box
|
||||
import net.minecraft.util.math.ChunkPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.minecraft.util.math.random.Random
|
||||
import net.minecraft.util.profiler.DummyProfiler
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.Difficulty
|
||||
import net.minecraft.world.GameRules
|
||||
import net.minecraft.world.MutableWorldProperties
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.biome.Biome
|
||||
import net.minecraft.world.biome.BiomeKeys
|
||||
import net.minecraft.world.chunk.Chunk
|
||||
import net.minecraft.world.chunk.ChunkManager
|
||||
import net.minecraft.world.chunk.ChunkStatus
|
||||
import net.minecraft.world.chunk.EmptyChunk
|
||||
import net.minecraft.world.chunk.light.LightingProvider
|
||||
import net.minecraft.world.entity.EntityLookup
|
||||
import net.minecraft.world.event.GameEvent
|
||||
import net.minecraft.world.tick.OrderedTick
|
||||
import net.minecraft.world.tick.QueryableTickScheduler
|
||||
import net.minecraft.world.tick.TickManager
|
||||
|
||||
fun <T> makeRegistry(registryWrapper: RegistryWrapper.Impl<T>, key: RegistryKey<out Registry<T>>): Registry<T> {
|
||||
val inverseLookup = registryWrapper.streamEntries()
|
||||
.asSequence().map { it.value() to it.registryKey() }
|
||||
.toMap()
|
||||
val idLookup = registryWrapper.streamEntries()
|
||||
.asSequence()
|
||||
.map { it.registryKey() }
|
||||
.withIndex()
|
||||
.associate { it.value to it.index }
|
||||
val map = registryWrapper.streamEntries().asSequence().map { it.registryKey() to it.value() }.toMap(mutableMapOf())
|
||||
val inverseIdLookup = idLookup.asIterable().associate { (k, v) -> v to k }
|
||||
return object : Registry<T> {
|
||||
override fun get(key: RegistryKey<T>?): T? {
|
||||
return registryWrapper.getOptional(key).getOrNull()?.value()
|
||||
}
|
||||
|
||||
override fun iterator(): MutableIterator<T> {
|
||||
return object : MutableIterator<T> {
|
||||
val iterator = registryWrapper.streamEntries().iterator()
|
||||
override fun hasNext(): Boolean {
|
||||
return iterator.hasNext()
|
||||
}
|
||||
|
||||
override fun next(): T {
|
||||
return iterator.next().value()
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRawId(value: T?): Int {
|
||||
return idLookup[inverseLookup[value ?: return -1] ?: return -1] ?: return -1
|
||||
}
|
||||
|
||||
override fun get(id: Identifier?): T? {
|
||||
return get(RegistryKey.of(key, id))
|
||||
}
|
||||
|
||||
override fun get(index: Int): T? {
|
||||
return get(inverseIdLookup[index] ?: return null)
|
||||
}
|
||||
|
||||
override fun size(): Int {
|
||||
return idLookup.size
|
||||
}
|
||||
|
||||
override fun getKey(): RegistryKey<out Registry<T>> {
|
||||
return key
|
||||
}
|
||||
|
||||
override fun getEntryInfo(key: RegistryKey<T>?): Optional<RegistryEntryInfo> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getLifecycle(): Lifecycle {
|
||||
return Lifecycle.stable()
|
||||
}
|
||||
|
||||
override fun getDefaultEntry(): Optional<RegistryEntry.Reference<T>> {
|
||||
return Optional.empty()
|
||||
}
|
||||
|
||||
override fun getIds(): MutableSet<Identifier> {
|
||||
return idLookup.keys.mapTo(mutableSetOf()) { it.value }
|
||||
}
|
||||
|
||||
override fun getEntrySet(): MutableSet<MutableMap.MutableEntry<RegistryKey<T>, T>> {
|
||||
return map.entries
|
||||
}
|
||||
|
||||
override fun getKeys(): MutableSet<RegistryKey<T>> {
|
||||
return map.keys
|
||||
}
|
||||
|
||||
override fun getRandom(random: Random?): Optional<RegistryEntry.Reference<T>> {
|
||||
return registryWrapper.streamEntries().findFirst()
|
||||
}
|
||||
|
||||
override fun containsId(id: Identifier?): Boolean {
|
||||
return idLookup.containsKey(RegistryKey.of(key, id ?: return false))
|
||||
}
|
||||
|
||||
override fun freeze(): Registry<T> {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun getEntry(rawId: Int): Optional<RegistryEntry.Reference<T>> {
|
||||
val x = inverseIdLookup[rawId] ?: return Optional.empty()
|
||||
return Optional.of(RegistryEntry.Reference.standAlone(registryWrapper, x))
|
||||
}
|
||||
|
||||
override fun streamEntries(): Stream<RegistryEntry.Reference<T>> {
|
||||
return registryWrapper.streamEntries()
|
||||
}
|
||||
|
||||
override fun streamTagsAndEntries(): Stream<Pair<TagKey<T>, RegistryEntryList.Named<T>>> {
|
||||
return streamTags().map { Pair(it, getOrCreateEntryList(it)) }
|
||||
}
|
||||
|
||||
override fun streamTags(): Stream<TagKey<T>> {
|
||||
return registryWrapper.streamTagKeys()
|
||||
}
|
||||
|
||||
override fun clearTags() {
|
||||
}
|
||||
|
||||
override fun getEntryOwner(): RegistryEntryOwner<T> {
|
||||
return registryWrapper
|
||||
}
|
||||
|
||||
override fun getReadOnlyWrapper(): RegistryWrapper.Impl<T> {
|
||||
return registryWrapper
|
||||
}
|
||||
|
||||
override fun populateTags(tagEntries: MutableMap<TagKey<T>, MutableList<RegistryEntry<T>>>?) {
|
||||
}
|
||||
|
||||
override fun getOrCreateEntryList(tag: TagKey<T>?): RegistryEntryList.Named<T> {
|
||||
return getEntryList(tag).orElseGet { RegistryEntryList.of(registryWrapper, tag) }
|
||||
}
|
||||
|
||||
override fun getEntryList(tag: TagKey<T>?): Optional<RegistryEntryList.Named<T>> {
|
||||
return registryWrapper.getOptional(tag ?: return Optional.empty())
|
||||
}
|
||||
|
||||
override fun getEntry(value: T): RegistryEntry<T> {
|
||||
return registryWrapper.getOptional(inverseLookup[value]!!).get()
|
||||
}
|
||||
|
||||
override fun getEntry(key: RegistryKey<T>?): Optional<RegistryEntry.Reference<T>> {
|
||||
return registryWrapper.getOptional(key ?: return Optional.empty())
|
||||
}
|
||||
|
||||
override fun getEntry(id: Identifier?): Optional<RegistryEntry.Reference<T>> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun createEntry(value: T): RegistryEntry.Reference<T> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun contains(key: RegistryKey<T>?): Boolean {
|
||||
return getEntry(key).isPresent
|
||||
}
|
||||
|
||||
override fun getId(value: T): Identifier? {
|
||||
return (inverseLookup[value] ?: return null).value
|
||||
}
|
||||
|
||||
override fun getKey(entry: T): Optional<RegistryKey<T>> {
|
||||
return Optional.ofNullable(inverseLookup[entry ?: return Optional.empty()])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createDynamicRegistry(): DynamicRegistryManager.Immutable {
|
||||
val wrapperLookup = BuiltinRegistries.createWrapperLookup()
|
||||
return object : DynamicRegistryManager.Immutable {
|
||||
override fun <E : Any?> getOptional(key: RegistryKey<out Registry<out E>>): Optional<Registry<E>> {
|
||||
val lookup = wrapperLookup.getOptionalWrapper(key).getOrNull() ?: return Optional.empty()
|
||||
val registry = makeRegistry(lookup, key as RegistryKey<out Registry<E>>)
|
||||
return Optional.of(registry)
|
||||
}
|
||||
|
||||
fun <T> entry(reg: RegistryKey<out Registry<T>>): DynamicRegistryManager.Entry<T> {
|
||||
return DynamicRegistryManager.Entry(reg, getOptional(reg).get())
|
||||
}
|
||||
|
||||
override fun streamAllRegistries(): Stream<DynamicRegistryManager.Entry<*>> {
|
||||
return wrapperLookup.streamAllRegistryKeys()
|
||||
.map { entry(it as RegistryKey<out Registry<Any>>) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FakeWorld(
|
||||
registries: DynamicRegistryManager.Immutable = createDynamicRegistry(),
|
||||
) : World(
|
||||
Properties,
|
||||
RegistryKey.of(RegistryKeys.WORLD, Identifier.of("firmament", "fakeworld")),
|
||||
registries,
|
||||
registries[RegistryKeys.DIMENSION_TYPE].entryOf(
|
||||
RegistryKey.of(
|
||||
RegistryKeys.DIMENSION_TYPE,
|
||||
Identifier.of("minecraft", "overworld")
|
||||
)
|
||||
),
|
||||
{ DummyProfiler.INSTANCE },
|
||||
true,
|
||||
false,
|
||||
0, 0
|
||||
) {
|
||||
object Properties : MutableWorldProperties {
|
||||
override fun getSpawnPos(): BlockPos {
|
||||
return BlockPos.ORIGIN
|
||||
}
|
||||
|
||||
override fun getSpawnAngle(): Float {
|
||||
return 0F
|
||||
}
|
||||
|
||||
override fun getTime(): Long {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun getTimeOfDay(): Long {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun isThundering(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isRaining(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setRaining(raining: Boolean) {
|
||||
}
|
||||
|
||||
override fun isHardcore(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getGameRules(): GameRules {
|
||||
return GameRules()
|
||||
}
|
||||
|
||||
override fun getDifficulty(): Difficulty {
|
||||
return Difficulty.HARD
|
||||
}
|
||||
|
||||
override fun isDifficultyLocked(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setSpawnPos(pos: BlockPos?, angle: Float) {}
|
||||
}
|
||||
|
||||
override fun getPlayers(): List<PlayerEntity> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun getBrightness(direction: Direction?, shaded: Boolean): Float {
|
||||
return 1f
|
||||
}
|
||||
|
||||
override fun getGeneratorStoredBiome(biomeX: Int, biomeY: Int, biomeZ: Int): RegistryEntry<Biome> {
|
||||
return registryManager.get(RegistryKeys.BIOME).entryOf(BiomeKeys.PLAINS)
|
||||
}
|
||||
|
||||
override fun getEnabledFeatures(): FeatureSet {
|
||||
return FeatureFlags.VANILLA_FEATURES
|
||||
}
|
||||
|
||||
class FakeTickScheduler<T> : QueryableTickScheduler<T> {
|
||||
override fun scheduleTick(orderedTick: OrderedTick<T>?) {
|
||||
}
|
||||
|
||||
override fun isQueued(pos: BlockPos?, type: T): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getTickCount(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun isTicking(pos: BlockPos?, type: T): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun getBlockTickScheduler(): QueryableTickScheduler<Block> {
|
||||
return FakeTickScheduler()
|
||||
}
|
||||
|
||||
override fun getFluidTickScheduler(): QueryableTickScheduler<Fluid> {
|
||||
return FakeTickScheduler()
|
||||
}
|
||||
|
||||
|
||||
class FakeChunkManager(val world: FakeWorld) : ChunkManager() {
|
||||
override fun getChunk(x: Int, z: Int, leastStatus: ChunkStatus?, create: Boolean): Chunk {
|
||||
return EmptyChunk(
|
||||
world,
|
||||
ChunkPos(x, z),
|
||||
world.registryManager.get(RegistryKeys.BIOME).entryOf(BiomeKeys.PLAINS)
|
||||
)
|
||||
}
|
||||
|
||||
override fun getWorld(): BlockView {
|
||||
return world
|
||||
}
|
||||
|
||||
override fun tick(shouldKeepTicking: BooleanSupplier?, tickChunks: Boolean) {
|
||||
}
|
||||
|
||||
override fun getDebugString(): String {
|
||||
return "FakeChunkManager"
|
||||
}
|
||||
|
||||
override fun getLoadedChunkCount(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun getLightingProvider(): LightingProvider {
|
||||
return FakeLightingProvider(this)
|
||||
}
|
||||
}
|
||||
|
||||
class FakeLightingProvider(chunkManager: FakeChunkManager) : LightingProvider(chunkManager, false, false)
|
||||
|
||||
override fun getChunkManager(): ChunkManager {
|
||||
return FakeChunkManager(this)
|
||||
}
|
||||
|
||||
override fun playSound(
|
||||
source: PlayerEntity?,
|
||||
x: Double,
|
||||
y: Double,
|
||||
z: Double,
|
||||
sound: RegistryEntry<SoundEvent>?,
|
||||
category: SoundCategory?,
|
||||
volume: Float,
|
||||
pitch: Float,
|
||||
seed: Long
|
||||
) {
|
||||
}
|
||||
|
||||
override fun syncWorldEvent(player: PlayerEntity?, eventId: Int, pos: BlockPos?, data: Int) {
|
||||
}
|
||||
|
||||
override fun emitGameEvent(event: RegistryEntry<GameEvent>?, emitterPos: Vec3d?, emitter: GameEvent.Emitter?) {
|
||||
}
|
||||
|
||||
override fun updateListeners(pos: BlockPos?, oldState: BlockState?, newState: BlockState?, flags: Int) {
|
||||
}
|
||||
|
||||
override fun playSoundFromEntity(
|
||||
source: PlayerEntity?,
|
||||
entity: Entity?,
|
||||
sound: RegistryEntry<SoundEvent>?,
|
||||
category: SoundCategory?,
|
||||
volume: Float,
|
||||
pitch: Float,
|
||||
seed: Long
|
||||
) {
|
||||
}
|
||||
|
||||
override fun asString(): String {
|
||||
return "FakeWorld"
|
||||
}
|
||||
|
||||
override fun getEntityById(id: Int): Entity? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getTickManager(): TickManager {
|
||||
return TickManager()
|
||||
}
|
||||
|
||||
override fun getMapState(id: MapIdComponent?): MapState? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun putMapState(id: MapIdComponent?, state: MapState?) {
|
||||
}
|
||||
|
||||
override fun increaseAndGetMapId(): MapIdComponent {
|
||||
return MapIdComponent(0)
|
||||
}
|
||||
|
||||
override fun setBlockBreakingInfo(entityId: Int, pos: BlockPos?, progress: Int) {
|
||||
}
|
||||
|
||||
override fun getScoreboard(): Scoreboard {
|
||||
return Scoreboard()
|
||||
}
|
||||
|
||||
override fun getRecipeManager(): RecipeManager {
|
||||
return RecipeManager(registryManager)
|
||||
}
|
||||
|
||||
object FakeEntityLookup : EntityLookup<Entity> {
|
||||
override fun get(id: Int): Entity? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun get(uuid: UUID?): Entity? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun iterate(): MutableIterable<Entity> {
|
||||
return mutableListOf()
|
||||
}
|
||||
|
||||
override fun <U : Entity?> forEachIntersects(
|
||||
filter: TypeFilter<Entity, U>?,
|
||||
box: Box?,
|
||||
consumer: LazyIterationConsumer<U>?
|
||||
) {
|
||||
}
|
||||
|
||||
override fun forEachIntersects(box: Box?, action: Consumer<Entity>?) {
|
||||
}
|
||||
|
||||
override fun <U : Entity?> forEach(filter: TypeFilter<Entity, U>?, consumer: LazyIterationConsumer<U>?) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun getEntityLookup(): EntityLookup<Entity> {
|
||||
return FakeEntityLookup
|
||||
}
|
||||
|
||||
override fun getBrewingRecipeRegistry(): BrewingRecipeRegistry {
|
||||
return BrewingRecipeRegistry.EMPTY
|
||||
}
|
||||
}
|
||||
54
src/main/kotlin/gui/entity/GuiPlayer.kt
Normal file
54
src/main/kotlin/gui/entity/GuiPlayer.kt
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
package moe.nea.firmament.gui.entity
|
||||
|
||||
import com.mojang.authlib.GameProfile
|
||||
import java.util.*
|
||||
import net.minecraft.client.network.AbstractClientPlayerEntity
|
||||
import net.minecraft.client.util.DefaultSkinHelper
|
||||
import net.minecraft.client.util.SkinTextures
|
||||
import net.minecraft.client.util.SkinTextures.Model
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.World
|
||||
|
||||
/**
|
||||
* @see moe.nea.firmament.init.EarlyRiser
|
||||
*/
|
||||
fun makeGuiPlayer(world: FakeWorld): GuiPlayer {
|
||||
val constructor = GuiPlayer::class.java.getDeclaredConstructor(
|
||||
World::class.java,
|
||||
BlockPos::class.java,
|
||||
Float::class.javaPrimitiveType,
|
||||
GameProfile::class.java
|
||||
)
|
||||
return constructor.newInstance(world, BlockPos.ORIGIN, 0F, GameProfile(UUID.randomUUID(), "Linnea"))
|
||||
}
|
||||
|
||||
class GuiPlayer(world: ClientWorld?, profile: GameProfile?) : AbstractClientPlayerEntity(world, profile) {
|
||||
override fun isSpectator(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isCreative(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRenderName(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
var skinTexture: Identifier = DefaultSkinHelper.getSkinTextures(this.getUuid()).texture
|
||||
var capeTexture: Identifier? = null
|
||||
var model: Model = Model.WIDE
|
||||
override fun getSkinTextures(): SkinTextures {
|
||||
return SkinTextures(
|
||||
skinTexture,
|
||||
null,
|
||||
capeTexture,
|
||||
null,
|
||||
model,
|
||||
true
|
||||
)
|
||||
}
|
||||
}
|
||||
25
src/main/kotlin/gui/entity/ModifyAge.kt
Normal file
25
src/main/kotlin/gui/entity/ModifyAge.kt
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
package moe.nea.firmament.gui.entity
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.decoration.ArmorStandEntity
|
||||
import net.minecraft.entity.mob.ZombieEntity
|
||||
import net.minecraft.entity.passive.PassiveEntity
|
||||
|
||||
object ModifyAge : EntityModifier {
|
||||
override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
|
||||
val isBaby = info["baby"]?.asBoolean ?: false
|
||||
if (entity is PassiveEntity) {
|
||||
entity.breedingAge = if (isBaby) -1 else 1
|
||||
} else if (entity is ZombieEntity) {
|
||||
entity.isBaby = isBaby
|
||||
} else if (entity is ArmorStandEntity) {
|
||||
entity.isSmall = isBaby
|
||||
} else {
|
||||
error("Cannot set age for $entity")
|
||||
}
|
||||
return entity
|
||||
}
|
||||
|
||||
}
|
||||
14
src/main/kotlin/gui/entity/ModifyCharged.kt
Normal file
14
src/main/kotlin/gui/entity/ModifyCharged.kt
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
package moe.nea.firmament.gui.entity
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.mob.CreeperEntity
|
||||
|
||||
object ModifyCharged : EntityModifier {
|
||||
override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
|
||||
require(entity is CreeperEntity)
|
||||
entity.dataTracker.set(CreeperEntity.CHARGED, true)
|
||||
return entity
|
||||
}
|
||||
}
|
||||
55
src/main/kotlin/gui/entity/ModifyEquipment.kt
Normal file
55
src/main/kotlin/gui/entity/ModifyEquipment.kt
Normal file
@@ -0,0 +1,55 @@
|
||||
|
||||
package moe.nea.firmament.gui.entity
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.component.DataComponentTypes
|
||||
import net.minecraft.component.type.DyedColorComponent
|
||||
import net.minecraft.entity.EquipmentSlot
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.item.ArmorItem
|
||||
import net.minecraft.item.Item
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
import moe.nea.firmament.rei.SBItemStack
|
||||
import moe.nea.firmament.util.SkyblockId
|
||||
import moe.nea.firmament.util.item.setEncodedSkullOwner
|
||||
import moe.nea.firmament.util.item.zeroUUID
|
||||
|
||||
object ModifyEquipment : EntityModifier {
|
||||
val names = mapOf(
|
||||
"hand" to EquipmentSlot.MAINHAND,
|
||||
"helmet" to EquipmentSlot.HEAD,
|
||||
"chestplate" to EquipmentSlot.CHEST,
|
||||
"leggings" to EquipmentSlot.LEGS,
|
||||
"feet" to EquipmentSlot.FEET,
|
||||
)
|
||||
|
||||
override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
|
||||
names.forEach { (key, slot) ->
|
||||
info[key]?.let {
|
||||
entity.equipStack(slot, createItem(it.asString))
|
||||
}
|
||||
}
|
||||
return entity
|
||||
}
|
||||
|
||||
private fun createItem(item: String): ItemStack {
|
||||
val split = item.split("#")
|
||||
if (split.size != 2) return SBItemStack(SkyblockId(item)).asImmutableItemStack()
|
||||
val (type, data) = split
|
||||
return when (type) {
|
||||
"SKULL" -> ItemStack(Items.PLAYER_HEAD).also { it.setEncodedSkullOwner(zeroUUID, data) }
|
||||
"LEATHER_LEGGINGS" -> coloredLeatherArmor(Items.LEATHER_LEGGINGS, data)
|
||||
"LEATHER_BOOTS" -> coloredLeatherArmor(Items.LEATHER_BOOTS, data)
|
||||
"LEATHER_HELMET" -> coloredLeatherArmor(Items.LEATHER_HELMET, data)
|
||||
"LEATHER_CHESTPLATE" -> coloredLeatherArmor(Items.LEATHER_CHESTPLATE, data)
|
||||
else -> error("Unknown leather piece: $type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun coloredLeatherArmor(leatherArmor: Item, data: String): ItemStack {
|
||||
val stack = ItemStack(leatherArmor)
|
||||
stack.set(DataComponentTypes.DYED_COLOR, DyedColorComponent(data.toInt(16), false))
|
||||
return stack
|
||||
}
|
||||
}
|
||||
61
src/main/kotlin/gui/entity/ModifyHorse.kt
Normal file
61
src/main/kotlin/gui/entity/ModifyHorse.kt
Normal file
@@ -0,0 +1,61 @@
|
||||
|
||||
package moe.nea.firmament.gui.entity
|
||||
|
||||
import com.google.gson.JsonNull
|
||||
import com.google.gson.JsonObject
|
||||
import kotlin.experimental.and
|
||||
import kotlin.experimental.inv
|
||||
import kotlin.experimental.or
|
||||
import net.minecraft.entity.EntityType
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.passive.AbstractHorseEntity
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
import moe.nea.firmament.gui.entity.EntityRenderer.fakeWorld
|
||||
|
||||
object ModifyHorse : EntityModifier {
|
||||
override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
|
||||
require(entity is AbstractHorseEntity)
|
||||
var entity: AbstractHorseEntity = entity
|
||||
info["kind"]?.let {
|
||||
entity = when (it.asString) {
|
||||
"skeleton" -> EntityType.SKELETON_HORSE.create(fakeWorld)!!
|
||||
"zombie" -> EntityType.ZOMBIE_HORSE.create(fakeWorld)!!
|
||||
"mule" -> EntityType.MULE.create(fakeWorld)!!
|
||||
"donkey" -> EntityType.DONKEY.create(fakeWorld)!!
|
||||
"horse" -> EntityType.HORSE.create(fakeWorld)!!
|
||||
else -> error("Unknown horse kind $it")
|
||||
}
|
||||
}
|
||||
info["armor"]?.let {
|
||||
if (it is JsonNull) {
|
||||
entity.setHorseArmor(ItemStack.EMPTY)
|
||||
} else {
|
||||
when (it.asString) {
|
||||
"iron" -> entity.setHorseArmor(ItemStack(Items.IRON_HORSE_ARMOR))
|
||||
"golden" -> entity.setHorseArmor(ItemStack(Items.GOLDEN_HORSE_ARMOR))
|
||||
"diamond" -> entity.setHorseArmor(ItemStack(Items.DIAMOND_HORSE_ARMOR))
|
||||
else -> error("Unknown horse armor $it")
|
||||
}
|
||||
}
|
||||
}
|
||||
info["saddled"]?.let {
|
||||
entity.setIsSaddled(it.asBoolean)
|
||||
}
|
||||
return entity
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun AbstractHorseEntity.setIsSaddled(shouldBeSaddled: Boolean) {
|
||||
val oldFlag = dataTracker.get(AbstractHorseEntity.HORSE_FLAGS)
|
||||
dataTracker.set(
|
||||
AbstractHorseEntity.HORSE_FLAGS,
|
||||
if (shouldBeSaddled) oldFlag or AbstractHorseEntity.SADDLED_FLAG.toByte()
|
||||
else oldFlag and AbstractHorseEntity.SADDLED_FLAG.toByte().inv()
|
||||
)
|
||||
}
|
||||
|
||||
fun AbstractHorseEntity.setHorseArmor(itemStack: ItemStack) {
|
||||
items.setStack(1, itemStack)
|
||||
}
|
||||
13
src/main/kotlin/gui/entity/ModifyInvisible.kt
Normal file
13
src/main/kotlin/gui/entity/ModifyInvisible.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
package moe.nea.firmament.gui.entity
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.entity.LivingEntity
|
||||
|
||||
object ModifyInvisible : EntityModifier {
|
||||
override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
|
||||
entity.isInvisible = info.get("invisible")?.asBoolean ?: true
|
||||
return entity
|
||||
}
|
||||
|
||||
}
|
||||
14
src/main/kotlin/gui/entity/ModifyName.kt
Normal file
14
src/main/kotlin/gui/entity/ModifyName.kt
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
package moe.nea.firmament.gui.entity
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.text.Text
|
||||
|
||||
object ModifyName : EntityModifier {
|
||||
override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
|
||||
entity.customName = Text.literal(info.get("name").asString)
|
||||
return entity
|
||||
}
|
||||
|
||||
}
|
||||
47
src/main/kotlin/gui/entity/ModifyPlayerSkin.kt
Normal file
47
src/main/kotlin/gui/entity/ModifyPlayerSkin.kt
Normal file
@@ -0,0 +1,47 @@
|
||||
|
||||
package moe.nea.firmament.gui.entity
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
import kotlin.experimental.and
|
||||
import kotlin.experimental.or
|
||||
import net.minecraft.client.util.SkinTextures
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.entity.player.PlayerModelPart
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
object ModifyPlayerSkin : EntityModifier {
|
||||
val playerModelPartIndex = PlayerModelPart.entries.associateBy { it.getName() }
|
||||
override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
|
||||
require(entity is GuiPlayer)
|
||||
info["cape"]?.let {
|
||||
entity.capeTexture = Identifier.of(it.asString)
|
||||
}
|
||||
info["skin"]?.let {
|
||||
entity.skinTexture = Identifier.of(it.asString)
|
||||
}
|
||||
info["slim"]?.let {
|
||||
entity.model = if (it.asBoolean) SkinTextures.Model.SLIM else SkinTextures.Model.WIDE
|
||||
}
|
||||
info["parts"]?.let {
|
||||
var trackedData = entity.dataTracker.get(PlayerEntity.PLAYER_MODEL_PARTS)
|
||||
if (it is JsonPrimitive && it.isBoolean) {
|
||||
trackedData = (if (it.asBoolean) -1 else 0).toByte()
|
||||
} else {
|
||||
val obj = it.asJsonObject
|
||||
for ((k, v) in obj.entrySet()) {
|
||||
val part = playerModelPartIndex[k]!!
|
||||
trackedData = if (v.asBoolean) {
|
||||
trackedData and (part.bitFlag.inv().toByte())
|
||||
} else {
|
||||
trackedData or (part.bitFlag.toByte())
|
||||
}
|
||||
}
|
||||
}
|
||||
entity.dataTracker.set(PlayerEntity.PLAYER_MODEL_PARTS, trackedData)
|
||||
}
|
||||
return entity
|
||||
}
|
||||
|
||||
}
|
||||
15
src/main/kotlin/gui/entity/ModifyRiding.kt
Normal file
15
src/main/kotlin/gui/entity/ModifyRiding.kt
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
package moe.nea.firmament.gui.entity
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.entity.LivingEntity
|
||||
|
||||
object ModifyRiding : EntityModifier {
|
||||
override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
|
||||
val newEntity = EntityRenderer.constructEntity(info)
|
||||
require(newEntity != null)
|
||||
newEntity.startRiding(entity, true)
|
||||
return entity
|
||||
}
|
||||
|
||||
}
|
||||
20
src/main/kotlin/gui/entity/ModifyWither.kt
Normal file
20
src/main/kotlin/gui/entity/ModifyWither.kt
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
package moe.nea.firmament.gui.entity
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.boss.WitherEntity
|
||||
|
||||
object ModifyWither : EntityModifier {
|
||||
override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
|
||||
require(entity is WitherEntity)
|
||||
info["tiny"]?.let {
|
||||
entity.setInvulTimer(if (it.asBoolean) 800 else 0)
|
||||
}
|
||||
info["armored"]?.let {
|
||||
entity.health = if (it.asBoolean) 1F else entity.maxHealth
|
||||
}
|
||||
return entity
|
||||
}
|
||||
|
||||
}
|
||||
66
src/main/kotlin/gui/hud/MoulConfigHud.kt
Normal file
66
src/main/kotlin/gui/hud/MoulConfigHud.kt
Normal file
@@ -0,0 +1,66 @@
|
||||
|
||||
package moe.nea.firmament.gui.hud
|
||||
|
||||
import io.github.notenoughupdates.moulconfig.gui.GuiComponentWrapper
|
||||
import io.github.notenoughupdates.moulconfig.gui.GuiContext
|
||||
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.resource.SynchronousResourceReloader
|
||||
import moe.nea.firmament.events.FinalizeResourceManagerEvent
|
||||
import moe.nea.firmament.events.HudRenderEvent
|
||||
import moe.nea.firmament.gui.config.HudMeta
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.MoulConfigUtils
|
||||
|
||||
abstract class MoulConfigHud(
|
||||
val name: String,
|
||||
val hudMeta: HudMeta,
|
||||
) {
|
||||
companion object {
|
||||
private val componentWrapper by lazy {
|
||||
object : GuiComponentWrapper(GuiContext(TextComponent("§cERROR"))) {
|
||||
init {
|
||||
this.client = MC.instance
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var fragment: GuiContext? = null
|
||||
|
||||
fun forceInit() {
|
||||
}
|
||||
|
||||
open fun shouldRender(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
init {
|
||||
require(name.matches("^[a-z_/]+$".toRegex()))
|
||||
HudRenderEvent.subscribe {
|
||||
if (!shouldRender()) return@subscribe
|
||||
val renderContext = componentWrapper.createContext(it.context)
|
||||
if (fragment == null)
|
||||
loadFragment()
|
||||
it.context.matrices.push()
|
||||
hudMeta.applyTransformations(it.context.matrices)
|
||||
val renderContextTranslated =
|
||||
renderContext.translated(hudMeta.absoluteX, hudMeta.absoluteY, hudMeta.width, hudMeta.height)
|
||||
.scaled(hudMeta.scale)
|
||||
fragment!!.root.render(renderContextTranslated)
|
||||
it.context.matrices.pop()
|
||||
}
|
||||
FinalizeResourceManagerEvent.subscribe {
|
||||
MC.resourceManager.registerReloader(object : SynchronousResourceReloader {
|
||||
override fun reload(manager: ResourceManager?) {
|
||||
fragment = null
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun loadFragment() {
|
||||
fragment = MoulConfigUtils.loadGui(name, this)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user