diff --git a/build.gradle.kts b/build.gradle.kts index bf7f2f2..8ce5b63 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,9 +6,10 @@ plugins { apply(plugin = "org.spongepowered.mixin") repositories { - maven("http://files.minecraftforge.net/maven") + maven("https://files.minecraftforge.net/maven") maven("https://repo.spongepowered.org/maven") maven("https://minecraft.curseforge.com/api/maven") + maven("https://www.cursemaven.com") } dependencies { @@ -16,6 +17,8 @@ dependencies { "implementation"("kottle:Kottle:${properties["kottleVersion"]}") "implementation"("org.spongepowered:mixin:0.8-SNAPSHOT") + + "api"(fg.deobf("curse.maven:clothconfig-348521:2813656")) } configurations["annotationProcessor"].extendsFrom(configurations["implementation"]) @@ -45,6 +48,7 @@ java { kotlin { target.compilations.configureEach { kotlinOptions.freeCompilerArgs += listOf("-Xno-param-assertions", "-Xno-call-assertions") + kotlinOptions.jvmTarget = "1.8" } } diff --git a/src/main/kotlin/mods/betterfoliage/BetterFoliageMod.kt b/src/main/kotlin/mods/betterfoliage/BetterFoliageMod.kt index 156c707..db57a32 100644 --- a/src/main/kotlin/mods/betterfoliage/BetterFoliageMod.kt +++ b/src/main/kotlin/mods/betterfoliage/BetterFoliageMod.kt @@ -1,33 +1,47 @@ package mods.betterfoliage +import me.shedaniel.forge.clothconfig2.api.ConfigBuilder import mods.betterfoliage.client.Client import mods.betterfoliage.client.config.BlockConfig -import mods.betterfoliage.client.config.Config -import mods.octarinecore.client.resource.AsnycSpriteProviderManager -import mods.octarinecore.client.resource.GeneratedBlockTexturePack +import mods.betterfoliage.client.config.MainConfig +import mods.octarinecore.common.config.clothGuiRoot +import mods.octarinecore.common.config.forgeSpecRoot import net.alexwells.kottle.FMLKotlinModLoadingContext import net.minecraft.client.Minecraft -import net.minecraft.client.particle.ParticleManager -import net.minecraft.client.renderer.model.ModelBakery +import net.minecraft.client.gui.screen.Screen +import net.minecraft.util.ResourceLocation +import net.minecraftforge.fml.ExtensionPoint.CONFIGGUIFACTORY import net.minecraftforge.fml.ModLoadingContext import net.minecraftforge.fml.common.Mod import net.minecraftforge.fml.config.ModConfig -import org.apache.logging.log4j.Level.DEBUG -import org.apache.logging.log4j.LogManager -import org.apache.logging.log4j.simple.SimpleLogger -import org.apache.logging.log4j.util.PropertiesUtil -import java.io.File -import java.io.PrintStream -import java.util.* +import org.apache.commons.lang3.tuple.Pair +import java.util.function.BiFunction +import java.util.function.BiPredicate +import java.util.function.Supplier @Mod(BetterFoliageMod.MOD_ID) object BetterFoliageMod { const val MOD_ID = "betterfoliage" val bus = FMLKotlinModLoadingContext.get().modEventBus + val config = MainConfig() init { - ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, Config.build()) + val ctx = ModLoadingContext.get() + + // Config instance + GUI handler + val configSpec = config.forgeSpecRoot() + ctx.registerConfig(ModConfig.Type.CLIENT, configSpec) + ctx.registerExtensionPoint(CONFIGGUIFACTORY) { BiFunction { client, parent -> + config.clothGuiRoot( + parentScreen = parent, + prefix = listOf(MOD_ID), + background = ResourceLocation("minecraft:textures/block/spruce_wood.png"), + saveAction = { configSpec.save() } + ) + } } + + Minecraft.getInstance().resourcePackList.addPackFinder(BetterFoliage.asyncPack.finder) bus.register(BlockConfig) Client.init() diff --git a/src/main/kotlin/mods/betterfoliage/client/Client.kt b/src/main/kotlin/mods/betterfoliage/client/Client.kt index b68deb2..7201dff 100644 --- a/src/main/kotlin/mods/betterfoliage/client/Client.kt +++ b/src/main/kotlin/mods/betterfoliage/client/Client.kt @@ -1,30 +1,38 @@ package mods.betterfoliage.client -import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.client.chunk.ChunkOverlayManager import mods.betterfoliage.client.config.BlockConfig -import mods.betterfoliage.client.integration.* -import mods.betterfoliage.client.render.* +import mods.betterfoliage.client.integration.OptifineCustomColors +import mods.betterfoliage.client.integration.ShadersModIntegration +import mods.betterfoliage.client.render.AsyncCactusDiscovery +import mods.betterfoliage.client.render.AsyncLogDiscovery +import mods.betterfoliage.client.render.LeafWindTracker +import mods.betterfoliage.client.render.RenderAlgae +import mods.betterfoliage.client.render.RenderCactus +import mods.betterfoliage.client.render.RenderConnectedGrass +import mods.betterfoliage.client.render.RenderConnectedGrassLog +import mods.betterfoliage.client.render.RenderCoral +import mods.betterfoliage.client.render.RenderGrass +import mods.betterfoliage.client.render.RenderLeaves +import mods.betterfoliage.client.render.RenderLilypad +import mods.betterfoliage.client.render.RenderLog +import mods.betterfoliage.client.render.RenderMycelium +import mods.betterfoliage.client.render.RenderNetherrack +import mods.betterfoliage.client.render.RenderReeds +import mods.betterfoliage.client.render.RisingSoulTextures import mods.betterfoliage.client.texture.AsyncGrassDiscovery import mods.betterfoliage.client.texture.AsyncLeafDiscovery import mods.betterfoliage.client.texture.LeafParticleRegistry -import mods.octarinecore.client.gui.textComponent import mods.octarinecore.client.render.RenderDecorator import mods.octarinecore.client.resource.IConfigChangeListener import net.minecraft.block.BlockState -import net.minecraft.client.Minecraft -import net.minecraft.util.math.BlockPos -import net.minecraft.util.text.TextFormatting -import net.minecraft.util.text.TranslationTextComponent -import net.minecraftforge.registries.ForgeRegistries -import org.apache.logging.log4j.Level /** * Object responsible for initializing (and holding a reference to) all the infrastructure of the mod * except for the call hooks. */ object Client { - var renderers= emptyList() + var renderers = emptyList() var configListeners = emptyList() val suppressRenderErrors = mutableSetOf() diff --git a/src/main/kotlin/mods/betterfoliage/client/config/Config.kt b/src/main/kotlin/mods/betterfoliage/client/config/Config.kt index 8c9becc..16c3db4 100644 --- a/src/main/kotlin/mods/betterfoliage/client/config/Config.kt +++ b/src/main/kotlin/mods/betterfoliage/client/config/Config.kt @@ -8,139 +8,6 @@ import net.minecraft.util.ResourceLocation import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.fml.config.ModConfig -private fun featureEnable() = boolean(true).lang("enabled") - -// Config singleton -object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.MOD_ID) { - - val enabled by boolean(true) - val nVidia by boolean(false) - - object leaves : ConfigCategory() { - val enabled by featureEnable() - val snowEnabled by boolean(true) - val hOffset by double(max=0.4, default=0.2).lang("hOffset") - val vOffset by double(max=0.4, default=0.1).lang("vOffset") - val size by double(min=0.75, max=2.5, default=1.4).lang("size") - val dense by boolean(false) - val hideInternal by boolean(true) - } - - object shortGrass : ConfigCategory(){ - val grassEnabled by boolean(true) - val myceliumEnabled by boolean(true) - val snowEnabled by boolean(true) - val hOffset by double(max=0.4, default=0.2).lang("hOffset") - val heightMin by double(min=0.1, max=2.5, default=0.6).lang("heightMin") - val heightMax by double(min=0.1, max=2.5, default=0.8).lang("heightMax") - val size by double(min=0.5, max=1.5, default=1.0).lang("size") - val population by int(max=64, default=64).lang("population") - val useGenerated by boolean(false) - val shaderWind by boolean(true).lang("shaderWind") - val saturationThreshold by double(default=0.1) - } - - object connectedGrass : ConfigCategory(){ - val enabled by boolean(true) - val snowEnabled by boolean(false) - } - - object roundLogs : ConfigCategory(){ - val enabled by featureEnable() - val radiusSmall by double(max=0.5, default=0.25) - val radiusLarge by double(max=0.5, default=0.44) - val dimming by double(default = 0.7) - val connectSolids by boolean(false) - val lenientConnect by boolean(true) - val connectPerpendicular by boolean(true) - val connectGrass by boolean(true) - val defaultY by boolean(false) - val zProtection by double(min = 0.9, default = 0.99) - } - - object cactus : ConfigCategory(){ - val enabled by featureEnable() - val size by double(min=0.5, max=1.5, default=0.8).lang("size") - val sizeVariation by double(max=0.5, default=0.1) - val hOffset by double(max=0.5, default=0.1).lang("hOffset") - } - - object lilypad : ConfigCategory(){ - val enabled by featureEnable() - val hOffset by double(max=0.25, default=0.1).lang("hOffset") - val flowerChance by int(max=64, default=16, min=0) - } - - object reed : ConfigCategory(){ - val enabled by featureEnable() - val hOffset by double(max=0.4, default=0.2).lang("hOffset") - val heightMin by double(min=1.5, max=3.5, default=1.7).lang("heightMin") - val heightMax by double(min=1.5, max=3.5, default=2.2).lang("heightMax") - val population by int(max=64, default=32).lang("population") - val minBiomeTemp by double(default=0.4) - val minBiomeRainfall by double(default=0.4) -// val biomes by biomeList { it.filterTemp(0.4f, null) && it.filterRain(0.4f, null) } - val shaderWind by boolean(true).lang("shaderWind") - } - - object algae : ConfigCategory(){ - val enabled by featureEnable() - val hOffset by double(max=0.25, default=0.1).lang("hOffset") - val size by double(min=0.5, max=1.5, default=1.0).lang("size") - val heightMin by double(min=0.1, max=1.5, default=0.5).lang("heightMin") - val heightMax by double(min=0.1, max=1.5, default=1.0).lang("heightMax") - val population by int(max=64, default=48).lang("population") -// val biomes by biomeList { it.filterClass("river", "ocean") } - val shaderWind by boolean(true).lang("shaderWind") - } - - object coral : ConfigCategory(){ - val enabled by featureEnable() - val shallowWater by boolean(false) - val hOffset by double(max=0.4, default=0.2).lang("hOffset") - val vOffset by double(max=0.4, default=0.1).lang("vOffset") - val size by double(min=0.5, max=1.5, default=0.7).lang("size") - val crustSize by double(min=0.5, max=1.5, default=1.4) - val chance by int(max=64, default=32) - val population by int(max=64, default=48).lang("population") -// val biomes by biomeList { it.filterClass("river", "ocean", "beach") } - } - - object netherrack : ConfigCategory(){ - val enabled by featureEnable() - val hOffset by double(max=0.4, default=0.2).lang("hOffset") - val heightMin by double(min=0.1, max=1.5, default=0.6).lang("heightMin") - val heightMax by double(min=0.1, max=1.5, default=0.8).lang("heightMax") - val size by double(min=0.5, max=1.5, default=1.0).lang("size") - } - - object fallingLeaves : ConfigCategory(){ - val enabled by featureEnable() - val speed by double(min=0.01, max=0.15, default=0.05) - val windStrength by double(min=0.1, max=2.0, default=0.5) - val stormStrength by double(min=0.1, max=2.0, default=0.8) - val size by double(min=0.25, max=1.5, default=0.75).lang("size") - val chance by double(min=0.001, max=1.0, default=0.02) - val perturb by double(min=0.01, max=1.0, default=0.25) - val lifetime by double(min=1.0, max=15.0, default=5.0) - val opacityHack by boolean(true) - } - - object risingSoul : ConfigCategory(){ - val enabled by featureEnable() - val chance by double(min=0.001, max=1.0, default=0.02) - val perturb by double(min=0.01, max=0.25, default=0.05) - val headSize by double(min=0.25, max=1.5, default=1.0) - val trailSize by double(min=0.25, max=1.5, default=0.75) - val opacity by double(min=0.05, max=1.0, default=0.5) - val sizeDecay by double(min=0.5, max=1.0, default=0.97) - val opacityDecay by double(min=0.5, max=1.0, default=0.97) - val lifetime by double(min=1.0, max=15.0, default=4.0) - val trailLength by int(min=2, max=128, default=48) - val trailDensity by int(min=1, max=16, default=3) - } -} - object BlockConfig { private val list = mutableListOf() diff --git a/src/main/kotlin/mods/betterfoliage/client/config/MainConfig.kt b/src/main/kotlin/mods/betterfoliage/client/config/MainConfig.kt new file mode 100644 index 0000000..d2e16b7 --- /dev/null +++ b/src/main/kotlin/mods/betterfoliage/client/config/MainConfig.kt @@ -0,0 +1,152 @@ +package mods.betterfoliage.client.config + +import mods.betterfoliage.BetterFoliageMod +import mods.octarinecore.common.config.DelegatingConfigGroup +import mods.octarinecore.common.config.boolean +import mods.octarinecore.common.config.double +import mods.octarinecore.common.config.integer +import mods.octarinecore.common.config.recurring +import mods.octarinecore.common.config.subNode + +fun featureEnable(default: Boolean = true) = boolean(default, langKey = recurring) + +val Config get() = BetterFoliageMod.config + +class MainConfig : DelegatingConfigGroup() { + val enabled by boolean(true, langKey = { "betterfoliage.global.enabled" }) + val nVidia by boolean(false) + + val leaves by subNode(::LeavesConfig) + val shortGrass by subNode(::ShortGrassConfig) + val connectedGrass by subNode(::ConnectedGrassConfig) + val roundLogs by subNode(::RoundLogConfig) + val cactus by subNode(::CactusConfig) + val lilypad by subNode(::LilypadConfig) + val reed by subNode(::ReedConfig) + val algae by subNode(::AlgaeConfig) + val coral by subNode(::CoralConfig) + val netherrack by subNode(::NetherrackConfig) + val fallingLeaves by subNode(::FallingLeavesConfig) + val risingSoul by subNode(::RisingSoulConfig) +} + +class LeavesConfig : DelegatingConfigGroup() { + val enabled by featureEnable() + val snowEnabled by boolean(true) + val hOffset by double(max=0.4, default=0.2, langKey = recurring) + val vOffset by double(max=0.4, default=0.1, langKey = recurring) + val size by double(min=0.75, max=2.5, default=1.4, langKey = recurring) + val dense by boolean(false) + val hideInternal by boolean(true) +} + +class ShortGrassConfig : DelegatingConfigGroup() { + val grassEnabled by boolean(true) + val myceliumEnabled by boolean(true) + val snowEnabled by boolean(true) + val hOffset by double(max=0.4, default=0.2, langKey = recurring) + val heightMin by double(min=0.1, max=2.5, default=0.6, langKey = recurring) + val heightMax by double(min=0.1, max=2.5, default=0.8, langKey = recurring) + val size by double(min=0.5, max=1.5, default=1.0, langKey = recurring) + val population by integer(max=64, default=64) + val useGenerated by boolean(false) + val shaderWind by boolean(true, langKey = recurring) + val saturationThreshold by double(default=0.1) +} + +class ConnectedGrassConfig() : DelegatingConfigGroup() { + val enabled by boolean(true) + val snowEnabled by boolean(false) +} + +class RoundLogConfig() : DelegatingConfigGroup() { + val enabled by featureEnable() + val radiusSmall by double(max=0.5, default=0.25) + val radiusLarge by double(max=0.5, default=0.44) + val dimming by double(default = 0.7) + val connectSolids by boolean(false) + val lenientConnect by boolean(true) + val connectPerpendicular by boolean(true) + val connectGrass by boolean(true) + val defaultY by boolean(false) + val zProtection by double(min = 0.9, default = 0.99) +} + +class CactusConfig() : DelegatingConfigGroup() { + val enabled by featureEnable() + val size by double(min=0.5, max=1.5, default=0.8, langKey = recurring) + val sizeVariation by double(max=0.5, default=0.1) + val hOffset by double(max=0.5, default=0.1, langKey = recurring) +} + +class LilypadConfig() : DelegatingConfigGroup() { + val enabled by featureEnable() + val hOffset by double(max=0.25, default=0.1, langKey = recurring) + val flowerChance by integer(max=64, default=16, min=0) +} + +class ReedConfig() : DelegatingConfigGroup() { + val enabled by featureEnable() + val hOffset by double(max=0.4, default=0.2, langKey = recurring) + val heightMin by double(min=1.5, max=3.5, default=1.7, langKey = recurring) + val heightMax by double(min=1.5, max=3.5, default=2.2, langKey = recurring) + val population by integer(max=64, default=32, langKey = recurring) + val minBiomeTemp by double(default=0.4) + val minBiomeRainfall by double(default=0.4) + val shaderWind by boolean(true, langKey = recurring) +} + +class AlgaeConfig() : DelegatingConfigGroup() { + val enabled by featureEnable() + val hOffset by double(max=0.25, default=0.1, langKey = recurring) + val size by double(min=0.5, max=1.5, default=1.0, langKey = recurring) + val heightMin by double(min=0.1, max=1.5, default=0.5, langKey = recurring) + val heightMax by double(min=0.1, max=1.5, default=1.0, langKey = recurring) + val population by integer(max=64, default=48, langKey = recurring) + val shaderWind by boolean(true, langKey = recurring) +} + +class CoralConfig() : DelegatingConfigGroup() { + val enabled by featureEnable() + val shallowWater by boolean(false) + val hOffset by double(max=0.4, default=0.2, langKey = recurring) + val vOffset by double(max=0.4, default=0.1, langKey = recurring) + val size by double(min=0.5, max=1.5, default=0.7, langKey = recurring) + val crustSize by double(min=0.5, max=1.5, default=1.4) + val chance by integer(max=64, default=32) + val population by integer(max=64, default=48, langKey = recurring) +} + +class NetherrackConfig() : DelegatingConfigGroup() { + val enabled by featureEnable() + val hOffset by double(max=0.4, default=0.2, langKey = recurring) + val heightMin by double(min=0.1, max=1.5, default=0.6, langKey = recurring) + val heightMax by double(min=0.1, max=1.5, default=0.8, langKey = recurring) + val size by double(min=0.5, max=1.5, default=1.0, langKey = recurring) +} + +class FallingLeavesConfig() : DelegatingConfigGroup() { + val enabled by featureEnable() + val speed by double(min=0.01, max=0.15, default=0.05) + val windStrength by double(min=0.1, max=2.0, default=0.5) + val stormStrength by double(min=0.1, max=2.0, default=0.8) + val size by double(min=0.25, max=1.5, default=0.75, langKey = recurring) + val chance by double(min=0.001, max=1.0, default=0.02) + val perturb by double(min=0.01, max=1.0, default=0.25) + val lifetime by double(min=1.0, max=15.0, default=5.0) + val opacityHack by boolean(true) +} + +class RisingSoulConfig() : DelegatingConfigGroup() { + val enabled by featureEnable() + val chance by double(min=0.001, max=1.0, default=0.02) + val perturb by double(min=0.01, max=0.25, default=0.05) + val headSize by double(min=0.25, max=1.5, default=1.0) + val trailSize by double(min=0.25, max=1.5, default=0.75) + val opacity by double(min=0.05, max=1.0, default=0.5) + val sizeDecay by double(min=0.5, max=1.0, default=0.97) + val opacityDecay by double(min=0.5, max=1.0, default=0.97) + val lifetime by double(min=1.0, max=15.0, default=4.0) + val trailLength by integer(min=2, max=128, default=48) + val trailDensity by integer(min=1, max=16, default=3) +} \ No newline at end of file diff --git a/src/main/kotlin/mods/octarinecore/common/config/Delegate.kt b/src/main/kotlin/mods/octarinecore/common/config/Delegate.kt new file mode 100644 index 0000000..c0cd803 --- /dev/null +++ b/src/main/kotlin/mods/octarinecore/common/config/Delegate.kt @@ -0,0 +1,172 @@ +package mods.octarinecore.common.config + +import me.shedaniel.forge.clothconfig2.api.AbstractConfigListEntry +import me.shedaniel.forge.clothconfig2.api.ConfigBuilder +import me.shedaniel.forge.clothconfig2.api.ConfigEntryBuilder +import me.shedaniel.forge.clothconfig2.gui.entries.SubCategoryListEntry +import net.minecraft.client.gui.screen.Screen +import net.minecraft.client.resources.I18n +import net.minecraft.util.ResourceLocation +import net.minecraftforge.common.ForgeConfigSpec +import java.util.* +import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.KProperty + +const val MAX_LINE_LEN = 30 + +fun DelegatingConfigGroup.forgeSpecRoot() = + ForgeConfigSpec.Builder() + .also { createForgeNode(it) } + .build() + +fun DelegatingConfigGroup.clothGuiRoot( + parentScreen: Screen, + prefix: List, + background: ResourceLocation, + saveAction: ()->Unit +) = ConfigBuilder.create() + .setParentScreen(parentScreen) + .setTitle(I18n.format((prefix + "title").joinToString("."))) + .setDefaultBackgroundTexture(background) + .setSavingRunnable(saveAction) + .also { builder -> + createClothNode(prefix).value.forEach { rootCategory -> + builder.getOrCreateCategory("main").addEntry(rootCategory) + } + } + .build() + +sealed class DelegatingConfigNode { + abstract fun createClothNode(path: List): AbstractConfigListEntry<*> +} + +abstract class DelegatingConfigValue : DelegatingConfigNode(), ReadOnlyProperty { + lateinit var forgeValue: ForgeConfigSpec.ConfigValue + abstract fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String) +} + +open class DelegatingConfigGroup : DelegatingConfigNode() { + val children = mutableMapOf() + + fun createForgeNode(builder: ForgeConfigSpec.Builder) { + children.forEach { (name, node) -> + when(node) { + is DelegatingConfigGroup -> { + builder.push(name) + node.createForgeNode(builder) + builder.pop() + } + is DelegatingConfigValue<*> -> node.createForgeNode(builder, name) + } + } + } + + override fun createClothNode(path: List): SubCategoryListEntry { + val builder = ConfigEntryBuilder.create() + .startSubCategory(path.joinToString(".").translate()) + .setTooltip(*path.joinToString(".").translateTooltip()) + .setExpended(false) + children.forEach { (name, node) -> builder.add(node.createClothNode(path + name)) } + return builder.build() + } +} + +interface DelegatingConfigGroupFactory { + operator fun provideDelegate(parent: DelegatingConfigGroup, property: KProperty<*>): ReadOnlyProperty +} + +fun subNode(factory: ()->T) = object : DelegatingConfigGroupFactory { + override operator fun provideDelegate(parent: DelegatingConfigGroup, property: KProperty<*>): ReadOnlyProperty { + val child = factory() + parent.children[property.name] = child + return object : ReadOnlyProperty { + override fun getValue(thisRef: DelegatingConfigGroup, property: KProperty<*>) = child + } + } +} + +interface DelegatingConfigValueFactory { + fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String): ForgeConfigSpec.ConfigValue + fun createClothNode(prop: CachingConfigProperty, path: List): AbstractConfigListEntry + + operator fun provideDelegate(parent: DelegatingConfigGroup, property: KProperty<*>): ReadOnlyProperty { + return object : CachingConfigProperty(parent, property) { + override fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String) { + forgeValue = this@DelegatingConfigValueFactory.createForgeNode(builder, name) + } + + override fun createClothNode(path: List): AbstractConfigListEntry<*> = createClothNode(this, path) + + }.apply { parent.children[property.name] = this } + } +} + +abstract class CachingConfigProperty(parent: DelegatingConfigGroup, property: KProperty<*>) : DelegatingConfigValue() { + var value: T? = null + override fun getValue(thisRef: DelegatingConfigGroup, property: KProperty<*>) = + value ?: forgeValue.get().apply { value = this } +} + +fun String.translate() = I18n.format(this) +fun String.translateTooltip(lineLength: Int = MAX_LINE_LEN) = ("$this.tooltip").translate().let { tooltip -> + tooltip.splitToSequence(" ").fold(mutableListOf("")) { tooltips, word -> + if (tooltips.last().length + word.length < lineLength) { + tooltips[tooltips.lastIndex] += "$word " + } else { + tooltips.add("$word ") + } + tooltips + }.map { it.trim() }.toTypedArray() +} + +fun boolean( + default: Boolean, + langKey: (List)->String = { it.joinToString(".") }, + valueOverride: (Boolean)->Boolean = { it } +) = object : DelegatingConfigValueFactory { + override fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String) = + builder.define(name, default) + + override fun createClothNode(prop: CachingConfigProperty, path: List) = ConfigEntryBuilder.create() + .startBooleanToggle(langKey(path).translate(), prop.forgeValue.get()) + .setTooltip(langKey(path).let { if (I18n.hasKey("$it.tooltip")) Optional.of(it.translateTooltip()) else Optional.empty() }) + .setSaveConsumer { prop.forgeValue.set(valueOverride(it)); prop.value = null } + .build() +} + +fun integer( + default: Int = 0, min: Int = 0, max: Int, + langKey: (List)->String = { it.joinToString(".") }, + valueOverride: (Int)->Int = { it } +) = object : DelegatingConfigValueFactory { + override fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String) = + builder.defineInRange(name, default, min, max) + + override fun createClothNode(prop: CachingConfigProperty, path: List) = ConfigEntryBuilder.create() + .startIntField(langKey(path).translate(), prop.forgeValue.get()) + .setTooltip(langKey(path).let { if (I18n.hasKey("$it.tooltip")) Optional.of(it.translateTooltip()) else Optional.empty() }) + .setMin(min).setMax(max) + .setSaveConsumer { prop.forgeValue.set(valueOverride(it)); prop.value = null } + .build() +} + +fun double( + default: Double = 0.0, min: Double = 0.0, max: Double = 1.0, + langKey: (List)->String = { it.joinToString(".") }, + valueOverride: (Double)->Double = { it } +) = object : DelegatingConfigValueFactory { + override fun createForgeNode(builder: ForgeConfigSpec.Builder, name: String) = + builder.defineInRange(name, default, min, max) + + override fun createClothNode(prop: CachingConfigProperty, path: List) = ConfigEntryBuilder.create() + .startDoubleField(langKey(path).translate(), prop.forgeValue.get()) + .setTooltip(langKey(path).let { if (I18n.hasKey("$it.tooltip")) Optional.of(it.translateTooltip()) else Optional.empty() }) + .setMin(min).setMax(max) + .setSaveConsumer { prop.forgeValue.set(valueOverride(it)); prop.value = null } + .build() +} + +val recurring = { path: List -> "${path.first()}.${path.last()}" } +fun fakeCategory(name: String) = { names: List -> + (listOf(names.first(), name) + names.drop(1)).joinToString(".") +} \ No newline at end of file diff --git a/src/main/kotlin/mods/octarinecore/common/config/DelegatingConfig.kt b/src/main/kotlin/mods/octarinecore/common/config/DelegatingConfig.kt deleted file mode 100644 index 75e3cfb..0000000 --- a/src/main/kotlin/mods/octarinecore/common/config/DelegatingConfig.kt +++ /dev/null @@ -1,89 +0,0 @@ -@file:JvmName("DelegatingConfigKt") - -package mods.octarinecore.common.config - -import mods.octarinecore.metaprog.reflectDelegates -import mods.octarinecore.metaprog.reflectNestedObjects -import net.minecraftforge.common.ForgeConfigSpec -import kotlin.properties.ReadOnlyProperty -import kotlin.reflect.KProperty - -open class DelegatingConfig(val modId: String, val langPrefix: String) { - fun build() = ForgeConfigSpec.Builder().apply { ConfigBuildContext(langPrefix, emptyList(), this).addCategory(this@DelegatingConfig) }.build() -} - -class ConfigBuildContext(val langPrefix: String, val path: List, val builder: ForgeConfigSpec.Builder) { - - fun addCategory(configObj: Any) { - configObj.reflectNestedObjects.forEach { (name, category) -> - builder.push(name) - descend(name).addCategory(category) - builder.pop() - } - configObj.reflectDelegates(ConfigDelegate::class.java).forEach { (name, delegate) -> - descend(name).apply { delegate.addToBuilder(this) } - } - } - - fun descend(pathName: String) = ConfigBuildContext(langPrefix, path + pathName, builder) -} - -open class ConfigCategory(val comment: String? = null) { -} - -abstract class ConfigDelegate : ReadOnlyProperty { - lateinit var configValue: ForgeConfigSpec.ConfigValue - var cachedValue: T? = null - - override fun getValue(thisRef: Any, property: KProperty<*>): T { - if (cachedValue == null) cachedValue = configValue.get() - return cachedValue!! - } - - abstract fun getConfigValue(name: String, builder: ForgeConfigSpec.Builder): ForgeConfigSpec.ConfigValue - fun addToBuilder(ctx: ConfigBuildContext) { - val langKey = ctx.langPrefix + "." + (langPrefixOverride ?: ctx.path.joinToString(".")) - ctx.builder.translation(langKey) - configValue = getConfigValue(ctx.path.last(), ctx.builder) - } - - var langPrefixOverride: String? = null - fun lang(prefix: String) = apply { langPrefixOverride = prefix } - -} - -class DelegatingBooleanValue(val defaultValue: Boolean) : ConfigDelegate() { - override fun getConfigValue(name: String, builder: ForgeConfigSpec.Builder) = builder.define(name, defaultValue) -} - -class DelegatingIntValue( - val minValue: Int = 0, - val maxValue: Int = 1, - val defaultValue: Int = 0 -) : ConfigDelegate() { - override fun getConfigValue(name: String, builder: ForgeConfigSpec.Builder) = builder.defineInRange(name, defaultValue, minValue, maxValue) -} - -class DelegatingLongValue( - val minValue: Long = 0, - val maxValue: Long = 1, - val defaultValue: Long = 0 -) : ConfigDelegate() { - override fun getConfigValue(name: String, builder: ForgeConfigSpec.Builder) = builder.defineInRange(name, defaultValue, minValue, maxValue) -} - -class DelegatingDoubleValue( - val minValue: Double = 0.0, - val maxValue: Double = 1.0, - val defaultValue: Double = 0.0 -) : ConfigDelegate() { - override fun getConfigValue(name: String, builder: ForgeConfigSpec.Builder) = builder.defineInRange(name, defaultValue, minValue, maxValue) -} - -// ============================ -// Delegate factory methods -// ============================ -fun double(min: Double = 0.0, max: Double = 1.0, default: Double) = DelegatingDoubleValue(min, max, default) -fun int(min: Int = 0, max: Int, default: Int) = DelegatingIntValue(min, max, default) -fun long(min: Long = 0, max: Long, default: Long) = DelegatingLongValue(min, max, default) -fun boolean(default: Boolean) = DelegatingBooleanValue(default)