diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 8f105e82..39e7948a 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -3,8 +3,6 @@ name: Java CI with Gradle on: push: branches: [ "master" ] - pull_request: - branches: [ "master" ] permissions: contents: read @@ -26,7 +24,7 @@ jobs: distribution: 'temurin' - name: Grant execute permission for gradlew run: chmod +x gradlew - - name: Build all file. + - name: Build all file run: ./gradlew build --stacktrace - name: Publish to Modrinth run: ./gradlew modrinthPublish --stacktrace diff --git a/api/standard-api/src/main/java/kr/toxicity/hud/api/manager/TextManager.java b/api/standard-api/src/main/java/kr/toxicity/hud/api/manager/TextManager.java index f102fec9..40292884 100644 --- a/api/standard-api/src/main/java/kr/toxicity/hud/api/manager/TextManager.java +++ b/api/standard-api/src/main/java/kr/toxicity/hud/api/manager/TextManager.java @@ -1,7 +1,4 @@ package kr.toxicity.hud.api.manager; -import org.jetbrains.annotations.NotNull; - public interface TextManager { - int getWidth(@NotNull String textName, double scale, @NotNull String text); } diff --git a/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/compatibility/mythicmobs/MythicMobsCompatibility.kt b/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/compatibility/mythicmobs/MythicMobsCompatibility.kt index 7522debe..7c14a59c 100644 --- a/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/compatibility/mythicmobs/MythicMobsCompatibility.kt +++ b/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/compatibility/mythicmobs/MythicMobsCompatibility.kt @@ -92,6 +92,70 @@ class MythicMobsCompatibility : Compatibility { } ?: 0 } } + }, + // entity + "entity_current_cooldown" to object : HudPlaceholder { + override fun getRequiredArgsLength(): Int = 1 + override fun invoke(args: MutableList, reason: UpdateEvent): Function { + return reason.unwrap { event: EntityEvent -> + val skill = MythicBukkit.inst().skillManager.getSkill(args[0]).orElseThrow { + RuntimeException("this skill doesn't exist: ${args[0]}") + } as AbstractSkill + Function { + skill.getCooldown(object : SkillCaster { + override fun getEntity(): AbstractEntity = BukkitAdapter.adapt(event.entity) + override fun setUsingDamageSkill(p0: Boolean) {} + override fun isUsingDamageSkill(): Boolean = false + }) + } + } + } + }, + "entity_aura_stack" to object : HudPlaceholder { + override fun getRequiredArgsLength(): Int = 1 + override fun invoke(args: MutableList, reason: UpdateEvent): Function { + return reason.unwrap { event: EntityEvent -> + Function get@ { + (MythicBukkit.inst().mobManager.getMythicMobInstance(event.entity) ?: return@get -1).getAuraStacks(args[0]) + } + } + } + }, + "entity_aura_max_duration" to object : HudPlaceholder { + override fun getRequiredArgsLength(): Int = 1 + override fun invoke(args: MutableList, reason: UpdateEvent): Function { + return reason.unwrap { event: EntityEvent -> + Function get@ { + (MythicBukkit.inst().mobManager.getMythicMobInstance(event.entity) ?: return@get -1).auraRegistry.auras[args[0]]?.maxOfOrNull { aura -> + aura.startDuration + } ?: 0 + } + } + } + }, + "entity_aura_duration" to object : HudPlaceholder { + override fun getRequiredArgsLength(): Int = 1 + override fun invoke(args: MutableList, reason: UpdateEvent): Function { + return reason.unwrap { event: EntityEvent -> + Function get@ { + (MythicBukkit.inst().mobManager.getMythicMobInstance(event.entity) ?: return@get -1).auraRegistry.auras[args[0]]?.maxOfOrNull { aura -> + aura.ticksRemaining + } ?: 0 + } + } + } + }, + "entity_aura_duration_reversed" to object : HudPlaceholder { + override fun getRequiredArgsLength(): Int = 1 + override fun invoke(args: MutableList, reason: UpdateEvent): Function { + return reason.unwrap { event: EntityEvent -> + Function get@ { + (MythicBukkit.inst().mobManager.getMythicMobInstance(event.entity) ?: return@get -1).auraRegistry.auras[args[0]]?.maxOfOrNull { aura -> + aura.startDuration - aura.ticksRemaining + } ?: 0 + } + } + } } ) override val strings: Map> @@ -150,6 +214,16 @@ class MythicMobsCompatibility : Compatibility { MythicBukkit.inst().mobManager.isMythicMob(e.entity) } } + }, + "entity_has_aura" to object : HudPlaceholder { + override fun getRequiredArgsLength(): Int = 1 + override fun invoke(args: MutableList, reason: UpdateEvent): Function { + return reason.unwrap { event: EntityEvent -> + Function get@ { _ -> + (MythicBukkit.inst().mobManager.getMythicMobInstance(event.entity) ?: return@get false).auraRegistry.hasAura(args[0]) + } + } + } } ) diff --git a/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/compatibility/oraxen/OraxenCompatibility.kt b/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/compatibility/oraxen/OraxenCompatibility.kt index 7a5322b8..c58cc2f5 100644 --- a/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/compatibility/oraxen/OraxenCompatibility.kt +++ b/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/compatibility/oraxen/OraxenCompatibility.kt @@ -45,6 +45,9 @@ class OraxenCompatibility : Compatibility { } else -> warn("Unknown Oraxen Version.") } - info("Successfully handle Oraxen $version.") + info( + "BetterHud hooks Oraxen $version.", + "Be sure to set 'pack-type' to 'none' in your config." + ) } } \ No newline at end of file diff --git a/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/module/bukkit/BukkitEntityModule.kt b/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/module/bukkit/BukkitEntityModule.kt index 16a4c2bc..0b73c9bd 100644 --- a/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/module/bukkit/BukkitEntityModule.kt +++ b/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/module/bukkit/BukkitEntityModule.kt @@ -6,11 +6,7 @@ import kr.toxicity.hud.api.placeholder.HudPlaceholder import kr.toxicity.hud.api.update.UpdateEvent import kr.toxicity.hud.api.yaml.YamlObject import kr.toxicity.hud.bootstrap.bukkit.module.BukkitModule -import kr.toxicity.hud.bootstrap.bukkit.util.ATTRIBUTE_MAX_HEALTH -import kr.toxicity.hud.bootstrap.bukkit.util.bukkitPlayer -import kr.toxicity.hud.bootstrap.bukkit.util.createBukkitTrigger -import kr.toxicity.hud.bootstrap.bukkit.util.unwrap -import org.bukkit.Material +import kr.toxicity.hud.bootstrap.bukkit.util.* import org.bukkit.entity.LivingEntity import org.bukkit.entity.Player import org.bukkit.entity.Projectile @@ -83,7 +79,15 @@ class BukkitEntityModule : BukkitModule { "max_health" to HudPlaceholder.of { _, u -> u.unwrap { e: EntityEvent -> Function { - (e.entity as? LivingEntity)?.getAttribute(ATTRIBUTE_MAX_HEALTH)?.value ?: 0.0 + (e.entity as? LivingEntity)?.maximumHealth ?: 0.0 + } + } + }, + "health_percentage" to HudPlaceholder.of { _, u -> + u.unwrap { e: EntityEvent -> + Function get@ { + val entity = e.entity as? LivingEntity ?: return@get 0.0 + entity.health / entity.maximumHealth } } }, @@ -120,17 +124,7 @@ class BukkitEntityModule : BukkitModule { e.entity.isDead } } - }, - "has_off_hand" to HudPlaceholder.of { _, _ -> - Function { - it.bukkitPlayer.inventory.itemInOffHand.type != Material.AIR - } - }, - "has_main_hand" to HudPlaceholder.of { _, _ -> - Function { - it.bukkitPlayer.inventory.itemInMainHand.type != Material.AIR - } - }, + } ) } \ No newline at end of file diff --git a/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/module/bukkit/BukkitStandardModule.kt b/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/module/bukkit/BukkitStandardModule.kt index 5fe2ce19..8eb8a3be 100644 --- a/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/module/bukkit/BukkitStandardModule.kt +++ b/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/module/bukkit/BukkitStandardModule.kt @@ -38,7 +38,7 @@ class BukkitStandardModule : BukkitModule { "health" to { _ -> { HudListener { p -> - p.bukkitPlayer.health / p.bukkitPlayer.getAttribute(ATTRIBUTE_MAX_HEALTH)!!.value + p.bukkitPlayer.health / p.bukkitPlayer.maximumHealth } } }, @@ -46,7 +46,7 @@ class BukkitStandardModule : BukkitModule { { HudListener { p -> (p.bukkitPlayer.vehicle as? LivingEntity)?.let { entity -> - entity.health / entity.getAttribute(ATTRIBUTE_MAX_HEALTH)!!.value + entity.health / entity.maximumHealth } ?: 0.0 } } @@ -82,7 +82,7 @@ class BukkitStandardModule : BukkitModule { "absorption" to { _ -> { HudListener { p -> - p.bukkitPlayer.absorptionAmount / p.bukkitPlayer.getAttribute(ATTRIBUTE_MAX_HEALTH)!!.value + p.bukkitPlayer.absorptionAmount / p.bukkitPlayer.maximumHealth } } }, @@ -121,35 +121,36 @@ class BukkitStandardModule : BukkitModule { }, "max_health" to HudPlaceholder.of { _, _ -> Function { p -> - p.bukkitPlayer.getAttribute(ATTRIBUTE_MAX_HEALTH)!!.value + p.bukkitPlayer.maximumHealth + } + }, + "health_percentage" to HudPlaceholder.of { _, _ -> + Function { p -> + val bukkit = p.bukkitPlayer + bukkit.health / bukkit.maximumHealth } }, "vehicle_max_health" to HudPlaceholder.of { _, _ -> Function { p -> - (p.bukkitPlayer.vehicle as? LivingEntity)?.getAttribute(ATTRIBUTE_MAX_HEALTH)?.value ?: 0.0 + (p.bukkitPlayer.vehicle as? LivingEntity)?.maximumHealth ?: 0.0 } }, "max_health_with_absorption" to HudPlaceholder.of { _, _ -> Function { p -> - p.bukkitPlayer.getAttribute(ATTRIBUTE_MAX_HEALTH)!!.value + p.bukkitPlayer.absorptionAmount + p.bukkitPlayer.maximumHealth + p.bukkitPlayer.absorptionAmount } }, "vehicle_max_health_with_absorption" to HudPlaceholder.of { _, _ -> Function { p -> (p.bukkitPlayer.vehicle as? LivingEntity)?.let { entity -> - entity.getAttribute(ATTRIBUTE_MAX_HEALTH)!!.value + entity.absorptionAmount + entity.maximumHealth + entity.absorptionAmount } ?: 0.0 } }, - "health_percentage" to HudPlaceholder.of { _, _ -> - Function { p -> - p.bukkitPlayer.health / p.bukkitPlayer.getAttribute(ATTRIBUTE_MAX_HEALTH)!!.value * 100.0 - } - }, "vehicle_health_percentage" to HudPlaceholder.of { _, _ -> Function { p -> (p.bukkitPlayer.vehicle as? LivingEntity)?.let { entity -> - entity.health / entity.getAttribute(ATTRIBUTE_MAX_HEALTH)!!.value * 100.0 + entity.health / entity.maximumHealth * 100.0 } ?: 0.0 } }, @@ -254,6 +255,16 @@ class BukkitStandardModule : BukkitModule { p.bukkitPlayer.isFrozen } }, + "has_off_hand" to HudPlaceholder.of { _, _ -> + Function { + it.bukkitPlayer.inventory.itemInOffHand.type != Material.AIR + } + }, + "has_main_hand" to HudPlaceholder.of { _, _ -> + Function { + it.bukkitPlayer.inventory.itemInMainHand.type != Material.AIR + } + }, "has_permission" to object : HudPlaceholder { override fun invoke(args: MutableList, reason: UpdateEvent): Function { return Function { diff --git a/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/util/Entities.kt b/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/util/Entities.kt new file mode 100644 index 00000000..32fb78f1 --- /dev/null +++ b/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/util/Entities.kt @@ -0,0 +1,6 @@ +package kr.toxicity.hud.bootstrap.bukkit.util + +import org.bukkit.entity.LivingEntity + +val LivingEntity.maximumHealth + get() = getAttribute(ATTRIBUTE_MAX_HEALTH)!!.value \ No newline at end of file diff --git a/bootstrap/fabric/src/main/kotlin/kr/toxicity/hud/bootstrap/fabric/compatibility/PolymerResourcePackCompatibility.kt b/bootstrap/fabric/src/main/kotlin/kr/toxicity/hud/bootstrap/fabric/compatibility/PolymerResourcePackCompatibility.kt index 21394527..7cfc8ed9 100644 --- a/bootstrap/fabric/src/main/kotlin/kr/toxicity/hud/bootstrap/fabric/compatibility/PolymerResourcePackCompatibility.kt +++ b/bootstrap/fabric/src/main/kotlin/kr/toxicity/hud/bootstrap/fabric/compatibility/PolymerResourcePackCompatibility.kt @@ -49,5 +49,9 @@ class PolymerResourcePackCompatibility : Compatibility { is OnReload -> warn("This mod is still on reload!") } } + info( + "BetterHud hooks Polymer resource pack.", + "Be sure to set 'pack-type' to 'none' in your config." + ) } } \ No newline at end of file diff --git a/bootstrap/fabric/src/main/kotlin/kr/toxicity/hud/bootstrap/fabric/module/fabric/FabricStandardModule.kt b/bootstrap/fabric/src/main/kotlin/kr/toxicity/hud/bootstrap/fabric/module/fabric/FabricStandardModule.kt index 5a9f7b53..018baf37 100644 --- a/bootstrap/fabric/src/main/kotlin/kr/toxicity/hud/bootstrap/fabric/module/fabric/FabricStandardModule.kt +++ b/bootstrap/fabric/src/main/kotlin/kr/toxicity/hud/bootstrap/fabric/module/fabric/FabricStandardModule.kt @@ -13,6 +13,7 @@ import kr.toxicity.hud.bootstrap.fabric.module.Module import net.minecraft.resources.ResourceLocation import net.minecraft.world.entity.LivingEntity import net.minecraft.world.entity.ai.attributes.Attributes +import net.minecraft.world.item.ItemStack import java.util.function.Function class FabricStandardModule : Module { @@ -206,8 +207,17 @@ class FabricStandardModule : Module { it.fabricPlayer.hasPermission(args[0]) } } - override fun getRequiredArgsLength(): Int = 1 - } + }, + "has_main_hand" to HudPlaceholder.of { _, _ -> + Function { p -> + !p.fabricPlayer.mainHandItem.`is`(ItemStack.EMPTY.item) + } + }, + "has_off_hand" to HudPlaceholder.of { _, _ -> + Function { p -> + !p.fabricPlayer.offhandItem.`is`(ItemStack.EMPTY.item) + } + }, ) } \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index f2ef2a46..0b6eb2a2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,7 +31,7 @@ val platform = "4.3.4" val targetJavaVersion = 21 val velocity = "3.4.0" val bStats = "3.1.0" -val betterCommand = "1.1" +val betterCommand = "1.2" val supportedMinecraftVersions = listOf( //1.17 @@ -95,7 +95,7 @@ allprojects { apply(plugin = "org.jetbrains.dokka") group = "kr.toxicity.hud" - version = "1.8" + (System.getenv("BUILD_NUMBER")?.let { ".DEV-$it" } ?: "") + version = "1.9" + (System.getenv("BUILD_NUMBER")?.let { ".DEV-$it" } ?: "") repositories { mavenCentral() diff --git a/changelog/1.9.md b/changelog/1.9.md index 3aed1102..96cde10d 100644 --- a/changelog/1.9.md +++ b/changelog/1.9.md @@ -2,10 +2,9 @@ ## Notice - Now support about 1.17, 1.17.1 and 1.18.1 is dropped. -- BetterHud and my other pugin does NOT support a closed or obfuscated source mod/plugin/modified-client such as ItemsAdder, Optifine and Feather. - -## Updates +- BetterHud and my other plugin does NOT support a closed or obfuscated source mod/plugin/modified-client such as ItemsAdder, Optifine and Feather. +## Resource pack merge - If your server has Polymer, BetterHud's resource pack will automatically be merged. (Fabric) - If your server has Oraxen, BetterHud's resource pack will automatically be merged. (Bukkit) @@ -14,10 +13,87 @@ - If your server uses Polymer, you have to use '/polymer generate-pack' instead of '/hud reload'. - If your server uses Oraxen, you have to use '/oraxen reload all' instead of '/hud reload'. -## Config +## Text layout +![1](https://github.com/user-attachments/assets/2201c5ee-093f-4790-ab55-e0c40bf6a32f) +```yaml +test_text: + texts: + 1: + name: unifont + pattern: Minecraft is a 3D sandbox adventure game developed by Mojang Studios where players can interact with a fully customizable three-dimensional world made of blocks and entities. Its diverse gameplay options allow players to choose the way they play, creating countless possibilities. + align: center + line-align: left + scale: 0.5 + line: 5 + split-width: 300 + y: 32 +``` +![2](https://github.com/user-attachments/assets/ff220742-ecf0-4239-bc2b-be172ac2cc24) +```yaml +test_text: + texts: + 1: + name: unifont + pattern: | + Background test + Background test2 3 34 23423 + background: test + align: center + line-align: center + scale: 0.5 + line: 5 + split-width: 300 + y: 32 +``` +- Add 'line' to use multiple line text. +- Add 'line-width' to define the y location of each line. +- Add 'split-width' to define max length of each line. +- Add 'line-align' to sort each line. +- BetterHud uses '\n' to split text line. + +## Placeholder (Bukkit) +These placeholders are added: +- has_main_hand +- has_off_hand +- entity_health_percentage +- entity_vehicle_health_percentage + +## Placeholder (Fabric) +These placeholders are added: +- has_main_hand +- has_off_hand + +## Placeholder (MythicMobs) +These placeholders are added: +- mythicmobs_aura_duration_reversed:arg +- mythicmobs_entity_current_cooldown:arg +- mythicmobs_entity_aura_stack:arg +- mythicmobs_entity_aura_max_duration:arg +- mythicmobs_entity_aura_duration:arg +- mythicmobs_entity_aura_duration_reversed:arg +- mythicmobs_entity_has_aura:arg +## Fix +- Fix inappropriate text space. +- Fix self-host to stop when switch to other pack type. +- Fix command load problem. +- Fix inappropriate compass location. +- Fix no mysql connector library found in Fabric and Velocity. + +## Improve +- Improve compass movement. + +## Config - Add 'remove-default-hotbar' to disable vanilla hotbar. - Add 'pack-type: none' to use other resource pack extension. +## Library +- Now BetterHud uses BetterCommand 1.2. +- Clean jar file to use runtime library injector. + ## Contribute -- These languages are included: ja_JP, vi_VN, zh_CH, zh_TW \ No newline at end of file +These languages are included: +- ja_JP +- vi_VN +- zh_CH +- zh_TW \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/background/HudBackground.kt b/dist/src/main/kotlin/kr/toxicity/hud/background/HudBackground.kt index 2314428b..36c7a9b9 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/background/HudBackground.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/background/HudBackground.kt @@ -4,6 +4,7 @@ import kr.toxicity.hud.configuration.HudConfiguration import kr.toxicity.hud.location.PixelLocation import kr.toxicity.hud.image.LoadedImage +//TODO replace it to proper background in the future. class HudBackground( override val path: String, val name: String, diff --git a/dist/src/main/kotlin/kr/toxicity/hud/compass/CompassType.kt b/dist/src/main/kotlin/kr/toxicity/hud/compass/CompassType.kt index 2cd6f740..5a69b38b 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/compass/CompassType.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/compass/CompassType.kt @@ -1,6 +1,7 @@ package kr.toxicity.hud.compass import kr.toxicity.hud.api.yaml.YamlObject +import kr.toxicity.hud.compass.type.CircleCompass import kr.toxicity.hud.resource.GlobalResource import java.io.File diff --git a/dist/src/main/kotlin/kr/toxicity/hud/compass/CircleCompass.kt b/dist/src/main/kotlin/kr/toxicity/hud/compass/type/CircleCompass.kt similarity index 99% rename from dist/src/main/kotlin/kr/toxicity/hud/compass/CircleCompass.kt rename to dist/src/main/kotlin/kr/toxicity/hud/compass/type/CircleCompass.kt index 706c3ce1..8e32be35 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/compass/CircleCompass.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/compass/type/CircleCompass.kt @@ -1,4 +1,4 @@ -package kr.toxicity.hud.compass +package kr.toxicity.hud.compass.type import com.google.gson.JsonArray import kr.toxicity.hud.api.component.WidthComponent @@ -6,6 +6,7 @@ import kr.toxicity.hud.api.configuration.HudObjectType import kr.toxicity.hud.api.player.HudPlayer import kr.toxicity.hud.api.update.UpdateEvent import kr.toxicity.hud.api.yaml.YamlObject +import kr.toxicity.hud.compass.CompassImpl import kr.toxicity.hud.equation.TEquation import kr.toxicity.hud.hud.HudImpl import kr.toxicity.hud.location.PixelLocation diff --git a/dist/src/main/kotlin/kr/toxicity/hud/component/LayoutComponentContainer.kt b/dist/src/main/kotlin/kr/toxicity/hud/component/LayoutComponentContainer.kt index 31523bfc..60079257 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/component/LayoutComponentContainer.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/component/LayoutComponentContainer.kt @@ -2,8 +2,8 @@ package kr.toxicity.hud.component import kr.toxicity.hud.api.component.PixelComponent import kr.toxicity.hud.api.component.WidthComponent -import kr.toxicity.hud.layout.LayoutAlign -import kr.toxicity.hud.layout.LayoutOffset +import kr.toxicity.hud.layout.enums.LayoutAlign +import kr.toxicity.hud.layout.enums.LayoutOffset import kr.toxicity.hud.util.EMPTY_WIDTH_COMPONENT import kr.toxicity.hud.util.toSpaceComponent 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 29b73a93..50914321 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/equation/EquationPairLocation.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/equation/EquationPairLocation.kt @@ -2,7 +2,7 @@ package kr.toxicity.hud.equation import kr.toxicity.hud.api.yaml.YamlObject import kr.toxicity.hud.location.PixelLocation -import kr.toxicity.hud.image.LocationGroup +import kr.toxicity.hud.location.LocationGroup import kr.toxicity.hud.location.GuiLocation class EquationPairLocation( diff --git a/dist/src/main/kotlin/kr/toxicity/hud/hud/HudElement.kt b/dist/src/main/kotlin/kr/toxicity/hud/hud/HudElement.kt index 9456509c..5477d374 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/hud/HudElement.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/hud/HudElement.kt @@ -6,11 +6,12 @@ import kr.toxicity.hud.component.LayoutComponentContainer import kr.toxicity.hud.location.PixelLocation import kr.toxicity.hud.layout.LayoutGroup import kr.toxicity.hud.location.GuiLocation +import kr.toxicity.hud.resource.GlobalResource import kr.toxicity.hud.util.EMPTY_WIDTH_COMPONENT class HudElement( hud: HudImpl, - file: List, + resource: GlobalResource, private val layout: LayoutGroup, gui: GuiLocation, pixel: PixelLocation @@ -19,7 +20,7 @@ class HudElement( HudImageElement(hud, image, gui, pixel) } private val textElement = layout.text.map { textLayout -> - HudTextElement(hud, file, textLayout, gui, pixel) + HudTextElement(hud, resource, textLayout, gui, pixel) } private val headElement = layout.head.map { image -> HudHeadElement(hud, image, gui, pixel) 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 0fc120f2..345797a9 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/hud/HudImageElement.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/hud/HudImageElement.kt @@ -13,6 +13,7 @@ import kr.toxicity.hud.shader.HudShader import kr.toxicity.hud.shader.ShaderGroup 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) { @@ -45,7 +46,7 @@ class HudImageElement(parent: HudImpl, private val image: ImageLayout, gui: GuiL val finalWidth = WidthComponent(Component.text() .content(c) .font(parent.imageKey) - .append(NEGATIVE_ONE_SPACE_COMPONENT.component), Math.round(pair.image.image.width.toDouble() * scale).toInt()) + NEW_LAYER + .append(NEGATIVE_ONE_SPACE_COMPONENT.component), (pair.image.image.width.toDouble() * scale).roundToInt()) parent.jsonArray?.let { array -> HudImpl.createBit(shader, ascent) { y -> array.add(jsonObjectOf( 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 f4bb2ad1..4f53ce2b 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/hud/HudImpl.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/hud/HudImpl.kt @@ -9,19 +9,20 @@ import kr.toxicity.hud.api.update.UpdateEvent import kr.toxicity.hud.api.yaml.YamlObject import kr.toxicity.hud.configuration.HudConfiguration import kr.toxicity.hud.location.PixelLocation -import kr.toxicity.hud.location.AnimationType +import kr.toxicity.hud.location.animation.AnimationType import kr.toxicity.hud.manager.ConfigManagerImpl import kr.toxicity.hud.manager.LayoutManager import kr.toxicity.hud.manager.ShaderManagerImpl import kr.toxicity.hud.pack.PackGenerator import kr.toxicity.hud.location.GuiLocation +import kr.toxicity.hud.resource.GlobalResource import kr.toxicity.hud.shader.HudShader import kr.toxicity.hud.util.* class HudImpl( override val path: String, private val internalName: String, - file: List, + resource: GlobalResource, section: YamlObject ): Hud, HudConfiguration { companion object { @@ -65,7 +66,7 @@ class HudImpl( layout.animation.location.map { HudElement( this@HudImpl, - file, + resource, layout, gui, it + pixel @@ -83,7 +84,7 @@ class HudImpl( it.value to it.key }.toTypedArray()) )) - PackGenerator.addTask(file + "$imageEncoded.json") { + PackGenerator.addTask(resource.font + "$imageEncoded.json") { jsonObjectOf("providers" to array).toByteArray() } } diff --git a/dist/src/main/kotlin/kr/toxicity/hud/hud/HudTextElement.kt b/dist/src/main/kotlin/kr/toxicity/hud/hud/HudTextElement.kt index 015750af..38344028 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/hud/HudTextElement.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/hud/HudTextElement.kt @@ -4,18 +4,20 @@ 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.location.PixelLocation import kr.toxicity.hud.image.LoadedImage import kr.toxicity.hud.layout.BackgroundLayout import kr.toxicity.hud.layout.TextLayout +import kr.toxicity.hud.location.GuiLocation +import kr.toxicity.hud.location.PixelLocation import kr.toxicity.hud.manager.ConfigManagerImpl import kr.toxicity.hud.manager.MinecraftManager import kr.toxicity.hud.manager.TextManagerImpl import kr.toxicity.hud.pack.PackGenerator import kr.toxicity.hud.renderer.TextRenderer -import kr.toxicity.hud.location.GuiLocation +import kr.toxicity.hud.resource.GlobalResource import kr.toxicity.hud.shader.HudShader import kr.toxicity.hud.shader.ShaderGroup +import kr.toxicity.hud.text.BackgroundKey import kr.toxicity.hud.text.HudTextData import kr.toxicity.hud.util.* import net.kyori.adventure.text.Component @@ -23,7 +25,7 @@ import kotlin.math.roundToInt class HudTextElement( parent: HudImpl, - file: List, + resource: GlobalResource, private val text: TextLayout, gui: GuiLocation, pixel: PixelLocation @@ -39,104 +41,111 @@ class HudTextElement( loc.opacity, text.property ) - val yAxis = loc.y.coerceAtLeast(-HudImpl.ADD_HEIGHT).coerceAtMost(HudImpl.ADD_HEIGHT) - val group = ShaderGroup(shader, text.text.name, text.scale, yAxis) - val key = TextManagerImpl.getKey(group) ?: run { - val index2 = ++parent.textIndex - val array = text.startJson() - text.text.array.forEach { - HudImpl.createBit(shader, yAxis) { y -> - array.add(jsonObjectOf( - "type" to "bitmap", - "file" to "$NAME_SPACE_ENCODED:${it.file}", - "ascent" to y, - "height" to (it.height * text.scale).roundToInt(), - "chars" to it.chars - )) - } - } - var textIndex = 0xC0000 - val textEncoded = "hud_${parent.name}_text_${index2 + 1}".encodeKey() - val key = createAdventureKey(textEncoded) - val imageMap = HashMap() - text.text.images.forEach { - val result = textIndex++.parseChar() - val imageScale = it.value.scale * text.scale - val height = (it.value.image.image.height.toDouble() * imageScale).roundToInt() - val div = height.toDouble() / it.value.image.image.height - HudImpl.createBit(shader, loc.y + it.value.location.y) { y -> - array.add(jsonObjectOf( - "type" to "bitmap", - "file" to "$NAME_SPACE_ENCODED:${"glyph_${it.key}".encodeKey()}.png", - "ascent" to y, - "height" to height, - "chars" to jsonArrayOf(result) - )) - } - imageMap[it.key] = it.value.location.x.toSpaceComponent() + WidthComponent(Component.text() - .font(key) - .content(result) - .append(NEGATIVE_ONE_SPACE_COMPONENT.component), (it.value.image.image.width.toDouble() * div).roundToInt()) - } - if (ConfigManagerImpl.loadMinecraftDefaultTextures) { - HudImpl.createBit(shader, text.emojiLocation.y) { y -> - MinecraftManager.applyAll(array, y, text.emojiScale, key) { - textIndex++ - }.forEach { - imageMap[it.key] = text.emojiLocation.x.toSpaceComponent() + it.value + val imageCodepointMap = text.text.imageCharWidth.map { + it.value.name to it.key + }.toMap() + val index2 = ++parent.textIndex + val keys = (0.. + val yAxis = (loc.y + lineIndex * text.lineWidth).coerceAtLeast(-HudImpl.ADD_HEIGHT).coerceAtMost(HudImpl.ADD_HEIGHT) + val group = ShaderGroup(shader, text.text.name, text.scale, yAxis) + TextManagerImpl.getKey(group) ?: run { + val array = text.startJson() + text.text.array.forEach { + HudImpl.createBit(shader, yAxis) { y -> + array.add(jsonObjectOf( + "type" to "bitmap", + "file" to "$NAME_SPACE_ENCODED:${it.file}", + "ascent" to y, + "height" to (it.height * text.scale).roundToInt(), + "chars" to it.chars + )) } } - } - val result = HudTextData( - key, - imageMap, - text.background?.let { - fun getString(image: LoadedImage, file: String): WidthComponent { - val result = textIndex++.parseChar() - val height = (image.image.height.toDouble() * text.backgroundScale).roundToInt() - val div = height.toDouble() / image.image.height - HudImpl.createBit(HudShader( - gui, - text.renderScale, - text.layer - 1, - false, - loc.opacity + it.location.opacity, - text.property - ), loc.y + it.location.y) { y -> - array.add(jsonObjectOf( + var textIndex = TEXT_IMAGE_START_CODEPOINT + text.text.imageCharWidth.size + val textEncoded = "hud_${parent.name}_text_${index2 + 1}_${lineIndex + 1}".encodeKey() + val imageMap = HashMap() + val key = createAdventureKey(textEncoded) + text.text.imageCharWidth.forEach { + val height = (it.value.height.toDouble() * text.scale * text.emojiScale).roundToInt() + HudImpl.createBit(shader, loc.y + it.value.location.y + lineIndex * text.lineWidth) { y -> + array.add( + jsonObjectOf( "type" to "bitmap", - "file" to "$NAME_SPACE_ENCODED:$file.png", + "file" to "$NAME_SPACE_ENCODED:${"glyph_${it.value.name}".encodeKey()}.png", "ascent" to y, "height" to height, - "chars" to jsonArrayOf(result) - )) + "chars" to jsonArrayOf(it.key.parseChar()) + ) + ) + } + } + if (ConfigManagerImpl.loadMinecraftDefaultTextures) { + HudImpl.createBit(shader, loc.y + text.emojiLocation.y + lineIndex * text.lineWidth) { y -> + MinecraftManager.applyAll(array, y, text.emojiScale, key) { + ++textIndex + }.forEach { + imageMap[it.key] = text.emojiLocation.x.toSpaceComponent() + it.value + } + } + } + PackGenerator.addTask(resource.font + "$textEncoded.json") { + jsonObjectOf("providers" to array).toByteArray() + } + BackgroundKey( + key, + //TODO replace it to proper background in the future. + text.background?.let { + fun getString(image: LoadedImage, file: String): WidthComponent { + val result = (++textIndex).parseChar() + val height = (image.image.height.toDouble() * text.backgroundScale).roundToInt() + val div = height.toDouble() / image.image.height + HudImpl.createBit(HudShader( + gui, + text.renderScale, + text.layer - 1, + false, + loc.opacity * it.location.opacity, + text.property + ), loc.y + it.location.y + lineIndex * text.lineWidth) { y -> + array.add(jsonObjectOf( + "type" to "bitmap", + "file" to "$NAME_SPACE_ENCODED:$file.png", + "ascent" to y, + "height" to height, + "chars" to jsonArrayOf(result) + )) + } + return WidthComponent(Component.text() + .font(key) + .content(result) + .append(NEGATIVE_ONE_SPACE_COMPONENT.component), (image.image.width.toDouble() * div).roundToInt()) } - return WidthComponent(Component.text() - .font(key) - .content(result) - .append(NEGATIVE_ONE_SPACE_COMPONENT.component), (image.image.width.toDouble() * div).roundToInt()) + BackgroundLayout( + it.location.x, + getString(it.left, "background_${it.name}_left".encodeKey()), + getString(it.right, "background_${it.name}_right".encodeKey()), + getString(it.body, "background_${it.name}_body".encodeKey()) + ) } - BackgroundLayout( - it.location.x, - getString(it.left, "background_${it.name}_left".encodeKey()), - getString(it.right, "background_${it.name}_right".encodeKey()), - getString(it.body, "background_${it.name}_body".encodeKey()) - ) + ).apply { + TextManagerImpl.setKey(group, this) } - ) - PackGenerator.addTask(file + "$textEncoded.json") { - jsonObjectOf("providers" to array).toByteArray() } - TextManagerImpl.setKey(group, result) - result } TextRenderer( text.text.charWidth, + text.text.imageCharWidth, text.color, - key, + HudTextData( + keys, + imageCodepointMap, + text.splitWidth, + ), text.pattern, text.align, + text.lineAlign, text.scale, + text.emojiScale, loc.x, text.numberEquation, text.numberFormat, @@ -146,7 +155,7 @@ class HudTextElement( text.useLegacyFormat, text.legacySerializer, text.space, - text.conditions.and(text.text.conditions) + text.conditions and text.text.conditions ) }.getText(UpdateEvent.EMPTY) 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 94fb548c..791f446e 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/image/HudImage.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/image/HudImage.kt @@ -2,6 +2,7 @@ package kr.toxicity.hud.image 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.ListenerManagerImpl import kr.toxicity.hud.util.toConditions diff --git a/dist/src/main/kotlin/kr/toxicity/hud/image/LocationGroup.kt b/dist/src/main/kotlin/kr/toxicity/hud/image/LocationGroup.kt deleted file mode 100644 index 89a1f7e4..00000000 --- a/dist/src/main/kotlin/kr/toxicity/hud/image/LocationGroup.kt +++ /dev/null @@ -1,9 +0,0 @@ -package kr.toxicity.hud.image - -import kr.toxicity.hud.location.GuiLocation -import kr.toxicity.hud.location.PixelLocation - -data class LocationGroup( - val gui: GuiLocation, - val pixel: PixelLocation -) \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/image/ImageType.kt b/dist/src/main/kotlin/kr/toxicity/hud/image/enums/ImageType.kt similarity index 98% rename from dist/src/main/kotlin/kr/toxicity/hud/image/ImageType.kt rename to dist/src/main/kotlin/kr/toxicity/hud/image/enums/ImageType.kt index a95b579c..f13097aa 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/image/ImageType.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/image/enums/ImageType.kt @@ -1,4 +1,4 @@ -package kr.toxicity.hud.image +package kr.toxicity.hud.image.enums import kr.toxicity.hud.api.component.PixelComponent import kr.toxicity.hud.api.listener.HudListener diff --git a/dist/src/main/kotlin/kr/toxicity/hud/image/SplitType.kt b/dist/src/main/kotlin/kr/toxicity/hud/image/enums/SplitType.kt similarity index 97% rename from dist/src/main/kotlin/kr/toxicity/hud/image/SplitType.kt rename to dist/src/main/kotlin/kr/toxicity/hud/image/enums/SplitType.kt index d3d05a56..9b003a53 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/image/SplitType.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/image/enums/SplitType.kt @@ -1,5 +1,7 @@ -package kr.toxicity.hud.image +package kr.toxicity.hud.image.enums +import kr.toxicity.hud.image.LoadedImage +import kr.toxicity.hud.image.NamedLoadedImage import kr.toxicity.hud.util.circleCut import kr.toxicity.hud.util.removeEmptyWidth import java.awt.AlphaComposite diff --git a/dist/src/main/kotlin/kr/toxicity/hud/layout/BackgroundLayout.kt b/dist/src/main/kotlin/kr/toxicity/hud/layout/BackgroundLayout.kt index 8776f620..d84eaf26 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/layout/BackgroundLayout.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/layout/BackgroundLayout.kt @@ -2,6 +2,7 @@ package kr.toxicity.hud.layout import kr.toxicity.hud.api.component.WidthComponent +//TODO replace it to proper background in the future. class BackgroundLayout( val x: Int, 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 eb98c927..14c356c9 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/layout/HeadLayout.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/layout/HeadLayout.kt @@ -1,6 +1,7 @@ package kr.toxicity.hud.layout import kr.toxicity.hud.api.yaml.YamlObject +import kr.toxicity.hud.layout.enums.LayoutAlign import kr.toxicity.hud.location.PixelLocation import kr.toxicity.hud.manager.PlayerHeadManager import kr.toxicity.hud.player.head.HeadRenderType 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 94d3521c..6adf9ef9 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/layout/LayoutGroup.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/layout/LayoutGroup.kt @@ -2,7 +2,9 @@ package kr.toxicity.hud.layout import kr.toxicity.hud.api.yaml.YamlObject import kr.toxicity.hud.configuration.HudConfiguration -import kr.toxicity.hud.location.AnimationLocation +import kr.toxicity.hud.layout.enums.LayoutAlign +import kr.toxicity.hud.layout.enums.LayoutOffset +import kr.toxicity.hud.location.animation.AnimationLocation import kr.toxicity.hud.location.PixelLocation import kr.toxicity.hud.util.* import net.kyori.adventure.audience.Audience 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 7af06b81..4b94e315 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/layout/TextLayout.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/layout/TextLayout.kt @@ -3,6 +3,7 @@ package kr.toxicity.hud.layout import kr.toxicity.hud.api.yaml.YamlObject import kr.toxicity.hud.background.HudBackground import kr.toxicity.hud.equation.TEquation +import kr.toxicity.hud.layout.enums.LayoutAlign import kr.toxicity.hud.location.PixelLocation import kr.toxicity.hud.manager.BackgroundManager import kr.toxicity.hud.manager.ConfigManagerImpl @@ -25,6 +26,7 @@ class TextLayout( 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 { TEquation(it) @@ -46,6 +48,14 @@ class TextLayout( val useLegacyFormat: Boolean = yamlObject.getAsBoolean("use-legacy-format", ConfigManagerImpl.useLegacyFormat) val legacySerializer: ComponentDeserializer = yamlObject.get("legacy-serializer")?.asString()?.toLegacySerializer() ?: ConfigManagerImpl.legacySerializer + val line = yamlObject.getAsInt("line", 1).apply { + if (this < 1) throw RuntimeException("line cannot be < 1: $s") + } + val splitWidth = if (line == 1) Int.MAX_VALUE else yamlObject.getAsInt("split-width", 200).apply { + if (this < 1) throw RuntimeException("split-width cannot be < 1: $s") + } + val lineWidth = yamlObject.getAsInt("line-width", 10) + fun startJson() = jsonArrayOf( jsonObjectOf( "type" to "space", diff --git a/dist/src/main/kotlin/kr/toxicity/hud/layout/LayoutAlign.kt b/dist/src/main/kotlin/kr/toxicity/hud/layout/enums/LayoutAlign.kt similarity index 61% rename from dist/src/main/kotlin/kr/toxicity/hud/layout/LayoutAlign.kt rename to dist/src/main/kotlin/kr/toxicity/hud/layout/enums/LayoutAlign.kt index 1c2be3da..32e0278a 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/layout/LayoutAlign.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/layout/enums/LayoutAlign.kt @@ -1,4 +1,4 @@ -package kr.toxicity.hud.layout +package kr.toxicity.hud.layout.enums enum class LayoutAlign { LEFT, diff --git a/dist/src/main/kotlin/kr/toxicity/hud/layout/LayoutOffset.kt b/dist/src/main/kotlin/kr/toxicity/hud/layout/enums/LayoutOffset.kt similarity index 61% rename from dist/src/main/kotlin/kr/toxicity/hud/layout/LayoutOffset.kt rename to dist/src/main/kotlin/kr/toxicity/hud/layout/enums/LayoutOffset.kt index dff7d076..b8536884 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/layout/LayoutOffset.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/layout/enums/LayoutOffset.kt @@ -1,4 +1,4 @@ -package kr.toxicity.hud.layout +package kr.toxicity.hud.layout.enums enum class LayoutOffset { LEFT, diff --git a/dist/src/main/kotlin/kr/toxicity/hud/location/LocationGroup.kt b/dist/src/main/kotlin/kr/toxicity/hud/location/LocationGroup.kt new file mode 100644 index 00000000..6d90dd21 --- /dev/null +++ b/dist/src/main/kotlin/kr/toxicity/hud/location/LocationGroup.kt @@ -0,0 +1,6 @@ +package kr.toxicity.hud.location + +data class LocationGroup( + val gui: GuiLocation, + val pixel: PixelLocation +) \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/location/AnimationLocation.kt b/dist/src/main/kotlin/kr/toxicity/hud/location/animation/AnimationLocation.kt similarity index 92% rename from dist/src/main/kotlin/kr/toxicity/hud/location/AnimationLocation.kt rename to dist/src/main/kotlin/kr/toxicity/hud/location/animation/AnimationLocation.kt index 10458ab5..c8b19c70 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/location/AnimationLocation.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/location/animation/AnimationLocation.kt @@ -1,7 +1,8 @@ -package kr.toxicity.hud.location +package kr.toxicity.hud.location.animation import kr.toxicity.hud.api.yaml.YamlObject import kr.toxicity.hud.equation.EquationTriple +import kr.toxicity.hud.location.PixelLocation import kotlin.math.roundToInt class AnimationLocation( diff --git a/dist/src/main/kotlin/kr/toxicity/hud/location/AnimationType.kt b/dist/src/main/kotlin/kr/toxicity/hud/location/animation/AnimationType.kt similarity index 55% rename from dist/src/main/kotlin/kr/toxicity/hud/location/AnimationType.kt rename to dist/src/main/kotlin/kr/toxicity/hud/location/animation/AnimationType.kt index a30adcd9..0571043c 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/location/AnimationType.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/location/animation/AnimationType.kt @@ -1,4 +1,4 @@ -package kr.toxicity.hud.location +package kr.toxicity.hud.location.animation enum class AnimationType { LOOP, diff --git a/dist/src/main/kotlin/kr/toxicity/hud/manager/BackgroundManager.kt b/dist/src/main/kotlin/kr/toxicity/hud/manager/BackgroundManager.kt index 481249d8..e5ecfbe1 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/manager/BackgroundManager.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/manager/BackgroundManager.kt @@ -8,8 +8,8 @@ import kr.toxicity.hud.util.* import net.kyori.adventure.audience.Audience import java.io.File +//TODO replace it to proper background in the future. object BackgroundManager : BetterHudManager { - private val backgroundMap = HashMap() override fun start() { diff --git a/dist/src/main/kotlin/kr/toxicity/hud/manager/CommandManager.kt b/dist/src/main/kotlin/kr/toxicity/hud/manager/CommandManager.kt index 947ed343..07067fc0 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/manager/CommandManager.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/manager/CommandManager.kt @@ -44,6 +44,14 @@ object CommandManager : BetterHudManager { } } }, MiniMessage.miniMessage(), BOOTSTRAP.logger()) + .exceptionHandler { + if (ConfigManagerImpl.debug) { + warn( + "Stack trace:", + it.stackTraceToString() + ) + } + } .addSerializer(HudPlayerStack::class.java, ClassSerializer.builder { _, s -> if (s == "all") HudPlayerStack(PlayerManagerImpl.allHudPlayer) else PlayerManagerImpl.getHudPlayer(s)?.let { diff --git a/dist/src/main/kotlin/kr/toxicity/hud/manager/HudManagerImpl.kt b/dist/src/main/kotlin/kr/toxicity/hud/manager/HudManagerImpl.kt index 78df70ff..ad3bb778 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/manager/HudManagerImpl.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/manager/HudManagerImpl.kt @@ -23,7 +23,7 @@ object HudManagerImpl : BetterHudManager, HudManager { DATA_FOLDER.subFolder("huds").forEachAllYaml(sender) { file, s, yamlObject -> runWithExceptionHandling(sender, "Unable to load this hud: $s in ${file.name}") { hudMap.putSync("hud", s) { - HudImpl(file.path, s, resource.font, yamlObject) + HudImpl(file.path, s, resource, yamlObject) } } } 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 730e3f1e..324e43c7 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/manager/ImageManager.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/manager/ImageManager.kt @@ -2,8 +2,8 @@ package kr.toxicity.hud.manager import kr.toxicity.hud.api.component.WidthComponent import kr.toxicity.hud.image.HudImage -import kr.toxicity.hud.image.ImageType -import kr.toxicity.hud.image.SplitType +import kr.toxicity.hud.image.enums.ImageType +import kr.toxicity.hud.image.enums.SplitType import kr.toxicity.hud.pack.PackGenerator import kr.toxicity.hud.resource.GlobalResource import kr.toxicity.hud.shader.ShaderGroup diff --git a/dist/src/main/kotlin/kr/toxicity/hud/manager/PopupManagerImpl.kt b/dist/src/main/kotlin/kr/toxicity/hud/manager/PopupManagerImpl.kt index 28f85889..17361a56 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/manager/PopupManagerImpl.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/manager/PopupManagerImpl.kt @@ -19,7 +19,7 @@ object PopupManagerImpl : BetterHudManager, PopupManager { DATA_FOLDER.subFolder("popups").forEachAllYaml(sender) { file, s, yamlObject -> runCatching { popupMap.putSync("popup", s) { - PopupImpl(file.path, resource.font, s, yamlObject) + PopupImpl(file.path, resource, s, yamlObject) } }.onFailure { e -> warn( 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 6bd66a7f..e9576afd 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/manager/TextManagerImpl.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/manager/TextManagerImpl.kt @@ -3,16 +3,13 @@ package kr.toxicity.hud.manager import com.google.gson.JsonArray import kr.toxicity.hud.api.manager.TextManager import kr.toxicity.hud.configuration.PluginConfiguration -import kr.toxicity.hud.location.PixelLocation import kr.toxicity.hud.image.LocatedImage +import kr.toxicity.hud.location.PixelLocation import kr.toxicity.hud.pack.PackGenerator import kr.toxicity.hud.placeholder.ConditionBuilder import kr.toxicity.hud.resource.GlobalResource import kr.toxicity.hud.shader.ShaderGroup -import kr.toxicity.hud.text.CharWidth -import kr.toxicity.hud.text.HudText -import kr.toxicity.hud.text.HudTextArray -import kr.toxicity.hud.text.HudTextData +import kr.toxicity.hud.text.* import kr.toxicity.hud.util.* import net.kyori.adventure.audience.Audience import java.awt.AlphaComposite @@ -23,7 +20,6 @@ import java.io.File import java.io.InputStreamReader import java.util.* import java.util.concurrent.ConcurrentHashMap -import kotlin.collections.HashMap import kotlin.math.roundToInt object TextManagerImpl : BetterHudManager, TextManager { @@ -63,7 +59,7 @@ object TextManagerImpl : BetterHudManager, TextManager { private val textCacheMap = HashMap() private val textWidthMap = HashMap() - private val textKeyMap = ConcurrentHashMap() + private val textKeyMap = ConcurrentHashMap() private val defaultBitmapImageMap = HashMap() private val translatableString = HashMap>() @@ -133,7 +129,7 @@ object TextManagerImpl : BetterHudManager, TextManager { @Synchronized fun getKey(shaderGroup: ShaderGroup) = textKeyMap[shaderGroup] @Synchronized - fun setKey(shaderGroup: ShaderGroup, key: HudTextData) { + fun setKey(shaderGroup: ShaderGroup, key: BackgroundKey) { textKeyMap[shaderGroup] = key } @@ -257,7 +253,7 @@ object TextManagerImpl : BetterHudManager, TextManager { provider, scale, resource.textures, - HashMap().apply { + TreeMap().apply { section.get("images")?.asObject() ?.forEachSubConfiguration { key, yamlObject -> put(key, LocatedImage( @@ -438,7 +434,7 @@ object TextManagerImpl : BetterHudManager, TextManager { ): HudText { return synchronized(textCacheMap) { textCacheMap[TextCache(saveName, emptySet())]?.let { old -> - HudText(path, saveName, old.array, old.images, old.charWidth, old.conditions) + HudText(path, saveName, old.array, old.charWidth, old.imageCharWidth, old.conditions) } } ?: run { val saveFontName = synchronized (this) { @@ -495,8 +491,8 @@ object TextManagerImpl : BetterHudManager, TextManager { path, saveName, textArray, - mapOf(), charWidthMap, + mapOf(), condition ) } @@ -520,7 +516,7 @@ object TextManagerImpl : BetterHudManager, TextManager { ): HudText { return synchronized(textCacheMap) { textCacheMap[TextCache(saveName, images.keys)]?.let { old -> - HudText(path, saveName, old.array, old.images, old.charWidth, old.conditions) + HudText(path, saveName, old.array, old.charWidth, old.imageCharWidth, old.conditions) } } ?: run { val saveFontName = synchronized (this) { @@ -576,7 +572,17 @@ object TextManagerImpl : BetterHudManager, TextManager { } val textList = ArrayList() var i = 0 + var imageStart = TEXT_IMAGE_START_CODEPOINT + val imageCharWidthMap = HashMap() images.forEach { + val h = (it.value.image.image.height.toDouble() * it.value.scale).roundToInt() + val div = h / it.value.image.image.height.toDouble() + imageCharWidthMap[++imageStart] = ImageCharWidth( + it.key, + it.value.location, + (it.value.image.image.width * div).roundToInt(), + h + ) PackGenerator.addTask(imageSaveFolder + "${"glyph_${it.key}".encodeKey()}.png") { it.value.image.image.toByteArray() } @@ -615,7 +621,7 @@ object TextManagerImpl : BetterHudManager, TextManager { } } } - val result = HudText(path, saveName, textList, images, charWidthMap, condition) + val result = HudText(path, saveName, textList, charWidthMap, imageCharWidthMap, condition) synchronized(textCacheMap) { textCacheMap[TextCache(saveName, images.keys)] = result } @@ -629,10 +635,4 @@ object TextManagerImpl : BetterHudManager, TextManager { override fun end() { } - - override fun getWidth(textName: String, scale: Double, text: String): Int = textMap[textName]?.let { - text.codePoints().map { i -> - if (i == ' '.code) 4 else (it.charWidth[i]?.scaledWidth(scale) ?: 0) + 1 - }.sum() - } ?: 0 } \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/pack/PackGenerator.kt b/dist/src/main/kotlin/kr/toxicity/hud/pack/PackGenerator.kt index 1565cd63..1d400e1a 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/pack/PackGenerator.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/pack/PackGenerator.kt @@ -101,6 +101,7 @@ object PackGenerator { get() = Collections.unmodifiableMap(builder.byteArrayMap) override fun close() { + if (PackUploader.stop()) info("Resource pack host is stopped.") } override fun invoke(p1: PackFile) { @@ -132,6 +133,7 @@ object PackGenerator { get() = Collections.unmodifiableMap(builder.byteArrayMap) override fun close() { + if (PackUploader.stop()) info("Resource pack host is stopped.") builder.close() } override fun invoke(p1: PackFile) { 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 31c6cb41..bd3a004c 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/popup/PopupImpl.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/popup/PopupImpl.kt @@ -15,12 +15,13 @@ import kr.toxicity.hud.location.PixelLocation import kr.toxicity.hud.manager.* import kr.toxicity.hud.pack.PackGenerator import kr.toxicity.hud.location.GuiLocation +import kr.toxicity.hud.resource.GlobalResource import kr.toxicity.hud.util.* import java.util.* class PopupImpl( override val path: String, - file: List, + resource: GlobalResource, val internalName: String, section: YamlObject ) : Popup, HudConfiguration { @@ -83,7 +84,7 @@ class PopupImpl( yamlObject.get("pixel")?.asObject()?.let { pixel -> PixelLocation(pixel) } ?: PixelLocation.zero, - file, + resource.font, ) } }.ifNull("layouts configuration not set.").ifEmpty { @@ -118,7 +119,7 @@ class PopupImpl( it.value to it.key }.toTypedArray()) )) - PackGenerator.addTask(file + "$imageEncoded.json") { + PackGenerator.addTask(resource.font + "$imageEncoded.json") { jsonObjectOf("providers" to arr).toByteArray() } } 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 a7b8c54c..ad99a0a3 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/popup/PopupLayout.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/popup/PopupLayout.kt @@ -7,12 +7,13 @@ 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.location.PixelLocation import kr.toxicity.hud.image.LoadedImage -import kr.toxicity.hud.image.LocationGroup import kr.toxicity.hud.layout.BackgroundLayout -import kr.toxicity.hud.location.AnimationType +import kr.toxicity.hud.location.LocationGroup import kr.toxicity.hud.layout.LayoutGroup +import kr.toxicity.hud.location.animation.AnimationType +import kr.toxicity.hud.location.GuiLocation +import kr.toxicity.hud.location.PixelLocation import kr.toxicity.hud.manager.* import kr.toxicity.hud.pack.PackGenerator import kr.toxicity.hud.player.head.HeadKey @@ -21,9 +22,9 @@ import kr.toxicity.hud.player.head.HeadRenderType.STANDARD import kr.toxicity.hud.renderer.HeadRenderer import kr.toxicity.hud.renderer.ImageRenderer import kr.toxicity.hud.renderer.TextRenderer -import kr.toxicity.hud.location.GuiLocation import kr.toxicity.hud.shader.HudShader import kr.toxicity.hud.shader.ShaderGroup +import kr.toxicity.hud.text.BackgroundKey import kr.toxicity.hud.text.HudTextData import kr.toxicity.hud.util.* import net.kyori.adventure.text.Component @@ -141,8 +142,8 @@ class PopupLayout( "chars" to jsonArrayOf(char) )) } - val xWidth = Math.round(it.image.image.width.toDouble() * scale).toInt() - val comp = WidthComponent(Component.text().content(char).font(parent.imageKey), xWidth) + NEGATIVE_ONE_SPACE_COMPONENT + NEW_LAYER + val xWidth = (it.image.image.width.toDouble() * scale).roundToInt() + val comp = WidthComponent(Component.text().content(char).font(parent.imageKey), xWidth) + NEGATIVE_ONE_SPACE_COMPONENT ImageManager.setImage(shaderGroup, comp) comp } @@ -159,7 +160,7 @@ class PopupLayout( "chars" to jsonArrayOf(char) )) } - val comp = WidthComponent(Component.text().content(char).font(parent.imageKey), Math.round(it.image.image.width.toDouble() * target.scale).toInt()) + NEGATIVE_ONE_SPACE_COMPONENT + NEW_LAYER + val comp = WidthComponent(Component.text().content(char).font(parent.imageKey), Math.round(it.image.image.width.toDouble() * target.scale).toInt()) + NEGATIVE_ONE_SPACE_COMPONENT list.add(comp.toPixelComponent(pixel.x)) } @@ -191,99 +192,111 @@ class PopupLayout( textLayout.property ) val group = ShaderGroup(textShader, textLayout.text.name, textLayout.scale, pixel.y) - val textKey = TextManagerImpl.getKey(group) ?: run { - val index = ++textIndex - val array = textLayout.startJson() - HudImpl.createBit(textShader, pixel.y) { y -> - textLayout.text.array.forEach { - array.add(jsonObjectOf( - "type" to "bitmap", - "file" to "$NAME_SPACE_ENCODED:${it.file}", - "ascent" to y, - "height" to (it.height * textLayout.scale).roundToInt(), - "chars" to it.chars - )) - } - } - var textIndex = 0xC0000 - val imageMap = HashMap() - val textEncoded = "popup_${parent.name}_text_${index}".encodeKey() - val key = createAdventureKey(textEncoded) - textLayout.text.images.forEach { - val result = textIndex++.parseChar() - val imageScale = it.value.scale * textLayout.scale - val height = (it.value.image.image.height.toDouble() * imageScale).roundToInt() - val div = height.toDouble() / it.value.image.image.height - HudImpl.createBit(textShader, pixel.y + it.value.location.y) { y -> - array.add(jsonObjectOf( - "type" to "bitmap", - "file" to "$NAME_SPACE_ENCODED:${"glyph_${it.key}".encodeKey()}.png", - "ascent" to y, - "height" to height, - "chars" to jsonArrayOf(result) - )) - } - imageMap[it.key] = it.value.location.x.toSpaceComponent() + WidthComponent(Component.text() - .font(key) - .content(result) - .append(NEGATIVE_ONE_SPACE_COMPONENT.component), (it.value.image.image.width.toDouble() * div).roundToInt()) - } - if (ConfigManagerImpl.loadMinecraftDefaultTextures) { - HudImpl.createBit(textShader, textLayout.emojiLocation.y) { y -> - MinecraftManager.applyAll(array, y, textLayout.emojiScale, key) { - textIndex++ - }.forEach { - imageMap[it.key] = textLayout.emojiLocation.x.toSpaceComponent() + it.value + val imageCodepointMap = textLayout.text.imageCharWidth.map { + it.value.name to it.key + }.toMap() + val index = ++textIndex + val keys = (0.. + TextManagerImpl.getKey(group) ?: run { + val array = textLayout.startJson() + HudImpl.createBit(textShader, pixel.y + lineIndex * textLayout.lineWidth) { y -> + textLayout.text.array.forEach { + array.add( + jsonObjectOf( + "type" to "bitmap", + "file" to "$NAME_SPACE_ENCODED:${it.file}", + "ascent" to y, + "height" to (it.height * textLayout.scale).roundToInt(), + "chars" to it.chars + ) + ) } } - } - val result = HudTextData( - key, - imageMap, - textLayout.background?.let { - fun getString(image: LoadedImage, file: String): WidthComponent { - val result = textIndex++.parseChar() - val height = (image.image.height.toDouble() * textLayout.backgroundScale).roundToInt() - val div = height.toDouble() / image.image.height - HudImpl.createBit(HudShader( - elementGui, - textLayout.renderScale, - textLayout.layer - 1, - false, - pixel.opacity + it.location.opacity, - textLayout.property - ), pixel.y + it.location.y) { y -> - array.add(jsonObjectOf( + val imageMap = HashMap() + val textEncoded = "popup_${parent.name}_text_${index}".encodeKey() + val key = createAdventureKey(textEncoded) + var imageTextIndex = TEXT_IMAGE_START_CODEPOINT + textLayout.text.imageCharWidth.size + textLayout.text.imageCharWidth.forEach { + val height = (it.value.height.toDouble() * textLayout.scale).roundToInt() + HudImpl.createBit(textShader, pixel.y + it.value.location.y + lineIndex * textLayout.lineWidth) { y -> + array.add( + jsonObjectOf( "type" to "bitmap", - "file" to "$NAME_SPACE_ENCODED:$file.png", + "file" to "$NAME_SPACE_ENCODED:${"glyph_${it.value.name}".encodeKey()}.png", "ascent" to y, "height" to height, - "chars" to jsonArrayOf(result) - )) + "chars" to jsonArrayOf(it.key.parseChar()) + ) + ) + } + } + if (ConfigManagerImpl.loadMinecraftDefaultTextures) { + HudImpl.createBit(textShader, pixel.y + textLayout.emojiLocation.y + lineIndex * textLayout.lineWidth) { y -> + MinecraftManager.applyAll(array, y, textLayout.emojiScale, key) { + ++imageTextIndex + }.forEach { + imageMap[it.key] = textLayout.emojiLocation.x.toSpaceComponent() + it.value } - return WidthComponent(Component.text().font(key).content(result).append(NEGATIVE_ONE_SPACE_COMPONENT.component), (image.image.width.toDouble() * div).roundToInt()) } - BackgroundLayout( - it.location.x, - getString(it.left, "background_${it.name}_left".encodeKey()), - getString(it.right, "background_${it.name}_right".encodeKey()), - getString(it.body, "background_${it.name}_body".encodeKey()) - ) } - ) - PackGenerator.addTask(file + "$textEncoded.json") { - jsonObjectOf("providers" to array).toByteArray() + PackGenerator.addTask(file + "$textEncoded.json") { + jsonObjectOf("providers" to array).toByteArray() + } + BackgroundKey( + key, + //TODO replace it to proper background in the future. + textLayout.background?.let { + fun getString(image: LoadedImage, file: String): WidthComponent { + val result = (++imageTextIndex).parseChar() + val height = (image.image.height.toDouble() * textLayout.backgroundScale).roundToInt() + val div = height.toDouble() / image.image.height + HudImpl.createBit(HudShader( + elementGui, + textLayout.renderScale, + textLayout.layer - 1, + false, + pixel.opacity * it.location.opacity, + textLayout.property + ), pixel.y + it.location.y + lineIndex * textLayout.lineWidth) { y -> + array.add(jsonObjectOf( + "type" to "bitmap", + "file" to "$NAME_SPACE_ENCODED:$file.png", + "ascent" to y, + "height" to height, + "chars" to jsonArrayOf(result) + )) + } + return WidthComponent(Component.text() + .font(key) + .content(result) + .append(NEGATIVE_ONE_SPACE_COMPONENT.component), (image.image.width.toDouble() * div).roundToInt()) + } + BackgroundLayout( + it.location.x, + getString(it.left, "background_${it.name}_left".encodeKey()), + getString(it.right, "background_${it.name}_right".encodeKey()), + getString(it.body, "background_${it.name}_body".encodeKey()) + ) + } + ).apply { + TextManagerImpl.setKey(group, this) + } } - TextManagerImpl.setKey(group, result) - result } TextRenderer( textLayout.text.charWidth, + textLayout.text.imageCharWidth, textLayout.color, - textKey, + HudTextData( + keys, + imageCodepointMap, + textLayout.splitWidth + ), textLayout.pattern, textLayout.align, + textLayout.lineAlign, textLayout.scale, + textLayout.emojiScale, pixel.x, textLayout.numberEquation, textLayout.numberFormat, @@ -293,7 +306,7 @@ class PopupLayout( textLayout.useLegacyFormat, textLayout.legacySerializer, textLayout.space, - textLayout.conditions.and(textLayout.text.conditions) + textLayout.conditions and textLayout.text.conditions ) } @@ -374,7 +387,7 @@ class PopupLayout( headLayout.type, headLayout.follow, headLayout.cancelIfFollowerNotExists, - headLayout.conditions.and(headLayout.head.conditions) + headLayout.conditions and headLayout.head.conditions ) } } diff --git a/dist/src/main/kotlin/kr/toxicity/hud/renderer/HeadRenderer.kt b/dist/src/main/kotlin/kr/toxicity/hud/renderer/HeadRenderer.kt index ec7377f5..2b590053 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/renderer/HeadRenderer.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/renderer/HeadRenderer.kt @@ -5,7 +5,7 @@ import kr.toxicity.hud.api.component.WidthComponent import kr.toxicity.hud.api.player.HudPlayer import kr.toxicity.hud.api.player.HudPlayerHead import kr.toxicity.hud.api.update.UpdateEvent -import kr.toxicity.hud.layout.LayoutAlign +import kr.toxicity.hud.layout.enums.LayoutAlign import kr.toxicity.hud.manager.PlaceholderManagerImpl import kr.toxicity.hud.manager.PlayerHeadManager import kr.toxicity.hud.manager.PlayerManagerImpl diff --git a/dist/src/main/kotlin/kr/toxicity/hud/renderer/TextRenderer.kt b/dist/src/main/kotlin/kr/toxicity/hud/renderer/TextRenderer.kt index bc4418e9..6a610553 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/renderer/TextRenderer.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/renderer/TextRenderer.kt @@ -5,29 +5,34 @@ 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.equation.TEquation -import kr.toxicity.hud.layout.LayoutAlign +import kr.toxicity.hud.layout.enums.LayoutAlign import kr.toxicity.hud.manager.PlaceholderManagerImpl import kr.toxicity.hud.manager.PlayerManagerImpl import kr.toxicity.hud.placeholder.ConditionBuilder import kr.toxicity.hud.text.CharWidth import kr.toxicity.hud.text.HudTextData +import kr.toxicity.hud.text.ImageCharWidth import kr.toxicity.hud.util.* import net.kyori.adventure.text.Component -import net.kyori.adventure.text.TextComponent import net.kyori.adventure.text.TextReplacementConfig +import net.kyori.adventure.text.format.Style import net.kyori.adventure.text.format.TextColor -import net.kyori.adventure.text.format.TextDecoration import net.kyori.adventure.text.minimessage.MiniMessage import java.text.DecimalFormat import java.util.regex.Pattern +import kotlin.math.ceil +import kotlin.math.floor class TextRenderer( private val widthMap: Map, + private val imageWidthMap: Map, private val defaultColor: TextColor, private val data: HudTextData, pattern: String, private val align: LayoutAlign, + private val lineAlign: LayoutAlign, private val scale: Double, + private val emojiScale: Double, private val x: Int, private val numberEquation: TEquation, @@ -43,7 +48,6 @@ class TextRenderer( private val condition: ConditionBuilder ) { companion object { - private const val SPACE_POINT = ' '.code private val decimalPattern = Pattern.compile("([0-9]+((\\.([0-9]+))?))") private val allPattern = Pattern.compile(".+") private val imagePattern = Pattern.compile("<(?(image|space)):(?(([a-zA-Z]|[0-9]|-)+))>") @@ -57,6 +61,22 @@ class TextRenderer( private val parsedPatter = PlaceholderManagerImpl.parse(pattern) + private val widthViewer = ValueViewer, Int>() + .addFunction( + { + when (it.first.font()) { + SPACE_KEY -> it.second - CURRENT_CENTER_SPACE_CODEPOINT + LEGACY_SPACE_KEY -> it.second - LEGACY_CENTER_SPACE_CODEPOINT + null -> when (it.second) { + TEXT_SPACE_KEY_CODEPOINT -> space + else -> (widthMap[it.second]?.scaledWidth(scale) ?: imageWidthMap[it.second]?.scaledWidth(scale * emojiScale))?.let { c -> c + 1 } + } + else -> null + } + } + ) + + fun getText(reason: UpdateEvent): (HudPlayer) -> PixelComponent { val buildPattern = parsedPatter(reason) val cond = condition.build(reason) @@ -73,108 +93,94 @@ class TextRenderer( } } if (!cond(targetHudPlayer)) return@build EMPTY_PIXEL_COMPONENT - var width = 0 - val patternResult = buildPattern(targetHudPlayer) - var targetString = (if (useLegacyFormat) legacySerializer(patternResult) else Component.text(patternResult)) - .color(defaultColor) - .replaceText(TextReplacementConfig.builder() - .match(imagePattern) - .replacement { r, _ -> - when (r.group(1)) { - "image" -> data.images[r.group(3)]?.let { - width += it.width - it.component.build() - } ?: Component.empty() - "space" -> r.group(3).toIntOrNull()?.toSpaceComponent()?.let { - width += it.width - it.component - } ?: Component.empty() - else -> Component.empty() - } - } - .build()) - .replaceText(TextReplacementConfig.builder() - .match(allPattern) - .replacement { r, _ -> - MiniMessage.miniMessage().deserialize(r.group()) - } - .build()) - if (!disableNumberFormat) { - targetString = targetString.replaceText(TextReplacementConfig.builder() - .match(decimalPattern) - .replacement { r, _ -> - val g = r.group() - Component.text(runCatching { - numberPattern.format(numberEquation.evaluate(g.toDouble())) - }.getOrDefault(g)) + + var widthComp = EMPTY_WIDTH_COMPONENT + + val compList = buildPattern(targetHudPlayer) + .parseToComponent() + .split(data.splitWidth, space, widthViewer) + var max = 0 + compList.forEachIndexed { index, comp -> + if (data.font.lastIndex < index) return@forEachIndexed + val backgroundKey = data.font[index] + var finalComp = comp + finalComp.component.font(backgroundKey.key) + //TODO replace it to proper background in the future. + backgroundKey.background?.let { + val builder = Component.text().append(it.left.component) + var length = 0 + while (length < comp.width) { + builder.append(it.body.component) + length += it.body.width } - .build()) + val total = it.left.width + length + it.right.width + val minus = -total + (length - comp.width) / 2 + it.left.width - it.x + finalComp = it.x.toSpaceComponent() + WidthComponent(builder.append(it.right.component), total) + minus.toSpaceComponent() + finalComp + } + if (finalComp.width > max) max = finalComp.width + widthComp = if (widthComp.width == 0) finalComp else widthComp plusWithAlign finalComp } - fun hasDecoration(parent: Boolean, state: TextDecoration.State) = when (state) { - TextDecoration.State.TRUE -> true - TextDecoration.State.NOT_SET -> parent - TextDecoration.State.FALSE -> false + widthComp.toPixelComponent(when (align) { + LayoutAlign.LEFT -> x + LayoutAlign.CENTER -> x - max / 2 + LayoutAlign.RIGHT -> x - max + }) + } + } + + private infix fun WidthComponent.plusWithAlign(other: WidthComponent) = plusWithAlign(lineAlign, other) + private fun WidthComponent.plusWithAlign(align: LayoutAlign, other: WidthComponent) = when (align) { + LayoutAlign.LEFT -> this + (-width).toSpaceComponent() + other + LayoutAlign.CENTER -> { + if (width > other.width) { + val div = (width - other.width).toDouble() / 2 + this + floor(-width + div).toInt().toSpaceComponent() + other + ceil(div).toInt().toSpaceComponent() + } else { + val div = floor((other.width - width).toDouble() / 2).toInt() + div.toSpaceComponent() + this + (-width - div).toSpaceComponent() + other + } + } + LayoutAlign.RIGHT -> { + if (width > other.width) { + val div = width - other.width + this + (-width + div).toSpaceComponent() + other + } else { + val div = other.width - width + div.toSpaceComponent() + this + (-width - div).toSpaceComponent() + other } - fun applyDecoration(component: Component, bold: Boolean, italic: Boolean): Component { - var ret = component - if (ret is TextComponent && ret.font() == null) { - val codepoint = ret.content().codePoints().toArray() - var add = 0 - if (bold) add++ - if (italic) add++ - if (space != 0) { - ret = ret.content(buildString { - codepoint.forEachIndexed { index, i -> - appendCodePoint(i) - width += if (i != SPACE_POINT) widthMap[i]?.let { width -> - width.scaledWidth(scale) + add + 1 - } ?: 0 else 4 + add - if (index < codepoint.lastIndex) { - width += space + add - appendCodePoint(TEXT_SPACE_KEY_CODEPOINT) - } - } - }) - } else { - width += codepoint.sumOf { - if (it != SPACE_POINT) widthMap[it]?.let { width -> - width.scaledWidth(scale) + add + 1 - } ?: 0 else 4 + add - } + } + } + + private fun String.parseToComponent(): Component { + var targetString = (if (useLegacyFormat) legacySerializer(this) else Component.text(this)) + .color(defaultColor) + .replaceText(TextReplacementConfig.builder() + .match(imagePattern) + .replacement { r, _ -> + when (r.group(1)) { + "image" -> data.imageCodepoint[r.group(3)]?.let { Component.text(it.parseChar()) } ?: Component.empty() + "space" -> r.group(3).toIntOrNull()?.toSpaceComponent()?.component ?: Component.empty() + else -> Component.empty() } - ret = ret.font(data.word) } - return ret.children(ret.children().map { - applyDecoration( - it, - hasDecoration(bold, it.decoration(TextDecoration.BOLD)), - hasDecoration(italic, it.decoration(TextDecoration.ITALIC)), - ) - }) - } - val finalComp = Component.text().append(applyDecoration( - targetString, - hasDecoration(false, targetString.decoration(TextDecoration.BOLD)), - hasDecoration(false, targetString.decoration(TextDecoration.ITALIC)) - )) - var comp = WidthComponent(finalComp, width) - - data.background?.let { - val builder = Component.text().append(it.left.component) - var length = 0 - while (length < comp.width) { - builder.append(it.body.component) - length += it.body.width + .build()) + .replaceText(TextReplacementConfig.builder() + .match(allPattern) + .replacement { r, _ -> + MiniMessage.miniMessage().deserialize(r.group()) } - val total = it.left.width + length + it.right.width - val minus = -total + (length - comp.width) / 2 + it.left.width - it.x - comp = it.x.toSpaceComponent() + WidthComponent(builder.append(it.right.component), total) + minus.toSpaceComponent() + comp - } - comp.toPixelComponent(when (align) { - LayoutAlign.LEFT -> x - LayoutAlign.CENTER -> x - comp.width / 2 - LayoutAlign.RIGHT -> x - comp.width - }) + .build()) + if (!disableNumberFormat) { + targetString = targetString.replaceText(TextReplacementConfig.builder() + .match(decimalPattern) + .replacement { r, _ -> + val g = r.group() + Component.text(runCatching { + numberPattern.format(numberEquation.evaluate(g.toDouble())) + }.getOrDefault(g)) + } + .build()) } + return targetString } } \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/shader/HudShader.kt b/dist/src/main/kotlin/kr/toxicity/hud/shader/HudShader.kt index d2d197f7..54bdbdab 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/shader/HudShader.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/shader/HudShader.kt @@ -29,4 +29,13 @@ data class HudShader( override fun compareTo(other: HudShader): Int { return comparator.compare(this, other) } + + fun toBackground() = HudShader( + gui, + renderScale, + layer - 1, + false, + opacity, + property + ) } \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/text/BackgroundKey.kt b/dist/src/main/kotlin/kr/toxicity/hud/text/BackgroundKey.kt new file mode 100644 index 00000000..34e3e1e6 --- /dev/null +++ b/dist/src/main/kotlin/kr/toxicity/hud/text/BackgroundKey.kt @@ -0,0 +1,10 @@ +package kr.toxicity.hud.text + +import kr.toxicity.hud.layout.BackgroundLayout +import net.kyori.adventure.key.Key + +//TODO replace it to proper background in the future. +class BackgroundKey( + val key: Key, + val background: BackgroundLayout? +) \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/text/CharWidth.kt b/dist/src/main/kotlin/kr/toxicity/hud/text/CharWidth.kt index 1b2382ee..972d4877 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/text/CharWidth.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/text/CharWidth.kt @@ -2,6 +2,6 @@ package kr.toxicity.hud.text import kotlin.math.roundToInt -data class CharWidth(val width: Int, val height: Int) { +open class CharWidth(val width: Int, val height: Int) { fun scaledWidth(scale: Double) = (width.toDouble() / height.toDouble() * (height * scale).roundToInt()).roundToInt() } \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/text/HudText.kt b/dist/src/main/kotlin/kr/toxicity/hud/text/HudText.kt index c1acd33c..20f85854 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/text/HudText.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/text/HudText.kt @@ -1,14 +1,13 @@ package kr.toxicity.hud.text import kr.toxicity.hud.configuration.HudConfiguration -import kr.toxicity.hud.image.LocatedImage import kr.toxicity.hud.placeholder.ConditionBuilder class HudText( override val path: String, val name: String, val array: List, - val images: Map, val charWidth: Map, + val imageCharWidth: Map, val conditions: ConditionBuilder ) : HudConfiguration \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/text/HudTextData.kt b/dist/src/main/kotlin/kr/toxicity/hud/text/HudTextData.kt index b62fe014..e1dd190b 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/text/HudTextData.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/text/HudTextData.kt @@ -1,12 +1,7 @@ package kr.toxicity.hud.text -import kr.toxicity.hud.api.component.WidthComponent -import kr.toxicity.hud.layout.BackgroundLayout -import net.kyori.adventure.key.Key - class HudTextData( - val word: Key, - - val images: Map, - val background: BackgroundLayout? + val font: List, + val imageCodepoint: Map, + val splitWidth: Int ) \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/text/ImageCharWidth.kt b/dist/src/main/kotlin/kr/toxicity/hud/text/ImageCharWidth.kt new file mode 100644 index 00000000..a9e48353 --- /dev/null +++ b/dist/src/main/kotlin/kr/toxicity/hud/text/ImageCharWidth.kt @@ -0,0 +1,5 @@ +package kr.toxicity.hud.text + +import kr.toxicity.hud.location.PixelLocation + +class ImageCharWidth(val name: String, val location: PixelLocation, width: Int, height: Int) : CharWidth(width, height) \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/util/Adventures.kt b/dist/src/main/kotlin/kr/toxicity/hud/util/Adventures.kt index ffcbc81f..1c615166 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/util/Adventures.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/util/Adventures.kt @@ -5,14 +5,19 @@ import kr.toxicity.hud.api.component.WidthComponent import kr.toxicity.hud.manager.ConfigManagerImpl import net.kyori.adventure.key.Key import net.kyori.adventure.text.Component +import net.kyori.adventure.text.ComponentLike +import net.kyori.adventure.text.TextComponent import net.kyori.adventure.text.format.NamedTextColor +import net.kyori.adventure.text.format.Style import net.kyori.adventure.text.format.TextColor import net.kyori.adventure.text.format.TextDecoration import kotlin.math.abs +import kotlin.math.roundToInt fun createAdventureKey(value: String) = Key.key(NAME_SPACE_ENCODED, value) const val TEXT_SPACE_KEY_CODEPOINT = 0xC0000 +const val TEXT_IMAGE_START_CODEPOINT = 0xC0000 val CONSOLE get() = BOOTSTRAP.console() @@ -42,13 +47,14 @@ val EMPTY_WIDTH_COMPONENT get() = WidthComponent(Component.text().color(NamedTextColor.WHITE), 0) val EMPTY_PIXEL_COMPONENT get() = PixelComponent(EMPTY_WIDTH_COMPONENT, 0) -val NEW_LAYER - get() = if (BOOTSTRAP.useLegacyFont()) EMPTY_WIDTH_COMPONENT else WidthComponent(Component.text().content(0xC0000.parseChar()).font(SPACE_KEY), 0) + +const val LEGACY_CENTER_SPACE_CODEPOINT = 0xFFC00 +const val CURRENT_CENTER_SPACE_CODEPOINT = 0xD0000 private val LEGACY_NEGATIVE_ONE_SPACE_COMPONENT - get() = WidthComponent(Component.text().content((0xFFC00 - 1).parseChar()).font(LEGACY_SPACE_KEY), 0) + get() = WidthComponent(Component.text().content((LEGACY_CENTER_SPACE_CODEPOINT - 1).parseChar()).font(LEGACY_SPACE_KEY), 0) private val CURRENT_NEGATIVE_ONE_SPACE_COMPONENT - get() = WidthComponent(Component.text().content((0xD0000 - 1).parseChar()).font(SPACE_KEY), 0) + get() = WidthComponent(Component.text().content((CURRENT_CENTER_SPACE_CODEPOINT - 1).parseChar()).font(SPACE_KEY), 0) val NEGATIVE_ONE_SPACE_COMPONENT get() = if (BOOTSTRAP.useLegacyFont()) { LEGACY_NEGATIVE_ONE_SPACE_COMPONENT @@ -104,4 +110,100 @@ fun Int.toSpaceComponent(width: Int) = if (BOOTSTRAP.useLegacyFont()) { .content((this + 0xD0000).parseChar()), width ) +} + +private class SplitBuilder( + private val chain: (TextComponent.Builder) -> Unit +) { + private var builder = Component.text() + var isClean = true + private set + fun accept(block: TextComponent.Builder.() -> Unit = {}) { + val build = builder.apply(block) + builder = Component.text() + isClean = true + chain(build) + } + fun build(block: TextComponent.Builder.() -> Unit = {}): TextComponent.Builder { + val build = builder.apply(block) + builder = Component.text() + isClean = true + return build + } + fun append(like: ComponentLike) { + builder.append(like) + isClean = false + } + fun then(firstChain: (SplitBuilder, TextComponent.Builder) -> Unit) = SplitBuilder other@ { + firstChain(this, it) + accept() + } +} + +private fun Style.parseDecoration(decoration: TextDecoration, default: Boolean): Boolean = when (decoration(decoration)) { + TextDecoration.State.NOT_SET -> default + TextDecoration.State.FALSE -> false + TextDecoration.State.TRUE -> true +} + +fun Component.split(endWidth: Int, space: Int, charWidth: (Pair) -> Int?): List { + var i = 0 + val list = ArrayList() + val topBuilder = SplitBuilder { + list.add(WidthComponent(it, i)) + i = 0 + } + fun Component.parse(target: SplitBuilder, bold: Boolean, italic: Boolean) { + if (this is TextComponent) { + var style = style() + var add = 0 + val subBold = style.parseDecoration(TextDecoration.BOLD, bold) + val subItalic = style.parseDecoration(TextDecoration.ITALIC, italic) + style = style.decorations(mapOf( + TextDecoration.BOLD to if (subBold) TextDecoration.State.TRUE else TextDecoration.State.FALSE, + TextDecoration.ITALIC to if (subItalic) TextDecoration.State.TRUE else TextDecoration.State.FALSE + )) + if (subBold) add++ + if (subItalic) add++ + val subBuilder = target.then { b, c -> + b.append(c) + } + val sb = StringBuilder() + + fun end() { + subBuilder.accept { + style(style).content(sb.toString()) + } + sb.setLength(0) + } + for (codepoint in content().codePoints()) { + if ('\n'.code == codepoint) { + end() + continue + } + sb.appendCodePoint(codepoint) + i += if (codepoint == ' '.code) 4 else charWidth(style to codepoint) ?: continue + i += add + if (space > 0) { + if (BOOTSTRAP.useLegacyFont()) subBuilder.append(space.toSpaceComponent().component) + else sb.appendCodePoint(TEXT_SPACE_KEY_CODEPOINT) + i += space + add + } + if (i >= endWidth && (i >= (1.25 * endWidth).roundToInt() || ' '.code == codepoint)) end() + } + if (!subBuilder.isClean || sb.isNotEmpty()) target.append(subBuilder.build { + style(style).content(sb.toString()) + }) + for (child in children()) { + child.parse(subBuilder, subBold, subItalic) + } + if (!subBuilder.isClean) target.append(subBuilder.build { + style(style) + }) + } + } + val style = style() + parse(topBuilder, style.parseDecoration(TextDecoration.BOLD, false), style.parseDecoration(TextDecoration.ITALIC, false)) + if (!topBuilder.isClean) list.add(WidthComponent(topBuilder.build(), i)) + return list } \ No newline at end of file diff --git a/dist/src/main/kotlin/kr/toxicity/hud/util/Files.kt b/dist/src/main/kotlin/kr/toxicity/hud/util/Files.kt index 611fca0e..a1cadb00 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/util/Files.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/util/Files.kt @@ -14,6 +14,9 @@ fun File.subFile(name: String) = File(this, name).apply { fun File.ifNotExist(message: String) = apply { if (!exists()) throw RuntimeException(message) } +fun File.ifNotExist(messageCreator: File.() -> String) = apply { + if (!exists()) throw RuntimeException(messageCreator()) +} fun File.forEach(block: (File) -> Unit) { listFiles()?.sortedBy { diff --git a/dist/src/main/kotlin/kr/toxicity/hud/util/Functions.kt b/dist/src/main/kotlin/kr/toxicity/hud/util/Functions.kt index e5d849c3..98b51d80 100644 --- a/dist/src/main/kotlin/kr/toxicity/hud/util/Functions.kt +++ b/dist/src/main/kotlin/kr/toxicity/hud/util/Functions.kt @@ -1,7 +1,7 @@ package kr.toxicity.hud.util import kr.toxicity.hud.equation.TEquation -import kr.toxicity.hud.layout.LayoutAlign +import kr.toxicity.hud.layout.enums.LayoutAlign import kr.toxicity.hud.manager.ConfigManagerImpl import net.kyori.adventure.audience.Audience diff --git a/dist/src/main/kotlin/kr/toxicity/hud/util/ValueViewer.kt b/dist/src/main/kotlin/kr/toxicity/hud/util/ValueViewer.kt new file mode 100644 index 00000000..d7031b44 --- /dev/null +++ b/dist/src/main/kotlin/kr/toxicity/hud/util/ValueViewer.kt @@ -0,0 +1,30 @@ +package kr.toxicity.hud.util + +import java.lang.ref.WeakReference + +class ValueViewer : (K) -> V? { + + private val refs = ArrayList<(K) -> V?>() + + fun addMap(vararg map: Map): ValueViewer { + val mapRef = map.map { + WeakReference(it) + } + for (weakReference in mapRef) { + refs.add { + weakReference.get()?.get(it) + } + } + return this + } + fun addFunction(vararg function: (K) -> V?): ValueViewer { + refs.addAll(function) + return this + } + + override fun invoke(p1: K): V? { + return if (refs.size == 1) refs[0](p1) else refs.firstNotNullOfOrNull { + it(p1) + } + } +} \ No newline at end of file