From 4d12e6de80bc2b52ddb97bed1650ade379aa46a7 Mon Sep 17 00:00:00 2001 From: toxicity Date: Mon, 25 Nov 2024 00:59:57 +0900 Subject: [PATCH] Add 'children' configuration in image. --- .../hud/api/component/PixelComponent.java | 6 ++ .../bootstrap/bukkit/BukkitBootstrapImpl.kt | 4 +- changelog/1.10.md | 42 ++++++++ .../hud/compass/type/CircleCompass.kt | 36 +++---- .../kr/toxicity/hud/equation/EquationPair.kt | 4 +- .../hud/equation/EquationPairLocation.kt | 4 +- .../toxicity/hud/equation/EquationTriple.kt | 6 +- .../kr/toxicity/hud/hud/HudHeadElement.kt | 2 +- .../kr/toxicity/hud/hud/HudImageElement.kt | 101 +++++++++--------- .../kotlin/kr/toxicity/hud/hud/HudImpl.kt | 8 +- .../kotlin/kr/toxicity/hud/image/HudImage.kt | 31 +++++- .../kr/toxicity/hud/image/ImageComponent.kt | 86 +++++++++++++++ .../kr/toxicity/hud/image/enums/ImageType.kt | 29 ++--- .../kr/toxicity/hud/layout/HeadLayout.kt | 4 +- .../kr/toxicity/hud/layout/HudLayout.kt | 4 +- .../kr/toxicity/hud/layout/ImageLayout.kt | 8 +- .../kr/toxicity/hud/layout/LayoutGroup.kt | 12 +-- .../kr/toxicity/hud/layout/TextLayout.kt | 20 ++-- .../location/animation/AnimationLocation.kt | 2 +- .../toxicity/hud/manager/ConfigManagerImpl.kt | 29 +++-- .../hud/manager/DatabaseManagerImpl.kt | 2 +- .../kr/toxicity/hud/manager/ImageManager.kt | 18 ++-- .../hud/manager/ListenerManagerImpl.kt | 2 +- .../toxicity/hud/manager/ShaderManagerImpl.kt | 4 +- .../toxicity/hud/manager/TextManagerImpl.kt | 10 +- .../hud/manager/TriggerManagerImpl.kt | 2 +- .../kr/toxicity/hud/placeholder/Conditions.kt | 14 +-- .../kotlin/kr/toxicity/hud/popup/PopupImpl.kt | 20 ++-- .../kr/toxicity/hud/popup/PopupLayout.kt | 71 ++++++------ .../kr/toxicity/hud/renderer/ImageRenderer.kt | 40 +++---- .../kr/toxicity/hud/shader/RenderScale.kt | 2 +- 31 files changed, 391 insertions(+), 232 deletions(-) create mode 100644 changelog/1.10.md create mode 100644 dist/src/main/kotlin/kr/toxicity/hud/image/ImageComponent.kt diff --git a/api/standard-api/src/main/java/kr/toxicity/hud/api/component/PixelComponent.java b/api/standard-api/src/main/java/kr/toxicity/hud/api/component/PixelComponent.java index f1321a04..f08ad35b 100644 --- a/api/standard-api/src/main/java/kr/toxicity/hud/api/component/PixelComponent.java +++ b/api/standard-api/src/main/java/kr/toxicity/hud/api/component/PixelComponent.java @@ -9,4 +9,10 @@ * @param pixel pixel */ public record PixelComponent(@NotNull WidthComponent component, int pixel) { + public @NotNull PixelComponent plus(@NotNull WidthComponent other) { + return new PixelComponent( + component.plus(other), + pixel + ); + } } diff --git a/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/BukkitBootstrapImpl.kt b/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/BukkitBootstrapImpl.kt index 2854d703..cfc880c6 100644 --- a/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/BukkitBootstrapImpl.kt +++ b/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/BukkitBootstrapImpl.kt @@ -110,8 +110,8 @@ class BukkitBootstrapImpl : BukkitBootstrap, JavaPlugin() { if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { DATA_FOLDER.subFolder("placeholders").forEachAllYaml(CONSOLE) { file, s, yamlObject -> runWithExceptionHandling(CONSOLE, "Unable to read this placeholder task: $s in ${file.name}") { - val variable = yamlObject.get("variable")?.asString().ifNull("variable not set.") - val placeholder = yamlObject.get("placeholder")?.asString().ifNull("placeholder not set.") + val variable = yamlObject["variable"]?.asString().ifNull("variable not set.") + val placeholder = yamlObject["placeholder"]?.asString().ifNull("placeholder not set.") val update = yamlObject.getAsInt("update", 1).coerceAtLeast(1) val async = yamlObject.getAsBoolean("async", false) updateTask.add(object : PlaceholderTask { diff --git a/changelog/1.10.md b/changelog/1.10.md new file mode 100644 index 00000000..b0c8352c --- /dev/null +++ b/changelog/1.10.md @@ -0,0 +1,42 @@ +# BetterHud 1.10 + +## Notice +- Now support about Oraxen is dropped. +- Now support about Nexo is available. + +## Add +- Add 'children' in image. +```yaml +children_full: + type: single + file: "children/full.png" #parent image + setting: + children: #define the children you want use. + - children_half + - children_container + children-mapper: #replace the image to child if this condition is matched. + children_container: + 1: + first: health_percentage + second: 0.33 + operation: "<" + children_half: + 1: + first: health_percentage + second: 0.66 + operation: "<" +children_half: + type: single + file: "children/half.png" +children_container: + type: single + file: "children/container.png" +``` +```yaml +children_full: + type: single + file: "children/full.png" + setting: + children: * #wild card(*) defines all available image. + follow: "skript_variable:your_value" #If you want to show image depend on some placeholder, set 'follow' section. +``` \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/compass/type/CircleCompass.kt b/dist/src/main/kotlin/kr/toxicity/hud/compass/type/CircleCompass.kt index 278175ea..96d0b43b 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/compass/type/CircleCompass.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/compass/type/CircleCompass.kt @@ -50,25 +50,25 @@ class CircleCompass( private val scale = section.getAsDouble("scale", 1.0).apply { if (this <= 0) throw RuntimeException("scale cannot be <= 0") } - private val scaleEquation = section.get("scale-equation")?.asString()?.let { + private val scaleEquation = section["scale-equation"]?.asString()?.let { TEquation(it) } ?: TEquation.one - private val colorEquation = section.get("color-equation")?.asObject()?.let { + private val colorEquation = section["color-equation"]?.asObject()?.let { ColorEquation(it) } ?: defaultColorEquation private val space = section.getAsInt("space", 2).coerceAtLeast(0) - private val pixel = PixelLocation(section.get("pixel")?.asObject().ifNull("pixel value not set.")) + PixelLocation.hotBarHeight + private val pixel = PixelLocation(section["pixel"]?.asObject().ifNull("pixel value not set.")) + PixelLocation.hotBarHeight private val shader = HudShader( - GuiLocation(section.get("gui")?.asObject().ifNull("gui value not set.")), + GuiLocation(section["gui"]?.asObject().ifNull("gui value not set.")), RenderScale.fromConfig(pixel, section), section.getAsInt("layer", 0), section.getAsBoolean("outline", false), pixel.opacity, - ShaderProperty.properties(section.get("properties")?.asArray()) + ShaderProperty.properties(section["properties"]?.asArray()) ) private var array: JsonArray? = JsonArray() - private val images = CompassImage(assets, section.get("file")?.asObject().ifNull("file value not set.")) + private val images = CompassImage(assets, section["file"]?.asObject().ifNull("file value not set.")) private val conditions = section.toConditions().build(UpdateEvent.EMPTY) private val isDefault = ConfigManagerImpl.defaultCompass.contains(internalName) || section.getAsBoolean("default", false) @@ -119,37 +119,37 @@ class CircleCompass( override fun getName(): String = internalName private inner class CompassImage(assets: File, section: YamlObject) { - val n = section.get("n")?.asObject()?.let { + val n = section["n"]?.asObject()?.let { CompassImageMap(assets, "n", it) } - val e = section.get("e")?.asObject()?.let { + val e = section["e"]?.asObject()?.let { CompassImageMap(assets, "e", it) } - val s = section.get("s")?.asObject()?.let { + val s = section["s"]?.asObject()?.let { CompassImageMap(assets, "s", it) } - val w = section.get("w")?.asObject()?.let { + val w = section["w"]?.asObject()?.let { CompassImageMap(assets, "w", it) } - val nw = section.get("nw")?.asObject()?.let { + val nw = section["nw"]?.asObject()?.let { CompassImageMap(assets, "nw", it) } - val ne = section.get("ne")?.asObject()?.let { + val ne = section["ne"]?.asObject()?.let { CompassImageMap(assets, "ne", it) } - val sw = section.get("sw")?.asObject()?.let { + val sw = section["sw"]?.asObject()?.let { CompassImageMap(assets, "sw", it) } - val se = section.get("se")?.asObject()?.let { + val se = section["se"]?.asObject()?.let { CompassImageMap(assets, "se", it) } - val chain = section.get("chain")?.asObject()?.let { + val chain = section["chain"]?.asObject()?.let { CompassImageMap(assets, "chain", it) } - val point = section.get("point")?.asObject()?.let { + val point = section["point"]?.asObject()?.let { CompassImageMap(assets, "point", it) } - val customIcon = section.get("custom-icon")?.asObject()?.associate { + val customIcon = section["custom-icon"]?.asObject()?.associate { it.key to CompassImageMap(assets, "custom_icon_${it.key}", it.value.asObject()) } ?: emptyMap() @@ -199,7 +199,7 @@ class CircleCompass( section: YamlObject ) { val map = run { - val fileName = section.get("name")?.asString().ifNull("name value not set.").replace('/', File.separatorChar) + val fileName = section["name"]?.asString().ifNull("name value not set.").replace('/', File.separatorChar) val scale = section.getAsDouble("scale", 1.0).apply { if (this <= 0.0) throw RuntimeException("scale cannot be <= 0.0") } diff --git a/dist/src/main/kotlin/kr/toxicity/hud/equation/EquationPair.kt b/dist/src/main/kotlin/kr/toxicity/hud/equation/EquationPair.kt index e728e35e..6eb23f3b 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/equation/EquationPair.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/equation/EquationPair.kt @@ -12,7 +12,7 @@ class EquationPair(val x: TEquation, val y: TEquation) { fun evaluate(d: Double) = x.evaluate(d) to y.evaluate(d) constructor(section: YamlObject): this( - section.get("x-equation")?.asString().ifNull("x-equation value not set.").toEquation(), - section.get("y-equation")?.asString().ifNull("y-equation value not set.").toEquation() + section["x-equation"]?.asString().ifNull("x-equation value not set.").toEquation(), + section["y-equation"]?.asString().ifNull("y-equation value not set.").toEquation() ) } \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/equation/EquationPairLocation.kt b/dist/src/main/kotlin/kr/toxicity/hud/equation/EquationPairLocation.kt index 50914321..1cc55d58 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/equation/EquationPairLocation.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/equation/EquationPairLocation.kt @@ -25,10 +25,10 @@ class EquationPairLocation( constructor(section: YamlObject): this( section.getAsInt("duration", 1).coerceAtLeast(1), - section.get("gui")?.asObject()?.let { + section["gui"]?.asObject()?.let { EquationPair(it) } ?: EquationPair.zero, - section.get("pixel")?.asObject()?.let { + section["pixel"]?.asObject()?.let { EquationTriple(it) } ?: EquationTriple.zero ) diff --git a/dist/src/main/kotlin/kr/toxicity/hud/equation/EquationTriple.kt b/dist/src/main/kotlin/kr/toxicity/hud/equation/EquationTriple.kt index 08c9daea..056d8393 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/equation/EquationTriple.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/equation/EquationTriple.kt @@ -12,8 +12,8 @@ class EquationTriple(val x: TEquation, val y: TEquation, val opacity: TEquation) fun evaluate(d: Double) = Triple(x.evaluate(d), y.evaluate(d), opacity.evaluate(d)) constructor(section: YamlObject): this( - section.get("x-equation")?.asString().ifNull("x-equation value not set.").toEquation(), - section.get("y-equation")?.asString().ifNull("y-equation value not set.").toEquation(), - section.get("opacity-equation")?.asString()?.toEquation() ?: TEquation.one + section["x-equation"]?.asString().ifNull("x-equation value not set.").toEquation(), + section["y-equation"]?.asString().ifNull("y-equation value not set.").toEquation(), + section["opacity-equation"]?.asString()?.toEquation() ?: TEquation.one ) } \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/hud/HudHeadElement.kt b/dist/src/main/kotlin/kr/toxicity/hud/hud/HudHeadElement.kt index 59af46ee..c32b5833 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/hud/HudHeadElement.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/hud/HudHeadElement.kt @@ -95,7 +95,7 @@ class HudHeadElement(parent: HudImpl, private val head: HeadLayout, gui: GuiLoca head.type, head.follow, head.cancelIfFollowerNotExists, - head.conditions.and(head.head.conditions) + head.conditions and head.head.conditions ).getHead(UpdateEvent.EMPTY) } diff --git a/dist/src/main/kotlin/kr/toxicity/hud/hud/HudImageElement.kt b/dist/src/main/kotlin/kr/toxicity/hud/hud/HudImageElement.kt index b5039934..7d316380 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/hud/HudImageElement.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/hud/HudImageElement.kt @@ -4,6 +4,8 @@ import kr.toxicity.hud.api.component.PixelComponent import kr.toxicity.hud.api.component.WidthComponent import kr.toxicity.hud.api.player.HudPlayer import kr.toxicity.hud.api.update.UpdateEvent +import kr.toxicity.hud.image.HudImage +import kr.toxicity.hud.image.ImageComponent import kr.toxicity.hud.location.PixelLocation import kr.toxicity.hud.layout.ImageLayout import kr.toxicity.hud.manager.ImageManager @@ -15,68 +17,71 @@ import kr.toxicity.hud.util.* import net.kyori.adventure.text.Component import kotlin.math.roundToInt -class HudImageElement(parent: HudImpl, private val image: ImageLayout, gui: GuiLocation, pixel: PixelLocation) { +class HudImageElement(parent: HudImpl, private val imageLayout: ImageLayout, gui: GuiLocation, pixel: PixelLocation) { private val chars = run { - val hud = image.image - val finalPixel = image.location + pixel + val hud = imageLayout.image + val finalPixel = imageLayout.location + pixel val shader = HudShader( gui, - image.renderScale, - image.layer, - image.outline, + imageLayout.renderScale, + imageLayout.layer, + imageLayout.outline, finalPixel.opacity, - image.property + imageLayout.property ) - - val list = ArrayList() - if (hud.listener != null) { - list.add(EMPTY_PIXEL_COMPONENT) - } val negativeSpace = parent.getOrCreateSpace(-1) - hud.image.forEach { pair -> - val fileName = "$NAME_SPACE_ENCODED:${pair.name}" - val height = (pair.image.image.height.toDouble() * image.scale).roundToInt() - val scale = height.toDouble() / pair.image.image.height - val ascent = finalPixel.y.coerceAtLeast(-HudImpl.ADD_HEIGHT).coerceAtMost(HudImpl.ADD_HEIGHT) - val shaderGroup = ShaderGroup(shader, fileName, image.scale, ascent) + fun HudImage.toComponent(): ImageComponent { + val list = ArrayList() + if (hud.listener != null) { + list.add(EMPTY_PIXEL_COMPONENT) + } + image.forEach { pair -> + val fileName = "$NAME_SPACE_ENCODED:${pair.name}" + val height = (pair.image.image.height.toDouble() * imageLayout.scale).roundToInt() + val scale = height.toDouble() / pair.image.image.height + val ascent = finalPixel.y.coerceAtLeast(-HudImpl.ADD_HEIGHT).coerceAtMost(HudImpl.ADD_HEIGHT) + val shaderGroup = ShaderGroup(shader, fileName, imageLayout.scale, ascent) - val component = ImageManager.getImage(shaderGroup) ?: run { - val c = (++parent.imageChar).parseChar() - val comp = Component.text() - .font(parent.imageKey) - val finalWidth = WidthComponent( - if (BOOTSTRAP.useLegacyFont()) comp.content(c).append(NEGATIVE_ONE_SPACE_COMPONENT.component) else comp.content("$c$negativeSpace"), - (pair.image.image.width.toDouble() * scale).roundToInt() - ) - parent.jsonArray?.let { array -> - HudImpl.createBit(shader, ascent) { y -> - array.add(jsonObjectOf( - "type" to "bitmap", - "file" to fileName, - "ascent" to y, - "height" to height, - "chars" to jsonArrayOf(c) - )) + val component = ImageManager.getImage(shaderGroup) ?: run { + val c = (++parent.imageChar).parseChar() + val comp = Component.text() + .font(parent.imageKey) + val finalWidth = WidthComponent( + if (BOOTSTRAP.useLegacyFont()) comp.content(c).append(NEGATIVE_ONE_SPACE_COMPONENT.component) else comp.content("$c$negativeSpace"), + (pair.image.image.width.toDouble() * scale).roundToInt() + ) + parent.jsonArray?.let { array -> + HudImpl.createBit(shader, ascent) { y -> + array.add(jsonObjectOf( + "type" to "bitmap", + "file" to fileName, + "ascent" to y, + "height" to height, + "chars" to jsonArrayOf(c) + )) + } } + ImageManager.setImage(shaderGroup, finalWidth) + finalWidth } - ImageManager.setImage(shaderGroup, finalWidth) - finalWidth - } - list.add(component.toPixelComponent(finalPixel.x + (pair.image.xOffset * scale).roundToInt())) + list.add(component.toPixelComponent(finalPixel.x + (pair.image.xOffset * scale).roundToInt())) + } + return ImageComponent(this, list, children.entries.associate { + it.key to it.value.toComponent() + }) } val renderer = ImageRenderer( - hud, - image.color, - image.space, - image.stack, - image.maxStack, - list, - image.follow, - image.cancelIfFollowerNotExists, - image.conditions.and(image.image.conditions) + imageLayout.color, + imageLayout.space, + imageLayout.stack, + imageLayout.maxStack, + imageLayout.image.toComponent(), + imageLayout.follow, + imageLayout.cancelIfFollowerNotExists, + imageLayout.conditions and imageLayout.image.conditions ) renderer.max() to renderer.getComponent(UpdateEvent.EMPTY) } diff --git a/dist/src/main/kotlin/kr/toxicity/hud/hud/HudImpl.kt b/dist/src/main/kotlin/kr/toxicity/hud/hud/HudImpl.kt index 30277519..1818f908 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/hud/HudImpl.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/hud/HudImpl.kt @@ -50,15 +50,15 @@ class HudImpl( (++imageChar).parseChar() } - private val elements = section.get("layouts")?.asObject().ifNull("layout configuration not set.").mapSubConfiguration { s, yamlObject -> - val layout = yamlObject.get("name")?.asString().ifNull("name value not set: $s").let { + private val elements = section["layouts"]?.asObject().ifNull("layout configuration not set.").mapSubConfiguration { s, yamlObject -> + val layout = yamlObject["name"]?.asString().ifNull("name value not set: $s").let { LayoutManager.getLayout(it).ifNull("this layout doesn't exist: $it") } var gui = GuiLocation(yamlObject) - yamlObject.get("gui")?.asObject()?.let { + yamlObject["gui"]?.asObject()?.let { gui += GuiLocation(it) } - val pixel = yamlObject.get("pixel")?.asObject()?.let { + val pixel = yamlObject["pixel"]?.asObject()?.let { PixelLocation(it) } ?: PixelLocation.zero HudAnimation( diff --git a/dist/src/main/kotlin/kr/toxicity/hud/image/HudImage.kt b/dist/src/main/kotlin/kr/toxicity/hud/image/HudImage.kt index a4c24ba3..ed33a18b 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/image/HudImage.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/image/HudImage.kt @@ -1,9 +1,15 @@ package kr.toxicity.hud.image +import kr.toxicity.hud.api.yaml.YamlArray +import kr.toxicity.hud.api.yaml.YamlElement import kr.toxicity.hud.api.yaml.YamlObject import kr.toxicity.hud.configuration.HudConfiguration import kr.toxicity.hud.image.enums.ImageType +import kr.toxicity.hud.manager.ImageManager import kr.toxicity.hud.manager.ListenerManagerImpl +import kr.toxicity.hud.manager.PlaceholderManagerImpl +import kr.toxicity.hud.placeholder.Conditions +import kr.toxicity.hud.util.ifNull import kr.toxicity.hud.util.toConditions class HudImage( @@ -14,7 +20,30 @@ class HudImage( setting: YamlObject ) : HudConfiguration { val conditions = setting.toConditions() - val listener = setting.get("listener")?.asObject()?.let { + val listener = setting["listener"]?.asObject()?.let { ListenerManagerImpl.getListener(it) } + val children by lazy { + fun Iterable.asMap() = associate { + val str = it.asString() + if (str == name) throw RuntimeException("circular image reference: $name") + str to ImageManager.getImage(str).ifNull("This children image doesn't exist in $name: $str") + } + when (val child = setting["children"]) { + is YamlArray -> child.asMap() + is YamlElement -> if (child.asString() == "*") ImageManager.allImage.associateBy { + it.name + } else listOf(child).asMap() + null -> emptyMap() + else -> throw RuntimeException("Unsupported children section: $name") + } + } + val follow = setting["follow"]?.asString()?.let { + PlaceholderManagerImpl.find(it).apply { + if (!java.lang.String::class.java.isAssignableFrom(clazz)) throw RuntimeException("This placeholder is not a string in image $name: $it") + } + } + val childrenMapper = setting["children-mapper"]?.asObject()?.map { + it.key to Conditions.parse(it.value.asObject()) + } } \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/image/ImageComponent.kt b/dist/src/main/kotlin/kr/toxicity/hud/image/ImageComponent.kt new file mode 100644 index 00000000..a38d34de --- /dev/null +++ b/dist/src/main/kotlin/kr/toxicity/hud/image/ImageComponent.kt @@ -0,0 +1,86 @@ +package kr.toxicity.hud.image + +import kr.toxicity.hud.api.component.PixelComponent +import kr.toxicity.hud.api.component.WidthComponent +import kr.toxicity.hud.api.listener.HudListener +import kr.toxicity.hud.api.player.HudPlayer +import kr.toxicity.hud.api.update.UpdateEvent +import kr.toxicity.hud.util.ifNull +import net.kyori.adventure.text.format.NamedTextColor +import net.kyori.adventure.text.format.TextColor + +class ImageComponent( + private val original: HudImage, + val images: List, + val children: Map, +) : Map by children { + val type = original.type + val listener = original.listener ?: HudListener.EMPTY.let { + { _ -> + it + } + } + val max + get(): Int = maxOf(images.maxOf { + it.component.width + }, children.values.maxOfOrNull { + it.max + } ?: 0) + + private fun PixelComponent.applyColor(color: TextColor) = PixelComponent(if (color.value() == NamedTextColor.WHITE.value()) component else WidthComponent( + component.component.build().toBuilder().color(color), + component.width + ), pixel) + + fun applyColor(color: TextColor): ImageComponent = ImageComponent( + original, + images.map { + it.applyColor(color) + }, + children.entries.associate { + it.key to it.value.applyColor(color) + } + ) + + private fun interface ImageMapper : (HudPlayer) -> ImageComponent + + private val childrenMapper: (UpdateEvent) -> ImageMapper = original.childrenMapper?.map { + children[it.first].ifNull("This children doesn't exist in ${original.name}: ${it.first}") to it.second + }?.let { + { event: UpdateEvent -> + it.map { builder -> + builder.first to builder.second.build(event) + }.let { buildList -> + ImageMapper { player -> + buildList.firstOrNull { pair -> + pair.second(player) + }?.first ?: this + } + } + } + } ?: { + ImageMapper { + this + } + } + + private class ImageMapperTree( + val defaultMapper: ImageMapper, + val children: Map + ) : Map by children + + fun imageMapper(event: UpdateEvent): (HudPlayer) -> ImageComponent { + val buildFollow = original.follow?.build(event) + val mapperTree = ImageMapperTree( + childrenMapper(event), + children.entries.associate { + it.key to it.value.childrenMapper(event) + } + ) + return { player -> + (buildFollow?.let { + mapperTree[it.value(player) as String] + } ?: mapperTree.defaultMapper)(player) + } + } +} \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/image/enums/ImageType.kt b/dist/src/main/kotlin/kr/toxicity/hud/image/enums/ImageType.kt index f13097aa..113516c2 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/image/enums/ImageType.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/image/enums/ImageType.kt @@ -3,43 +3,44 @@ package kr.toxicity.hud.image.enums import kr.toxicity.hud.api.component.PixelComponent import kr.toxicity.hud.api.listener.HudListener import kr.toxicity.hud.api.player.HudPlayer +import kr.toxicity.hud.image.ImageComponent import kr.toxicity.hud.util.EMPTY_PIXEL_COMPONENT import kotlin.math.roundToInt enum class ImageType { SINGLE { - override fun getComponent(listener: HudListener, frame: Int, list: List, player: HudPlayer): PixelComponent { + override fun getComponent(listener: HudListener, frame: Int, component: ImageComponent, player: HudPlayer): PixelComponent { val get = listener.getValue(player).run { - if (isNaN()) 0 else (this * list.lastIndex).roundToInt() + if (isNaN()) 0 else (this * component.images.lastIndex).roundToInt() } - return if (list.isNotEmpty()) { - if (get >= 0) list[get + return if (component.images.isNotEmpty()) { + if (get >= 0) component.images[get .coerceAtLeast(0) - .coerceAtMost(list.lastIndex)] else list[0] + .coerceAtMost(component.images.lastIndex)] else component.images[0] } else EMPTY_PIXEL_COMPONENT } }, LISTENER { - override fun getComponent(listener: HudListener, frame: Int, list: List, player: HudPlayer): PixelComponent { + override fun getComponent(listener: HudListener, frame: Int, component: ImageComponent, player: HudPlayer): PixelComponent { val get = listener.getValue(player).run { - if (isNaN()) 0 else (this * list.lastIndex).roundToInt() + if (isNaN()) 0 else (this * component.images.lastIndex).roundToInt() } - return if (get >= 0) list[get + return if (get >= 0) component.images[get .coerceAtLeast(0) - .coerceAtMost(list.lastIndex)] else list[frame % list.size] + .coerceAtMost(component.images.lastIndex)] else component.images[frame % component.images.size] } }, SEQUENCE { - override fun getComponent(listener: HudListener, frame: Int, list: List, player: HudPlayer): PixelComponent { + override fun getComponent(listener: HudListener, frame: Int, component: ImageComponent, player: HudPlayer): PixelComponent { val get = listener.getValue(player).run { - if (isNaN()) 0 else (this * list.lastIndex).roundToInt() + if (isNaN()) 0 else (this * component.images.lastIndex).roundToInt() } - return if (get >= 0) list[get + return if (get >= 0) component.images[get .coerceAtLeast(0) - .coerceAtMost(list.lastIndex)] else list[frame % list.size] + .coerceAtMost(component.images.lastIndex)] else component.images[frame % component.images.size] } } ; - abstract fun getComponent(listener: HudListener, frame: Int, list: List, player: HudPlayer): PixelComponent + abstract fun getComponent(listener: HudListener, frame: Int, component: ImageComponent, player: HudPlayer): PixelComponent } \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/layout/HeadLayout.kt b/dist/src/main/kotlin/kr/toxicity/hud/layout/HeadLayout.kt index 14c356c9..d781c7e7 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/layout/HeadLayout.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/layout/HeadLayout.kt @@ -14,12 +14,12 @@ class HeadLayout( yamlObject: YamlObject, loc: PixelLocation ) : HudLayout(loc, yamlObject) { - val head: HudHead = yamlObject.get("name")?.asString().ifNull("name value not set: $s").let { + val head: HudHead = yamlObject["name"]?.asString().ifNull("name value not set: $s").let { PlayerHeadManager.getHead(it).ifNull("this head doesn't exist: $it in $s") } val type = HeadRenderType.valueOf(yamlObject.getAsString("type", "standard").uppercase()) val align: LayoutAlign = when (type) { - HeadRenderType.STANDARD -> yamlObject.get("align")?.asString().toLayoutAlign() + HeadRenderType.STANDARD -> yamlObject["align"]?.asString().toLayoutAlign() HeadRenderType.FANCY -> LayoutAlign.CENTER } } \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/layout/HudLayout.kt b/dist/src/main/kotlin/kr/toxicity/hud/layout/HudLayout.kt index 2d626479..83473a29 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/layout/HudLayout.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/layout/HudLayout.kt @@ -10,8 +10,8 @@ import kr.toxicity.hud.util.toConditions abstract class HudLayout(originalLoc: PixelLocation, yaml: YamlObject) { val outline: Boolean = yaml.getAsBoolean("outline", false) val layer: Int = yaml.getAsInt("layer", 0) - val property: Int = ShaderProperty.properties(yaml.get("properties")?.asArray()) - val follow: String? = yaml.get("follow")?.asString() + val property: Int = ShaderProperty.properties(yaml["properties"]?.asArray()) + val follow: String? = yaml["follow"]?.asString() val location: PixelLocation = PixelLocation(yaml) + originalLoc + PixelLocation.hotBarHeight val cancelIfFollowerNotExists: Boolean = yaml.getAsBoolean("cancel-if-follower-not-exists", true) val conditions: ConditionBuilder = yaml.toConditions() diff --git a/dist/src/main/kotlin/kr/toxicity/hud/layout/ImageLayout.kt b/dist/src/main/kotlin/kr/toxicity/hud/layout/ImageLayout.kt index 3e742e5e..006669e6 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/layout/ImageLayout.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/layout/ImageLayout.kt @@ -16,18 +16,18 @@ class ImageLayout( yamlObject: YamlObject, loc: PixelLocation, ) : HudLayout(loc, yamlObject) { - val image: HudImage = yamlObject.get("name")?.asString().ifNull("name value not set: $s").let { n -> + val image: HudImage = yamlObject["name"]?.asString().ifNull("name value not set: $s").let { n -> ImageManager.getImage(n).ifNull("this image doesn't exist: $n") } - val color: TextColor = yamlObject.get("color")?.asString()?.toTextColor() ?: NamedTextColor.WHITE + val color: TextColor = yamlObject["color"]?.asString()?.toTextColor() ?: NamedTextColor.WHITE val scale: Double = yamlObject.getAsDouble("scale", 1.0) val space: Int = yamlObject.getAsInt("space", 1) - val stack: PlaceholderBuilder<*>? = yamlObject.get("stack")?.asString()?.let { + val stack: PlaceholderBuilder<*>? = yamlObject["stack"]?.asString()?.let { PlaceholderManagerImpl.find(it).ifNull("this placeholder doesn't exist: $it").apply { if (clazz != java.lang.Number::class.java) throw RuntimeException("this placeholder is not integer: $it") } } - val maxStack: PlaceholderBuilder<*>? = yamlObject.get("max-stack")?.asString()?.let { + val maxStack: PlaceholderBuilder<*>? = yamlObject["max-stack"]?.asString()?.let { PlaceholderManagerImpl.find(it).ifNull("this placeholder doesn't exist: $it").apply { if (clazz != java.lang.Number::class.java) throw RuntimeException("this placeholder is not integer: $it") } diff --git a/dist/src/main/kotlin/kr/toxicity/hud/layout/LayoutGroup.kt b/dist/src/main/kotlin/kr/toxicity/hud/layout/LayoutGroup.kt index 6adf9ef9..ec8c466d 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/layout/LayoutGroup.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/layout/LayoutGroup.kt @@ -17,30 +17,30 @@ class LayoutGroup( private val loc = PixelLocation(section) - val align = section.get("align")?.asString()?.let { + val align = section["align"]?.asString()?.let { runWithExceptionHandling(sender, "Unable to find that align: $it") { LayoutAlign.valueOf(it.uppercase()) }.getOrNull() } ?: LayoutAlign.LEFT - val offset = section.get("offset")?.asString()?.let { + val offset = section["offset"]?.asString()?.let { runWithExceptionHandling(sender, "Unable to find that offset: $it") { LayoutOffset.valueOf(it.uppercase()) }.getOrNull() } ?: LayoutOffset.CENTER - val image = section.get("images")?.asObject()?.mapSubConfiguration { s, yamlObject -> + val image = section["images"]?.asObject()?.mapSubConfiguration { s, yamlObject -> ImageLayout(s, yamlObject, loc) } ?: emptyList() - val text = section.get("texts")?.asObject()?.mapSubConfiguration { s, yamlObject -> + val text = section["texts"]?.asObject()?.mapSubConfiguration { s, yamlObject -> TextLayout(s, yamlObject, loc) } ?: emptyList() - val head = section.get("heads")?.asObject()?.mapSubConfiguration { s, yamlObject -> + val head = section["heads"]?.asObject()?.mapSubConfiguration { s, yamlObject -> HeadLayout(s, yamlObject, loc) } ?: emptyList() val conditions = section.toConditions() - val animation = section.get("animations")?.asObject()?.let { animations -> + val animation = section["animations"]?.asObject()?.let { animations -> AnimationLocation(animations) } ?: AnimationLocation.zero } \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/layout/TextLayout.kt b/dist/src/main/kotlin/kr/toxicity/hud/layout/TextLayout.kt index 723dd1c7..9457cff4 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/layout/TextLayout.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/layout/TextLayout.kt @@ -33,34 +33,34 @@ class TextLayout( ) } - val pattern: String = yamlObject.get("pattern")?.asString().ifNull("pattern value not set: $s") - val text: HudText = yamlObject.get("name")?.asString().ifNull("name value not set: $s").let { n -> + val pattern: String = yamlObject["pattern"]?.asString().ifNull("pattern value not set: $s") + val text: HudText = yamlObject["name"]?.asString().ifNull("name value not set: $s").let { n -> TextManagerImpl.getText(n).ifNull("this text doesn't exist: $n") } val scale: Double = yamlObject.getAsDouble("scale", 1.0) val space: Int = yamlObject.getAsInt("space", 0).coerceAtLeast(0) - val align: LayoutAlign = yamlObject.get("align")?.asString().toLayoutAlign() - val lineAlign: LayoutAlign = yamlObject.get("line-align")?.asString().toLayoutAlign() - val color: TextColor = yamlObject.get("color")?.asString()?.toTextColor() ?: NamedTextColor.WHITE - val numberEquation: TEquation = yamlObject.get("number-equation")?.asString()?.let { + val align: LayoutAlign = yamlObject["align"]?.asString().toLayoutAlign() + val lineAlign: LayoutAlign = yamlObject["line-align"]?.asString().toLayoutAlign() + val color: TextColor = yamlObject["color"]?.asString()?.toTextColor() ?: NamedTextColor.WHITE + val numberEquation: TEquation = yamlObject["number-equation"]?.asString()?.let { TEquation(it) } ?: TEquation.t - val numberFormat: DecimalFormat = yamlObject.get("number-format")?.asString()?.let { + val numberFormat: DecimalFormat = yamlObject["number-format"]?.asString()?.let { DecimalFormat(it) } ?: ConfigManagerImpl.numberFormat val disableNumberFormat: Boolean = yamlObject.getAsBoolean("disable-number-format", true) - val background: HudBackground? = yamlObject.get("background")?.asString()?.let { + val background: HudBackground? = yamlObject["background"]?.asString()?.let { BackgroundManager.getBackground(it) } val backgroundScale: Double = yamlObject.getAsDouble("background-scale", scale) - val emojiLocation: PixelLocation = yamlObject.get("emoji-pixel")?.asObject()?.let { + val emojiLocation: PixelLocation = yamlObject["emoji-pixel"]?.asObject()?.let { PixelLocation(it) } ?: PixelLocation.zero val emojiScale: Double = yamlObject.getAsDouble("emoji-scale", 1.0).apply { if (this <= 0) throw RuntimeException("emoji-scale cannot be <= 0") } val useLegacyFormat: Boolean = yamlObject.getAsBoolean("use-legacy-format", ConfigManagerImpl.useLegacyFormat) - val legacySerializer: ComponentDeserializer = yamlObject.get("legacy-serializer")?.asString()?.toLegacySerializer() ?: ConfigManagerImpl.legacySerializer + val legacySerializer: ComponentDeserializer = yamlObject["legacy-serializer"]?.asString()?.toLegacySerializer() ?: ConfigManagerImpl.legacySerializer val line = yamlObject.getAsInt("line", 1).apply { if (this < 1) throw RuntimeException("line cannot be < 1: $s") diff --git a/dist/src/main/kotlin/kr/toxicity/hud/location/animation/AnimationLocation.kt b/dist/src/main/kotlin/kr/toxicity/hud/location/animation/AnimationLocation.kt index c8b19c70..b0be4d82 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/location/animation/AnimationLocation.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/location/animation/AnimationLocation.kt @@ -29,7 +29,7 @@ class AnimationLocation( ) constructor(section: YamlObject): this( - section.get("type")?.asString()?.let { + section["type"]?.asString()?.let { AnimationType.valueOf(it.uppercase()) } ?: AnimationType.LOOP, section.getAsInt("duration", 20).coerceAtLeast(1), diff --git a/dist/src/main/kotlin/kr/toxicity/hud/manager/ConfigManagerImpl.kt b/dist/src/main/kotlin/kr/toxicity/hud/manager/ConfigManagerImpl.kt index 258b2e8f..ecb43505 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/manager/ConfigManagerImpl.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/manager/ConfigManagerImpl.kt @@ -7,7 +7,6 @@ import kr.toxicity.hud.pack.PackType import kr.toxicity.hud.resource.GlobalResource import kr.toxicity.hud.resource.KeyResource import kr.toxicity.hud.util.* -import kr.toxicity.hud.yaml.YamlObjectImpl import net.kyori.adventure.audience.Audience import net.kyori.adventure.text.Component import net.kyori.adventure.text.format.NamedTextColor @@ -120,32 +119,32 @@ object ConfigManagerImpl : BetterHudManager, ConfigManager { needToUpdatePack = false val yaml = PluginConfiguration.CONFIG.create() debug = yaml.getAsBoolean("debug", false) - defaultHud = yaml.get("default-hud")?.asArray()?.map { + defaultHud = yaml["default-hud"]?.asArray()?.map { it.asString() } ?: emptyList() - defaultPopup = yaml.get("default-popup")?.asArray()?.map { + defaultPopup = yaml["default-popup"]?.asArray()?.map { it.asString() } ?: emptyList() - defaultCompass = yaml.get("default-compass")?.asArray()?.map { + defaultCompass = yaml["default-compass"]?.asArray()?.map { it.asString() } ?: emptyList() - yaml.get("default-font-name")?.asString()?.let { + yaml["default-font-name"]?.asString()?.let { if (defaultFontName != it) needToUpdatePack = true defaultFontName = it } - yaml.get("pack-type")?.asString()?.let { + yaml["pack-type"]?.asString()?.let { runWithExceptionHandling(CONSOLE, "Unable to find this pack type: $it") { packType = PackType.valueOf(it.uppercase()) } } tickSpeed = yaml.getAsLong("tick-speed", 1) - numberFormat = yaml.get("number-format")?.asString()?.let { + numberFormat = yaml["number-format"]?.asString()?.let { runWithExceptionHandling(CONSOLE, "Unable to read this number-format: $it") { DecimalFormat(it) }.getOrNull() } ?: DecimalFormat("#,###.#") disableToBedrockPlayer = yaml.getAsBoolean("disable-to-bedrock-player", true) - yaml.get("build-folder-location")?.asString()?.let { + yaml["build-folder-location"]?.asString()?.let { buildFolderLocation = it.replace('/', File.separatorChar) } line = yaml.getAsInt("bossbar-line", 1).coerceAtLeast(1).coerceAtMost(7) @@ -161,10 +160,10 @@ object ConfigManagerImpl : BetterHudManager, ConfigManager { enableProtection = yaml.getAsBoolean("enable-protection", false) mergeBossBar = yaml.getAsBoolean("merge-boss-bar", true) enableSelfHost = yaml.getAsBoolean("enable-self-host", false) - mergeOtherFolders = yaml.get("merge-other-folders")?.asArray()?.map { + mergeOtherFolders = yaml["merge-other-folders"]?.asArray()?.map { it.asString() } ?: emptyList() - yaml.get("self-host-ip")?.asString()?.let { ip -> + yaml["self-host-ip"]?.asString()?.let { ip -> selfHostIp = ip } selfHostPort = yaml.getAsInt("self-host-port", 8163) @@ -175,22 +174,22 @@ object ConfigManagerImpl : BetterHudManager, ConfigManager { } else { BOOTSTRAP.endMetrics() } - yaml.get("loading-head")?.asString()?.let { + yaml["loading-head"]?.asString()?.let { loadingHead = it } clearBuildFolder = yaml.getAsBoolean("clear-build-folder", true) loadMinecraftDefaultTextures = yaml.getAsBoolean("load-minecraft-default-textures", true) - includedMinecraftTextures = yaml.get("included-minecraft-list")?.asArray()?.map { + includedMinecraftTextures = yaml["included-minecraft-list"]?.asArray()?.map { it.asString() } ?: emptyList() useLegacyFormat = yaml.getAsBoolean("use-legacy-format", true) - yaml.get("legacy-serializer")?.asString()?.let { + yaml["legacy-serializer"]?.asString()?.let { runWithExceptionHandling(CONSOLE, "Unable to find legacy serializer.") { legacySerializer = it.toLegacySerializer() } } - key = KeyResource(yaml.get("namespace")?.asString() ?: NAME_SPACE) - minecraftJarVersion = yaml.get("minecraft-jar-version")?.asString() ?: "bukkit" + key = KeyResource(yaml["namespace"]?.asString() ?: NAME_SPACE) + minecraftJarVersion = yaml["minecraft-jar-version"]?.asString() ?: "bukkit" removeDefaultHotbar = yaml.getAsBoolean("remove-default-hotbar", false) }.onFailure { e -> warn( diff --git a/dist/src/main/kotlin/kr/toxicity/hud/manager/DatabaseManagerImpl.kt b/dist/src/main/kotlin/kr/toxicity/hud/manager/DatabaseManagerImpl.kt index 0cffa2ca..a3e11a94 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/manager/DatabaseManagerImpl.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/manager/DatabaseManagerImpl.kt @@ -54,7 +54,7 @@ object DatabaseManagerImpl : BetterHudManager, DatabaseManager { add("compasses") { CompassManagerImpl.getCompass(it) } - yaml.get("locations")?.asArray()?.forEach { + yaml["locations"]?.asArray()?.forEach { runWithExceptionHandling(CONSOLE, "unable to load ${player.name()}'s location.") { player.pointers().add(PointedLocation.deserialize(it.asObject())) } diff --git a/dist/src/main/kotlin/kr/toxicity/hud/manager/ImageManager.kt b/dist/src/main/kotlin/kr/toxicity/hud/manager/ImageManager.kt index 324e43c7..36fea202 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/manager/ImageManager.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/manager/ImageManager.kt @@ -21,6 +21,8 @@ object ImageManager : BetterHudManager { private val imageNameComponent = ConcurrentHashMap() + val allImage get() = imageMap.values + @Synchronized fun getImage(group: ShaderGroup) = imageNameComponent[group] @Synchronized @@ -46,12 +48,12 @@ object ImageManager : BetterHudManager { DATA_FOLDER.subFolder("images").forEachAllYaml(sender) { file, s, yamlObject -> runWithExceptionHandling(sender, "Unable to load this image: $s in ${file.name}") { val image = when (val type = ImageType.valueOf( - yamlObject.get("type")?.asString().ifNull("type value not set.").uppercase() + yamlObject["type"]?.asString().ifNull("type value not set.").uppercase() )) { ImageType.SINGLE -> { val targetFile = File( assets, - yamlObject.get("file")?.asString().ifNull("file value not set.") + yamlObject["file"]?.asString().ifNull("file value not set.") .replace('/', File.separatorChar) ) HudImage( @@ -65,19 +67,19 @@ object ImageManager : BetterHudManager { .toNamed(targetFile.name), ), type, - yamlObject.get("setting")?.asObject() ?: emptySetting + yamlObject["setting"]?.asObject() ?: emptySetting ) } ImageType.LISTENER -> { - val splitType = yamlObject.get("split-type")?.asString()?.let { splitType -> + val splitType = yamlObject["split-type"]?.asString()?.let { splitType -> runWithExceptionHandling(sender, "Unable to find that split-type: $splitType") { SplitType.valueOf(splitType.uppercase()) }.getOrNull() } ?: SplitType.LEFT val getFile = File( assets, - yamlObject.get("file")?.asString().ifNull("file value not set.") + yamlObject["file"]?.asString().ifNull("file value not set.") .replace('/', File.separatorChar) ) HudImage( @@ -91,7 +93,7 @@ object ImageManager : BetterHudManager { .toNamed(getFile.name), yamlObject.getAsInt("split", 25).coerceAtLeast(1) ), type, - yamlObject.get("setting")?.asObject() + yamlObject["setting"]?.asObject() .ifNull("setting configuration not found.") ) } @@ -101,7 +103,7 @@ object ImageManager : BetterHudManager { HudImage( file.path, s, - (yamlObject.get("files")?.asArray()?.map { + (yamlObject["files"]?.asArray()?.map { it.asString() } ?: emptyList()).ifEmpty { warn("files is empty.") @@ -125,7 +127,7 @@ object ImageManager : BetterHudManager { } }.sum(), type, - yamlObject.get("setting")?.asObject() ?: emptySetting + yamlObject["setting"]?.asObject() ?: emptySetting ) } } diff --git a/dist/src/main/kotlin/kr/toxicity/hud/manager/ListenerManagerImpl.kt b/dist/src/main/kotlin/kr/toxicity/hud/manager/ListenerManagerImpl.kt index 2445daa6..1691c5cf 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/manager/ListenerManagerImpl.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/manager/ListenerManagerImpl.kt @@ -37,7 +37,7 @@ object ListenerManagerImpl : BetterHudManager, ListenerManager { override fun getAllListenerKeys(): Set = Collections.unmodifiableSet(listenerMap.keys) fun getListener(section: YamlObject): (UpdateEvent) -> HudListener { - val clazz = section.get("class")?.asString().ifNull("class value not set.") + val clazz = section["class"]?.asString().ifNull("class value not set.") return listenerMap[clazz].ifNull("this class doesn't exist: $clazz")(section) } diff --git a/dist/src/main/kotlin/kr/toxicity/hud/manager/ShaderManagerImpl.kt b/dist/src/main/kotlin/kr/toxicity/hud/manager/ShaderManagerImpl.kt index ca037ad0..e1bfa983 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/manager/ShaderManagerImpl.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/manager/ShaderManagerImpl.kt @@ -106,7 +106,7 @@ object ShaderManagerImpl : BetterHudManager, ShaderManager { val replaceList = mutableSetOf() val yaml = PluginConfiguration.SHADER.create() - barColor = yaml.get("bar-color")?.asString()?.let { + barColor = yaml["bar-color"]?.asString()?.let { runCatching { BossBar.Color.valueOf(it.uppercase()) }.getOrNull() @@ -151,7 +151,7 @@ object ShaderManagerImpl : BetterHudManager, ShaderManager { if (yaml.getAsBoolean("disable-level-text", false)) replaceList.add("HideExp") - yaml.get("hotbar")?.asObject()?.let { + yaml["hotbar"]?.asObject()?.let { if (it.getAsBoolean("disable", false)) { replaceList.add("RemapHotBar") val locations = diff --git a/dist/src/main/kotlin/kr/toxicity/hud/manager/TextManagerImpl.kt b/dist/src/main/kotlin/kr/toxicity/hud/manager/TextManagerImpl.kt index a250968c..efbfce8c 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/manager/TextManagerImpl.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/manager/TextManagerImpl.kt @@ -226,7 +226,7 @@ object TextManagerImpl : BetterHudManager, TextManager { DATA_FOLDER.subFolder("texts").forEachAllYaml(sender) { file, s, section -> runWithExceptionHandling(sender, "Unable to load this text: $s in ${file.name}") { - val fontDir = section.get("file")?.asString()?.let { + val fontDir = section["file"]?.asString()?.let { File(fontFolder, it).ifNotExist("this file doesn't exist: $it") } val scale = section.getAsInt("scale", 16) @@ -253,12 +253,12 @@ object TextManagerImpl : BetterHudManager, TextManager { scale, resource.textures, TreeMap().apply { - section.get("images")?.asObject() + section["images"]?.asObject() ?.forEachSubConfiguration { key, yamlObject -> put(key, LocatedImage( File( assetsFolder, - yamlObject.get("name")?.asString().ifNull("image does not set: $key") + yamlObject["name"]?.asString().ifNull("image does not set: $key") ) .ifNotExist("this image doesn't exist: $key") .toImage() @@ -271,7 +271,7 @@ object TextManagerImpl : BetterHudManager, TextManager { )) } }, - section.get("include")?.asArray()?.map { + section["include"]?.asArray()?.map { it.asString() } ?: emptyList(), section.toConditions(), @@ -283,7 +283,7 @@ object TextManagerImpl : BetterHudManager, TextManager { file.path, s, resource.textures, - section.get("chars").ifNull("Unable to find 'chars' array.").asObject().mapSubConfiguration { _, obj -> + section["chars"].ifNull("Unable to find 'chars' array.").asObject().mapSubConfiguration { _, obj -> BitmapData( obj.get("codepoints").ifNull("codepoints value not set.").asArray().map { y -> y.asString() diff --git a/dist/src/main/kotlin/kr/toxicity/hud/manager/TriggerManagerImpl.kt b/dist/src/main/kotlin/kr/toxicity/hud/manager/TriggerManagerImpl.kt index 24c1d4be..8972a251 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/manager/TriggerManagerImpl.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/manager/TriggerManagerImpl.kt @@ -24,7 +24,7 @@ object TriggerManagerImpl : BetterHudManager, TriggerManager { } fun getTrigger(yamlObject: YamlObject): HudTrigger<*> { - val clazz = yamlObject.get("class")?.asString()?.ifNull("class value not found.") + val clazz = yamlObject["class"]?.asString()?.ifNull("class value not found.") val builder = map[clazz].ifNull("this class doesn't exist: $clazz") return builder(yamlObject) } diff --git a/dist/src/main/kotlin/kr/toxicity/hud/placeholder/Conditions.kt b/dist/src/main/kotlin/kr/toxicity/hud/placeholder/Conditions.kt index d8c8ccb6..ef93c698 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/placeholder/Conditions.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/placeholder/Conditions.kt @@ -7,12 +7,12 @@ import kr.toxicity.hud.util.ifNull import kr.toxicity.hud.util.warn object Conditions { - fun parse(section: YamlObject): ConditionBuilder { - var value: ConditionBuilder = ConditionBuilder.alwaysTrue + fun parse(section: YamlObject, defaultBuilder: ConditionBuilder = ConditionBuilder.alwaysTrue): ConditionBuilder { + var value: ConditionBuilder = defaultBuilder section.forEachSubConfiguration { s, yamlObject -> runCatching { val new = parse0(yamlObject) - value = when (val gate = yamlObject.get("gate")?.asString() ?: "and") { + value = when (val gate = yamlObject["gate"]?.asString() ?: "and") { "and" -> value and new "or" -> value or new else -> { @@ -31,13 +31,13 @@ object Conditions { @Suppress("UNCHECKED_CAST") private fun parse0(section: YamlObject): ConditionBuilder { - val first = PlaceholderManagerImpl.find(section.get("first")?.asString().ifNull("first value not set.")) - val second = PlaceholderManagerImpl.find(section.get("second")?.asString().ifNull("second value not set.")) - val operationValue = section.get("operation")?.asString().ifNull("operation value not set") + val first = PlaceholderManagerImpl.find(section["first"]?.asString().ifNull("first value not set.")) + val second = PlaceholderManagerImpl.find(section["second"]?.asString().ifNull("second value not set.")) + val operationValue = section["operation"]?.asString().ifNull("operation value not set") if (first.clazz != second.clazz) throw RuntimeException("type mismatch: ${first.clazz.simpleName} and ${second.clazz.simpleName}") - val operation = (Operations.find(first.clazz) ?: throw RuntimeException("unable to load valid operation. you need to call developer.")).map[section.get("operation")?.asString().ifNull(operationValue)].ifNull("unsupported operation: $operationValue") as (Any, Any) -> Boolean + val operation = (Operations.find(first.clazz) ?: throw RuntimeException("unable to load valid operation. you need to call developer.")).map[section["operation"]?.asString().ifNull(operationValue)].ifNull("unsupported operation: $operationValue") as (Any, Any) -> Boolean return ConditionBuilder { updateEvent -> val o1 = first.build(updateEvent) val o2 = second.build(updateEvent) diff --git a/dist/src/main/kotlin/kr/toxicity/hud/popup/PopupImpl.kt b/dist/src/main/kotlin/kr/toxicity/hud/popup/PopupImpl.kt index fcd24ac5..3ae80a22 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/popup/PopupImpl.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/popup/PopupImpl.kt @@ -26,19 +26,19 @@ class PopupImpl( section: YamlObject ) : Popup, HudConfiguration { val gui = GuiLocation(section) - val move = section.get("move")?.asObject()?.let { + val move = section["move"]?.asObject()?.let { EquationPairLocation(it) } ?: EquationPairLocation.zero private val duration = section.getAsInt("duration", -1) private val update = section.getAsBoolean("update", true) - private val group = section.get("group")?.asString() ?: internalName + private val group = section["group"]?.asString() ?: internalName private val unique = section.getAsBoolean("unique", false) private val queue = duration > 0 && section.getAsBoolean("queue", false) private val push = !queue && section.getAsBoolean("push", false) private val alwaysCheckCondition = queue && section.getAsBoolean("always-check-condition", true) private val default = ConfigManagerImpl.defaultPopup.contains(internalName) || section.getAsBoolean("default", false) private val keyMapping = section.getAsBoolean("key-mapping", false) - private val index: ((UpdateEvent) -> (HudPlayer) -> Int)? = section.get("index")?.asString()?.let { + private val index: ((UpdateEvent) -> (HudPlayer) -> Int)? = section["index"]?.asString()?.let { PlaceholderManagerImpl.find(it).apply { if (clazz != java.lang.Number::class.java) throw RuntimeException("this index is not a number. it is ${clazz.simpleName}.") }.let { @@ -64,16 +64,16 @@ class PopupImpl( } fun newChar(): String = (++imageChar).parseChar() - private val sortType = section.get("sort")?.asString()?.let { + private val sortType = section["sort"]?.asString()?.let { PopupSortType.valueOf(it.uppercase()) } ?: PopupSortType.LAST - private val layouts = section.get("layouts")?.asObject()?.let { + private val layouts = section["layouts"]?.asObject()?.let { val json = array.ifNull("error is occurred.") it.mapSubConfiguration { _, yamlObject -> - val layout = yamlObject.get("name")?.asString().ifNull("name value not set.") + val layout = yamlObject["name"]?.asString().ifNull("name value not set.") var loc = GuiLocation(yamlObject) - yamlObject.get("gui")?.asObject()?.let { gui -> + yamlObject["gui"]?.asObject()?.let { gui -> loc += GuiLocation(gui) } PopupLayout( @@ -81,7 +81,7 @@ class PopupImpl( LayoutManager.getLayout(layout).ifNull("this layout doesn't exist: $layout"), this@PopupImpl, loc, - yamlObject.get("pixel")?.asObject()?.let { pixel -> + yamlObject["pixel"]?.asObject()?.let { pixel -> PixelLocation(pixel) } ?: PixelLocation.zero, resource.font, @@ -99,12 +99,12 @@ class PopupImpl( show(event, hudPlayer) } } - section.get("triggers")?.asObject()?.forEachSubConfiguration { _, yamlObject -> + section["triggers"]?.asObject()?.forEachSubConfiguration { _, yamlObject -> TriggerManagerImpl.getTrigger(yamlObject).registerEvent { t, u -> task(u, t) } } - section.get("hide-triggers")?.asObject()?.forEachSubConfiguration { _, yamlObject -> + section["hide-triggers"]?.asObject()?.forEachSubConfiguration { _, yamlObject -> TriggerManagerImpl.getTrigger(yamlObject).registerEvent { t, u -> PlayerManagerImpl.getHudPlayer(t)?.let { hide(it) diff --git a/dist/src/main/kotlin/kr/toxicity/hud/popup/PopupLayout.kt b/dist/src/main/kotlin/kr/toxicity/hud/popup/PopupLayout.kt index 5b4ad368..297f19af 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/popup/PopupLayout.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/popup/PopupLayout.kt @@ -7,6 +7,8 @@ import kr.toxicity.hud.api.player.HudPlayer import kr.toxicity.hud.api.update.UpdateEvent import kr.toxicity.hud.component.LayoutComponentContainer import kr.toxicity.hud.hud.HudImpl +import kr.toxicity.hud.image.HudImage +import kr.toxicity.hud.image.ImageComponent import kr.toxicity.hud.image.LoadedImage import kr.toxicity.hud.layout.BackgroundLayout import kr.toxicity.hud.location.LocationGroup @@ -119,53 +121,56 @@ class PopupLayout( pixel.opacity, target.property ) - val list = ArrayList() val negativeSpace = parent.getOrCreateSpace(-1) - if (hudImage.listener != null) list.add(EMPTY_PIXEL_COMPONENT) - hudImage.image.forEach { - val fileName = "$NAME_SPACE_ENCODED:${it.name}" + fun HudImage.toComponent(): ImageComponent { + val list = ArrayList() + if (listener != null) list.add(EMPTY_PIXEL_COMPONENT) + image.forEach { + val fileName = "$NAME_SPACE_ENCODED:${it.name}" - val height = (it.image.image.height * target.scale).roundToInt() - val scale = height.toDouble() / it.image.image.height - val xOffset = (it.image.xOffset * scale).roundToInt() - val ascent = pixel.y - val shaderGroup = ShaderGroup(imageShader, fileName, target.scale, ascent) + val height = (it.image.image.height * target.scale).roundToInt() + val scale = height.toDouble() / it.image.image.height + val xOffset = (it.image.xOffset * scale).roundToInt() + val ascent = pixel.y + val shaderGroup = ShaderGroup(imageShader, fileName, target.scale, ascent) - val component = ImageManager.getImage(shaderGroup) ?: run { - val char = parent.newChar() - HudImpl.createBit(imageShader, ascent) { y -> - array.add(jsonObjectOf( - "type" to "bitmap", - "file" to fileName, - "ascent" to y, - "height" to height, - "chars" to jsonArrayOf(char) - )) + val component = ImageManager.getImage(shaderGroup) ?: run { + val char = parent.newChar() + HudImpl.createBit(imageShader, ascent) { y -> + array.add(jsonObjectOf( + "type" to "bitmap", + "file" to fileName, + "ascent" to y, + "height" to height, + "chars" to jsonArrayOf(char) + )) + } + val xWidth = (it.image.image.width.toDouble() * scale).roundToInt() + val build = Component.text() + .font(parent.imageKey) + val comp = WidthComponent( + if (BOOTSTRAP.useLegacyFont()) build.content(char).append(NEGATIVE_ONE_SPACE_COMPONENT.component) else build.content("$char$negativeSpace"), + xWidth + ) + ImageManager.setImage(shaderGroup, comp) + comp } - val xWidth = (it.image.image.width.toDouble() * scale).roundToInt() - val build = Component.text() - .font(parent.imageKey) - val comp = WidthComponent( - if (BOOTSTRAP.useLegacyFont()) build.content(char).append(NEGATIVE_ONE_SPACE_COMPONENT.component) else build.content("$char$negativeSpace"), - xWidth - ) - ImageManager.setImage(shaderGroup, comp) - comp + list.add(component.toPixelComponent(pixel.x + xOffset)) } - - list.add(component.toPixelComponent(pixel.x + xOffset)) + return ImageComponent(this, list, children.entries.associate { + it.key to it.value.toComponent() + }) } ImageRenderer( - hudImage, target.color, target.space, target.stack, target.maxStack, - list, + target.image.toComponent(), target.follow, target.cancelIfFollowerNotExists, - hudImage.conditions.and(target.conditions) + hudImage.conditions and target.conditions ) } diff --git a/dist/src/main/kotlin/kr/toxicity/hud/renderer/ImageRenderer.kt b/dist/src/main/kotlin/kr/toxicity/hud/renderer/ImageRenderer.kt index 433d001f..04524d50 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/renderer/ImageRenderer.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/renderer/ImageRenderer.kt @@ -1,63 +1,49 @@ package kr.toxicity.hud.renderer import kr.toxicity.hud.api.component.PixelComponent -import kr.toxicity.hud.api.component.WidthComponent -import kr.toxicity.hud.api.listener.HudListener import kr.toxicity.hud.api.player.HudPlayer import kr.toxicity.hud.api.update.UpdateEvent -import kr.toxicity.hud.image.HudImage +import kr.toxicity.hud.image.ImageComponent import kr.toxicity.hud.manager.PlaceholderManagerImpl import kr.toxicity.hud.manager.PlayerManagerImpl import kr.toxicity.hud.placeholder.ConditionBuilder import kr.toxicity.hud.placeholder.PlaceholderBuilder import kr.toxicity.hud.util.EMPTY_PIXEL_COMPONENT import kr.toxicity.hud.util.append -import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.TextColor import kotlin.math.ceil import kotlin.math.roundToInt class ImageRenderer( - image: HudImage, color: TextColor, private val space: Int, private val stack: PlaceholderBuilder<*>?, private val maxStack: PlaceholderBuilder<*>?, - components: List, + component: ImageComponent, follow: String?, private val cancelIfFollowerNotExists: Boolean, private val conditions: ConditionBuilder ) { - private val type = image.type - private val components = components.map { - val comp = it.component - PixelComponent(if (color.value() == NamedTextColor.WHITE.value()) comp else WidthComponent( - comp.component.build().toBuilder().color(color), - comp.width - ), it.pixel) - } - private val followHudPlayer = follow?.let { PlaceholderManagerImpl.find(it).apply { if (!java.lang.String::class.java.isAssignableFrom(clazz)) throw RuntimeException("This placeholder is not a string: $it") } } - - val listener: (UpdateEvent) -> HudListener = image.listener ?: HudListener.EMPTY.let { - { _ -> - it - } - } + private val component = component.applyColor(color) fun getComponent(reason: UpdateEvent): (HudPlayer, Int) -> PixelComponent { val cond = conditions.build(reason) - val listen = listener(reason) + val listen = component.listener(reason) val follow = followHudPlayer?.build(reason) val stackGetter = stack?.build(reason) val maxStackGetter = maxStack?.build(reason) + val mapper = component.imageMapper(reason) + return build@ { hudPlayer, frame -> + val selected = mapper(hudPlayer) + val stackFrame = (stackGetter?.value(hudPlayer) as? Number)?.toDouble() ?: 0.0 val maxStackFrame = (maxStackGetter?.value(hudPlayer) as? Number)?.toInt()?.coerceAtLeast(1) ?: ceil(stackFrame).toInt() @@ -74,18 +60,16 @@ class ImageRenderer( if (stackFrame <= 0.0) return@build EMPTY_PIXEL_COMPONENT var empty = EMPTY_PIXEL_COMPONENT for (i in 0..