Compare commits

...

6 Commits

15 changed files with 465 additions and 97 deletions

View File

@ -1,7 +1,6 @@
package ru.dbotthepony.mc.otm.datagen package ru.dbotthepony.mc.otm.datagen
import net.minecraft.core.HolderSet import net.minecraft.core.HolderSet
import net.minecraft.core.RegistrySetBuilder
import net.minecraft.core.registries.Registries import net.minecraft.core.registries.Registries
import net.minecraft.data.worldgen.BootstrapContext import net.minecraft.data.worldgen.BootstrapContext
import net.minecraft.resources.ResourceKey import net.minecraft.resources.ResourceKey
@ -22,7 +21,7 @@ import net.neoforged.neoforge.registries.NeoForgeRegistries
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.registry.MBlocks import ru.dbotthepony.mc.otm.registry.MBlocks
import ru.dbotthepony.mc.otm.registry.MWorldGenFeatures import ru.dbotthepony.mc.otm.registry.MWorldGenFeatures
import ru.dbotthepony.mc.otm.worldgen.feature.BlackHolePlacerConfiguration import ru.dbotthepony.mc.otm.worldgen.feature.BlackHolePlacerFeature
private object ConfiguredFeatures { private object ConfiguredFeatures {
val TRITANIUM_ORE = key("tritanium_ore") val TRITANIUM_ORE = key("tritanium_ore")
@ -44,7 +43,7 @@ fun registerConfiguredFeatures(context: BootstrapContext<ConfiguredFeature<*, *>
context.register(ConfiguredFeatures.TRITANIUM_ORE, ConfiguredFeature(Feature.ORE, OreConfiguration(target, 9))) context.register(ConfiguredFeatures.TRITANIUM_ORE, ConfiguredFeature(Feature.ORE, OreConfiguration(target, 9)))
context.register(ConfiguredFeatures.BLACK_HOLE, ConfiguredFeature(MWorldGenFeatures.BLACK_HOLE_PLACER, context.register(ConfiguredFeatures.BLACK_HOLE, ConfiguredFeature(MWorldGenFeatures.BLACK_HOLE_PLACER,
BlackHolePlacerConfiguration(0.001f, Decimal(125_000), Decimal(500_000)))) BlackHolePlacerFeature.Config(0.001f, Decimal("0.25"), Decimal(1))))
} }
private object PlacedFeatures { private object PlacedFeatures {

View File

@ -11,7 +11,6 @@ import net.neoforged.fml.ModList;
import net.neoforged.fml.common.Mod; import net.neoforged.fml.common.Mod;
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent;
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
import net.neoforged.fml.loading.FMLLoader;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import ru.dbotthepony.mc.otm.android.AndroidResearchDescription; import ru.dbotthepony.mc.otm.android.AndroidResearchDescription;
@ -152,7 +151,7 @@ public final class OverdriveThatMatters {
bus.addListener(EventPriority.HIGHEST, this::setup); bus.addListener(EventPriority.HIGHEST, this::setup);
bus.addListener(EventPriority.NORMAL, this::setupClient); bus.addListener(EventPriority.NORMAL, this::setupClient);
if (FMLLoader.getDist() == Dist.CLIENT) { if (dist == Dist.CLIENT) {
bus.addListener(EventPriority.NORMAL, AndroidMenuKeyMapping.INSTANCE::register); bus.addListener(EventPriority.NORMAL, AndroidMenuKeyMapping.INSTANCE::register);
bus.addListener(EventPriority.NORMAL, AndroidAbilityKeyMapping.INSTANCE::register); bus.addListener(EventPriority.NORMAL, AndroidAbilityKeyMapping.INSTANCE::register);
bus.addListener(EventPriority.NORMAL, TritaniumArmorModel::register); bus.addListener(EventPriority.NORMAL, TritaniumArmorModel::register);

View File

@ -21,6 +21,7 @@ import org.lwjgl.opengl.GL11.GL_LESS
import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.absoluteValue
import kotlin.math.acos import kotlin.math.acos
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.pow import kotlin.math.pow
@ -287,10 +288,31 @@ fun renderChart(
val font = levelLabels.font ?: minecraft.font val font = levelLabels.font ?: minecraft.font
for ((level, label) in levelLabels.labels) { for ((level, label) in levelLabels.labels) {
var conflictingTop = false
var conflictingBottom = false
levelLabels.labels.keys.forEach {
if (it != level) {
conflictingTop = conflictingTop || it > level && (it - level).absoluteValue * height < font.lineHeight.toFloat()
conflictingBottom = conflictingBottom || it < level && (it - level).absoluteValue * height < font.lineHeight.toFloat()
}
}
val y0 = y + (1f - level) * height val y0 = y + (1f - level) * height
val tX = x + (levelLabels.textGravity.repositionX(width - 1f, font.width(label).toFloat() * textScale - 1f)) val tX = x + (levelLabels.textGravity.repositionX(width - 1f, font.width(label).toFloat() * textScale - 1f))
val tY = y0 - lineWidth - 1f - font.lineHeight * textScale + levelLabels.textGravity.repositionY(font.lineHeight * 2f * textScale + lineWidth + 2f, font.lineHeight.toFloat() * textScale) + 1f var tY = y0 + levelLabels.textGravity.repositionY(font.lineHeight * 2f * textScale + lineWidth + 2f, font.lineHeight.toFloat() * textScale)
if (conflictingTop && conflictingBottom) {
// do nothing, draw at center
tY -= 2f + lineWidth
} else if (conflictingTop) {
// draw at bottom
tY += lineWidth + 2f
} else {
// draw at top
tY -= font.lineHeight * textScale + lineWidth
}
font.draw( font.draw(
poseStack, poseStack,

View File

@ -14,13 +14,19 @@ import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.* import ru.dbotthepony.mc.otm.client.render.*
import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.core.RandomSource2Generator
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.asAngle import ru.dbotthepony.mc.otm.core.math.asAngle
import ru.dbotthepony.mc.otm.core.math.clusterize
import ru.dbotthepony.mc.otm.core.util.formatPower import ru.dbotthepony.mc.otm.core.util.formatPower
import ru.dbotthepony.mc.otm.core.math.times import ru.dbotthepony.mc.otm.core.math.times
import java.util.Random
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.absoluteValue
class EnergyCounterRenderer(private val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer<EnergyCounterBlockEntity> { class EnergyCounterRenderer(private val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer<EnergyCounterBlockEntity> {
private val random = Random()
override fun render( override fun render(
tile: EnergyCounterBlockEntity, tile: EnergyCounterBlockEntity,
p_112308_: Float, p_112308_: Float,
@ -80,7 +86,7 @@ class EnergyCounterRenderer(private val context: BlockEntityRendererProvider.Con
if (maximum.isZero || maximum.isInfinite) { if (maximum.isZero || maximum.isInfinite) {
normalized = FloatArray(chart.width) { normalized = FloatArray(chart.width) {
if (chart[it].isInfinite) 0.8f else 0.0f if (chart[it].isInfinite) 1f else 0.0f
} }
levelLabels = ChartLevelLabels( levelLabels = ChartLevelLabels(
@ -97,9 +103,25 @@ class EnergyCounterRenderer(private val context: BlockEntityRendererProvider.Con
val map = Float2ObjectArrayMap<Component>() val map = Float2ObjectArrayMap<Component>()
map[1f] = maximum.formatPower() map[1f] = maximum.formatPower()
map[0.75f] = (maximum * 0.75).formatPower()
map[0.5f] = (maximum * 0.5).formatPower() for (cluster in chart.asIterable().clusterize(random)) {
map[0.25f] = (maximum * 0.25).formatPower() val perc = (cluster.center / maximum).toFloat()
if (map.keys.none { (it - perc).absoluteValue < 0.08f }) {
map[perc] = cluster.center.formatPower()
}
}
/*
if (map.keys.none { (it - 0.75f).absoluteValue < 0.1f })
map[0.75f] = (maximum * 0.75).formatPower()
if (map.keys.none { (it - 0.5f).absoluteValue < 0.1f })
map[0.5f] = (maximum * 0.5).formatPower()
if (map.keys.none { (it - 0.25f).absoluteValue < 0.1f })
map[0.25f] = (maximum * 0.25).formatPower()
*/
levelLabels = ChartLevelLabels( levelLabels = ChartLevelLabels(
labels = map, labels = map,

View File

@ -8,11 +8,15 @@ import ru.dbotthepony.mc.otm.client.render.ChartMouseLabels
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
import ru.dbotthepony.mc.otm.client.render.renderChart import ru.dbotthepony.mc.otm.client.render.renderChart
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.core.RandomSource2Generator
import ru.dbotthepony.mc.otm.core.chart.DecimalHistoryChart import ru.dbotthepony.mc.otm.core.chart.DecimalHistoryChart
import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.clusterize
import ru.dbotthepony.mc.otm.core.util.formatPower
import ru.dbotthepony.mc.otm.core.util.formatTickDuration import ru.dbotthepony.mc.otm.core.util.formatTickDuration
import kotlin.math.absoluteValue
open class DecimalHistoryChartPanel<out S : MatteryScreen<*>>( open class DecimalHistoryChartPanel<out S : MatteryScreen<*>>(
screen: S, screen: S,
@ -31,25 +35,41 @@ open class DecimalHistoryChartPanel<out S : MatteryScreen<*>>(
if (maximum.isZero || maximum.isInfinite) { if (maximum.isZero || maximum.isInfinite) {
normalized = FloatArray(chart.width) { normalized = FloatArray(chart.width) {
if (chart[it].isInfinite) 0.8f else 0.0f if (chart[it].isInfinite) 1f else 0.0f
} }
levelLabels = ChartLevelLabels( levelLabels = ChartLevelLabels(
labels = mapOf( labels = mapOf(
0.8f to TextComponent(""), 1f to TextComponent(""),
// 0f to formatText(Decimal.ZERO), // 0f to formatText(Decimal.ZERO),
), ),
font = font font = font
) )
} else { } else {
normalized = FloatArray(chart.width) { (chart[it] / maximum).toFloat() * 0.9f } normalized = FloatArray(chart.width) { (chart[it] / maximum).toFloat() }
val map = Float2ObjectArrayMap<Component>() val map = Float2ObjectArrayMap<Component>()
map[0.9f] = formatText(maximum) map[1f] = formatText(maximum)
map[0.9f * 0.75f] = formatText(maximum * 0.75)
map[0.9f * 0.5f] = formatText(maximum * 0.5) for (cluster in chart.asIterable().clusterize(randomGenerator)) {
map[0.9f * 0.25f] = formatText(maximum * 0.25) val perc = (cluster.center / maximum).toFloat()
if (map.keys.none { (it - perc).absoluteValue < 0.08f }) {
map[perc] = cluster.center.formatPower()
}
}
/*
if (map.keys.none { (it - 0.75f).absoluteValue < 0.1f })
map[0.75f] = formatText(maximum * 0.75)
if (map.keys.none { (it - 0.5f).absoluteValue < 0.1f })
map[0.5f] = formatText(maximum * 0.5)
if (map.keys.none { (it - 0.25f).absoluteValue < 0.1f })
map[0.25f] = formatText(maximum * 0.25)
*/
levelLabels = ChartLevelLabels( levelLabels = ChartLevelLabels(
labels = map, labels = map,
@ -63,7 +83,8 @@ open class DecimalHistoryChartPanel<out S : MatteryScreen<*>>(
graphics.pose, graphics.pose,
listOf(normalized to RGBAColor.WHITE), listOf(normalized to RGBAColor.WHITE),
width, width,
height, height - font.lineHeight - 4f,
y = font.lineHeight.toFloat() + 4f,
labels = ChartMouseLabels( labels = ChartMouseLabels(
mouseX - absoluteX, mouseX - absoluteX,
mouseY - absoluteY, mouseY - absoluteY,

View File

@ -26,11 +26,13 @@ import ru.dbotthepony.mc.otm.client.render.popScissorRect
import ru.dbotthepony.mc.otm.client.render.pushScissorRect import ru.dbotthepony.mc.otm.client.render.pushScissorRect
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.input.QueryUserPanel import ru.dbotthepony.mc.otm.client.screen.panels.input.QueryUserPanel
import ru.dbotthepony.mc.otm.core.RandomSource2Generator
import ru.dbotthepony.mc.otm.core.collect.concatIterators import ru.dbotthepony.mc.otm.core.collect.concatIterators
import ru.dbotthepony.mc.otm.core.collect.flatMap import ru.dbotthepony.mc.otm.core.collect.flatMap
import java.util.* import java.util.*
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
import java.util.function.Predicate import java.util.function.Predicate
import java.util.random.RandomGenerator
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -144,6 +146,10 @@ open class EditablePanel<out S : Screen>(
} }
} }
val randomGenerator: RandomGenerator by lazy {
RandomSource2Generator(random)
}
/** /**
* Bigger values means lesser priority while docking, rendering and processing inputs. * Bigger values means lesser priority while docking, rendering and processing inputs.
*/ */

View File

@ -525,14 +525,14 @@ class AndroidStationScreen(p_97741_: AndroidStationMenu, p_97742_: Inventory, p_
if (isPreview && !layoutInvalidated) { if (isPreview && !layoutInvalidated) {
if (firstTick) { if (firstTick) {
scroller.init.invoke(this, RandomSource2Generator(random)) scroller.init.invoke(this, randomGenerator)
} }
val status = scroller.scroll.invoke(this, RandomSource2Generator(random)) val status = scroller.scroll.invoke(this, randomGenerator)
if (!status) { if (!status) {
scroller = PreviewScrollers.entries.let { it[random.nextInt(it.size)] } scroller = PreviewScrollers.entries.let { it[random.nextInt(it.size)] }
scroller.init.invoke(this, RandomSource2Generator(random)) scroller.init.invoke(this, randomGenerator)
} }
} }
} }

View File

@ -50,7 +50,6 @@ object ClientConfig : AbstractConfig("client", ModConfig.Type.CLIENT) {
var CHARTS_IN_TOOLTIPS: Boolean by builder var CHARTS_IN_TOOLTIPS: Boolean by builder
.comment("Draw charts in storage tooltips instead of list of last 20 values.") .comment("Draw charts in storage tooltips instead of list of last 20 values.")
.comment("Disable to get EnderIO-like experience")
.define("CHARTS_IN_TOOLTIPS", true) .define("CHARTS_IN_TOOLTIPS", true)
init { init {

View File

@ -1,10 +1,8 @@
package ru.dbotthepony.mc.otm.core.chart package ru.dbotthepony.mc.otm.core.chart
import com.google.common.collect.ImmutableList
import com.mojang.serialization.Codec import com.mojang.serialization.Codec
import net.minecraft.network.RegistryFriendlyByteBuf import net.minecraft.network.RegistryFriendlyByteBuf
import ru.dbotthepony.mc.otm.core.collect.reduce import ru.dbotthepony.mc.otm.core.collect.reduce
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.data.DecimalCodec import ru.dbotthepony.mc.otm.data.DecimalCodec
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
@ -33,28 +31,4 @@ class DecimalHistoryChart : AbstractHistoryChart<Decimal> {
get() = DecimalCodec get() = DecimalCodec
override val streamCodec: MatteryStreamCodec<RegistryFriendlyByteBuf, Decimal> override val streamCodec: MatteryStreamCodec<RegistryFriendlyByteBuf, Decimal>
get() = DecimalCodec.NETWORK get() = DecimalCodec.NETWORK
companion object {
private val HISTORY_WEIGHTERS: ImmutableList<Decimal> = immutableList {
/*for (i in 0 until 20) {
accept(Decimal(0.5.pow(i + 1)))
}*/
val average = Decimal(1) / Decimal(20)
for (i in 0 until 20) {
accept(average)
}
}
fun calcWeightedAverage(getter: (Int) -> Decimal): Decimal {
var result = Decimal.ZERO
for (i in 0 until 20) {
result += getter(i) * HISTORY_WEIGHTERS[i]
}
return result
}
}
} }

View File

@ -1,5 +1,8 @@
package ru.dbotthepony.mc.otm.core.chart package ru.dbotthepony.mc.otm.core.chart
import ru.dbotthepony.mc.otm.core.math.Cluster
import java.util.random.RandomGenerator
/** /**
* Common interface for reading chart values * Common interface for reading chart values
*/ */

View File

@ -0,0 +1,192 @@
package ru.dbotthepony.mc.otm.core.math
import it.unimi.dsi.fastutil.objects.ObjectArrayList
import ru.dbotthepony.mc.otm.core.random
import java.util.random.RandomGenerator
import kotlin.math.min
interface Cluster<V : Any> {
val values: List<V>
val center: V
data class Impl<V : Any>(override val values: List<V>, override val center: V) : Cluster<V>
}
private class ClusterValue<V : Comparable<V>>(val value: V, var cluster: MutableCluster<V>, var error: V) {
fun updateError(abs: (V) -> V, minus: (V, V) -> V) {
error = abs(minus(cluster.center, value))
}
fun maybeSwitchCluster(cluster: MutableCluster<V>, abs: (V) -> V, minus: (V, V) -> V): Boolean {
if (cluster == this.cluster) return false
val newError = abs(minus(cluster.center, value))
if (newError < error) {
error = newError
this.cluster.values.remove(this)
cluster.values.add(this)
cluster.generation++
this.cluster.generation++
this.cluster = cluster
return true
} else {
return false
}
}
}
private class MutableCluster<V : Comparable<V>>(var center: V) {
val values = ObjectArrayList<ClusterValue<V>>()
var generation = 0
var centerCalculatedAtGeneration = -1
fun calculateCenter(identity: V, plus: (V, V) -> V, minus: (V, V) -> V, divInt: (V, Int) -> V, abs: (V) -> V): Boolean {
if (values.isEmpty) return false
if (centerCalculatedAtGeneration == generation) return true
centerCalculatedAtGeneration = generation
var value = identity
values.forEach { value = plus(value, it.value) }
val old = center
center = divInt(value, values.size)
if (old != center) {
values.forEach { it.updateError(abs, minus) }
}
return true
}
fun pullFrom(clusters: MutableList<MutableCluster<V>>, identity: V): Boolean {
var candidate: ClusterValue<V>? = null
for (eCluser in clusters) {
if (eCluser.values.size > 1) {
for (value in eCluser.values) {
if (candidate == null || candidate.error < value.error) {
candidate = value
}
}
}
}
if (candidate != null) {
values.add(candidate)
candidate.cluster.values.remove(candidate)
this.generation++
candidate.cluster.generation++
candidate.cluster = this
center = candidate.value
candidate.error = identity
clusters.add(this)
return true
} else {
return false
}
}
}
private fun <V : Comparable<V>> Iterable<V>.clusterize(
random: RandomGenerator,
initialClusters: Int = 1,
identity: V,
plus: (V, V) -> V,
minus: (V, V) -> V,
divInt: (V, Int) -> V,
abs: (V) -> V,
heuristics: (min: V, max: V, error: V) -> Boolean,
): List<Cluster<V>> {
require(initialClusters > 0) { "Invalid amount of initial clusters: $initialClusters" }
val itr = iterator()
if (!itr.hasNext())
return listOf()
val clusters = ObjectArrayList<MutableCluster<V>>(initialClusters)
for (i in 0 until initialClusters) {
clusters.add(MutableCluster(identity))
}
val values = ObjectArrayList<ClusterValue<V>>()
var min = itr.next()
var max = min
values.add(ClusterValue(min, clusters.random(random), min))
values[0].cluster.values.add(values[0])
for (value in itr) {
min = minOf(min, value)
max = maxOf(max, value)
val cluster = clusters.random(random)
val wrapped = ClusterValue(value, cluster, value)
values.add(wrapped)
cluster.values.add(wrapped)
}
if (min == max) {
return listOf(Cluster.Impl(listOf(min), min))
}
while (true) {
var converged = false
var oversaturation = false
clusters.forEach { it.calculateCenter(identity, plus, minus, divInt, abs) }
while (!converged) {
converged = true
for (value in values) {
clusters.forEach { converged = !value.maybeSwitchCluster(it, abs, minus) && converged }
}
if (!converged) {
val emptyClusters = ObjectArrayList<MutableCluster<V>>()
val citr = clusters.iterator()
for (cluster in citr) {
if (!cluster.calculateCenter(identity, plus, minus, divInt, abs)) {
emptyClusters.add(cluster)
citr.remove()
}
}
for (cluster in emptyClusters) {
if (!cluster.pullFrom(clusters, identity)) {
oversaturation = true
break
}
}
}
}
val maxError = values.maxOf { it.error }
if (!oversaturation && clusters.size < values.size && heuristics(min, max, maxError)) {
val cluster = MutableCluster(identity)
check(cluster.pullFrom(clusters, identity)) { "Newly created cluster couldn't pull first value from other clusters (expected at least one cluster with 2 values in it)" }
clusters.add(cluster)
} else {
return clusters.stream()
.filter { it.values.isNotEmpty() }
.map { Cluster.Impl(it.values.map { it.value }, it.center) }
.sorted { o1, o2 -> o2.values.size.compareTo(o1.values.size) } // большие кластеры должны идти первыми
.toList()
}
}
}
// TODO: could use some tweaking
private val DECIMAL_ERROR_TOLERANCE = Decimal("0.02")
fun Iterable<Decimal>.clusterize(random: RandomGenerator, clusters: Int? = null): List<Cluster<Decimal>> {
return clusterize(random, clusters ?: 1, Decimal.ZERO, Decimal::plus, Decimal::minus, Decimal::div, Decimal::absoluteValue) { min, max, error ->
if (clusters != null)
false
else
error / (max - min) >= DECIMAL_ERROR_TOLERANCE
}
}

View File

@ -15,10 +15,10 @@ import ru.dbotthepony.mc.otm.config.ClientConfig
import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.clusterize
import ru.dbotthepony.mc.otm.core.math.isNegative import ru.dbotthepony.mc.otm.core.math.isNegative
import ru.dbotthepony.mc.otm.core.math.isZero import ru.dbotthepony.mc.otm.core.math.isZero
import ru.dbotthepony.mc.otm.menu.widget.IProfiledLevelGaugeWidget import ru.dbotthepony.mc.otm.menu.widget.IProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import java.math.BigInteger import java.math.BigInteger
import java.util.function.BooleanSupplier import java.util.function.BooleanSupplier
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
@ -314,34 +314,34 @@ private fun formatHistoryChart(
if (widget.received[it].isInfinite && widget.transferred[it].isInfinite) if (widget.received[it].isInfinite && widget.transferred[it].isInfinite)
0.5f 0.5f
else if (diff[it].isInfinite && diff[it].isNegative) else if (diff[it].isInfinite && diff[it].isNegative)
0.1f 0f
else if (diff[it].isInfinite && diff[it].isPositive) else if (diff[it].isInfinite && diff[it].isPositive)
0.9f 1f
else else
0.5f 0.5f
} }
received = FloatArray(diff.size) { received = FloatArray(diff.size) {
if (widget.received[it].isInfinite) 0.9f else 0.5f if (widget.received[it].isInfinite) 1f else 0.5f
} }
transferred = FloatArray(diff.size) { transferred = FloatArray(diff.size) {
if (widget.transferred[it].isInfinite) 0.1f else 0.5f if (widget.transferred[it].isInfinite) 0f else 0.5f
} }
labels = ChartLevelLabels( labels = ChartLevelLabels(
labels = mapOf( labels = mapOf(
0.5f to Decimal.ZERO.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias), 0.5f to Decimal.ZERO.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias),
0.1f to TextComponent("-∞"), 0f to TextComponent("-∞"),
0.9f to TextComponent(""), 1f to TextComponent(""),
) )
) )
} else if (hasPositiveInfinity) { } else if (hasPositiveInfinity) {
normalizedDiff = FloatArray(diff.size) { normalizedDiff = FloatArray(diff.size) {
if (diff[it].isInfinite) if (diff[it].isInfinite)
0.9f 1f
else else
0.5f 0f
} }
received = FloatArray(diff.size) { received = FloatArray(diff.size) {
@ -353,16 +353,16 @@ private fun formatHistoryChart(
labels = ChartLevelLabels( labels = ChartLevelLabels(
labels = mapOf( labels = mapOf(
0.5f to Decimal.ZERO.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias), 0f to Decimal.ZERO.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias),
0.9f to TextComponent(""), 1f to TextComponent(""),
) )
) )
} else if (hasNegativeInfinity) { } else if (hasNegativeInfinity) {
normalizedDiff = FloatArray(diff.size) { normalizedDiff = FloatArray(diff.size) {
if (diff[it].isInfinite) if (diff[it].isInfinite)
0.1f 0f
else else
0.5f 1f
} }
received = FloatArray(diff.size) received = FloatArray(diff.size)
@ -374,17 +374,16 @@ private fun formatHistoryChart(
labels = ChartLevelLabels( labels = ChartLevelLabels(
labels = mapOf( labels = mapOf(
0.5f to Decimal.ZERO.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias), 1f to Decimal.ZERO.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias),
0.1f to TextComponent("-∞"), 0f to TextComponent("-∞"),
) )
) )
} else { } else {
val max = maxOf(widget.received.maxOrNull() ?: Decimal.ZERO, widget.transferred.maxOrNull() ?: Decimal.ZERO) val maxTransferred = widget.transferred.maxOrNull() ?: Decimal.ZERO
val maxReceived = widget.received.maxOrNull() ?: Decimal.ZERO
val labelNames = Float2ObjectArrayMap<Component>() val labelNames = Float2ObjectArrayMap<Component>()
labelNames[0.5f] = Decimal.ZERO.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias) if (maxTransferred.isZero && maxReceived.isZero) {
if (max.isZero) {
normalizedDiff = FloatArray(diff.size) normalizedDiff = FloatArray(diff.size)
normalizedDiff.fill(0.5f) normalizedDiff.fill(0.5f)
@ -394,25 +393,131 @@ private fun formatHistoryChart(
transferred = FloatArray(diff.size) transferred = FloatArray(diff.size)
transferred.fill(0.5f) transferred.fill(0.5f)
labelNames[0.1f] = TextComponent("-∞") labelNames[0f] = TextComponent("-∞")
labelNames[0.9f] = TextComponent("") labelNames[0.5f] = Decimal.ZERO.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias)
labelNames[1f] = TextComponent("")
} else { } else {
val zero: Float
val transferredMult: Float
val receivedMult: Float
if (maxTransferred == maxReceived) {
zero = 0.5f
transferredMult = 0.5f
receivedMult = 0.5f
} else if (maxTransferred > maxReceived) {
val ratio = (maxReceived / maxTransferred).toFloat()
receivedMult = ratio * 0.5f
zero = 1f - receivedMult
transferredMult = 1f - receivedMult
} else {
val ratio = (maxTransferred / maxReceived).toFloat()
transferredMult = ratio * 0.5f
zero = transferredMult
receivedMult = 1f - transferredMult
}
normalizedDiff = FloatArray(diff.size) { normalizedDiff = FloatArray(diff.size) {
(diff[it] / max).toFloat() * 0.4f + 0.5f if (diff[it].isZero) {
zero
} else if (diff[it].isPositive) {
zero + (diff[it] / maxReceived).toFloat() * receivedMult
} else {
zero + (diff[it] / maxTransferred).toFloat() * transferredMult
}
} }
received = FloatArray(diff.size) { if (maxReceived.isNotZero) {
(widget.received[it] / max).toFloat() * 0.4f + 0.5f received = FloatArray(diff.size) {
zero + (widget.received[it] / maxReceived).toFloat() * receivedMult
}
} else {
received = FloatArray(diff.size)
received.fill(1f)
} }
transferred = FloatArray(diff.size) { if (maxTransferred.isNotZero) {
(widget.transferred[it] / max).toFloat() * -0.4f + 0.5f transferred = FloatArray(diff.size) {
zero + (widget.transferred[it] / maxTransferred).toFloat() * -transferredMult
}
} else {
transferred = FloatArray(diff.size)
transferred.fill(0f)
} }
labelNames[0.1f] = (-max).formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias) if (maxTransferred.isNotZero && maxReceived.isNotZero)
if (verbose.asBoolean) labelNames[0.3f] = (-max * Decimal.ONE_HALF).formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias) labelNames[zero] = Decimal.ZERO.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias)
labelNames[0.9f] = max.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias)
if (verbose.asBoolean) labelNames[0.7f] = (max * Decimal.ONE_HALF).formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias) labelNames[0f] = (-maxTransferred).formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias)
labelNames[1f] = maxReceived.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias)
val rand = java.util.Random()
if (maxTransferred.isNotZero && transferredMult > 0.2f) {
for (cluster in widget.transferred.clusterize(rand)) {
val perc = (cluster.center / maxTransferred).toFloat() * transferredMult
if (labelNames.keys.none { (it - perc).absoluteValue < 0.08f })
labelNames[perc] = (-cluster.center).formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias)
}
}
if (maxReceived.isNotZero && receivedMult > 0.2f) {
for (cluster in widget.received.clusterize(rand)) {
val perc = zero + (cluster.center / maxReceived).toFloat() * receivedMult
if (labelNames.keys.none { (it - perc).absoluteValue < 0.08f })
labelNames[perc] = cluster.center.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias)
}
}
val clusters = diff.asIterable().clusterize(rand)
for (cluster in clusters) {
val perc: Float
if (cluster.center.isZero)
continue
else if (cluster.center.isPositive)
perc = zero + (cluster.center / maxReceived).toFloat() * receivedMult
else
perc = (1f + (cluster.center / maxTransferred).toFloat()) * transferredMult
if (labelNames.keys.none { (it - perc).absoluteValue < 0.08f })
labelNames[perc] = cluster.center.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias)
}
/*
if ((zero - 0.5f).absoluteValue >= 0.25f) {
// если "ноль" далёк от центра графика, то надо попробовать добавить пограничную направляющую
if (maxReceived > maxTransferred) {
// пограничная на приём
val min = widget.received.minOrNull() ?: Decimal.ZERO
val perc = (min / maxReceived).toFloat()
if (min.isNotZero && perc < 0.92f) {
labelNames[zero + perc * receivedMult] = min.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias)
}
} else if (maxTransferred.isNotZero) {
// пограничная на отдачу
val min = widget.transferred.minOrNull() ?: Decimal.ZERO
val perc = (min / maxTransferred).toFloat()
if (min.isNotZero && perc < 0.92f) {
labelNames[perc * transferredMult] = (-min).formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias)
}
}
} else {
// если нет, то надо попробовать добавить половинчатые направляющие
if (transferredMult > 0.3f && (maxReceived.isZero || verbose.asBoolean && maxTransferred.isNotZero))
labelNames[transferredMult * 0.5f] = (-maxTransferred * Decimal.ONE_HALF).formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias)
if (receivedMult > 0.3f && (maxTransferred.isZero || verbose.asBoolean && maxReceived.isNotZero))
labelNames[zero + receivedMult * 0.5f] = (maxReceived * Decimal.ONE_HALF).formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias)
}
*/
} }
labels = ChartLevelLabels(labels = labelNames) labels = ChartLevelLabels(labels = labelNames)
@ -442,6 +547,38 @@ private fun formatHistoryChart(
) )
} }
result.add(Either.left(TextComponent("")))
run {
val incoming = widget.received[0]
val outgoing = widget.transferred[0]
val delta = incoming - outgoing
val deltaColor = if (delta.isZero) ChatFormatting.GRAY else if (delta.isPositive) ChatFormatting.DARK_GREEN else ChatFormatting.DARK_RED
result.add(Either.left(TranslatableComponent(
"otm.gui.diff",
delta.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias).copy().withStyle(deltaColor),
incoming.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias).copy().withStyle(ChatFormatting.DARK_GREEN),
outgoing.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias).copy().withStyle(ChatFormatting.DARK_RED),
)))
}
run {
val incoming = widget.received.calculateAverage()
val outgoing = widget.transferred.calculateAverage()
val delta = incoming - outgoing
val deltaColor = if (delta.isZero) ChatFormatting.GRAY else if (delta.isPositive) ChatFormatting.DARK_GREEN else ChatFormatting.DARK_RED
result.add(Either.left(TranslatableComponent(
"otm.gui.diff",
delta.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias).copy().withStyle(deltaColor),
incoming.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias).copy().withStyle(ChatFormatting.DARK_GREEN),
outgoing.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias).copy().withStyle(ChatFormatting.DARK_RED),
)))
}
result.add(Either.right(ChartTooltipElement(charts, if (verbose.asBoolean) 200f else 100f, if (verbose.asBoolean) 120f else 60f, levelLabels = labels))) result.add(Either.right(ChartTooltipElement(charts, if (verbose.asBoolean) 200f else 100f, if (verbose.asBoolean) 120f else 60f, levelLabels = labels)))
} }

View File

@ -1,9 +1,7 @@
package ru.dbotthepony.mc.otm.registry package ru.dbotthepony.mc.otm.registry
import net.minecraft.core.registries.BuiltInRegistries import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.world.level.levelgen.feature.Feature
import net.neoforged.bus.api.IEventBus import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.mc.otm.worldgen.feature.BlackHolePlacerConfiguration
import ru.dbotthepony.mc.otm.worldgen.feature.BlackHolePlacerFeature import ru.dbotthepony.mc.otm.worldgen.feature.BlackHolePlacerFeature
object MWorldGenFeatures { object MWorldGenFeatures {
@ -13,7 +11,5 @@ object MWorldGenFeatures {
registry.register(bus) registry.register(bus)
} }
val BLACK_HOLE_PLACER: Feature<BlackHolePlacerConfiguration> by registry.register("black_hole_placer") { val BLACK_HOLE_PLACER by registry.register("black_hole_placer") { BlackHolePlacerFeature }
BlackHolePlacerFeature(BlackHolePlacerConfiguration.CODEC)
}
} }

View File

@ -6,38 +6,36 @@ import net.minecraft.world.level.levelgen.feature.Feature
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleBlockEntity import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleBlockEntity
import ru.dbotthepony.mc.otm.config.ServerConfig
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.nextDecimal import ru.dbotthepony.mc.otm.core.math.nextDecimal
import ru.dbotthepony.mc.otm.data.DecimalCodec import ru.dbotthepony.mc.otm.data.DecimalCodec
import ru.dbotthepony.mc.otm.registry.MBlocks import ru.dbotthepony.mc.otm.registry.MBlocks
class BlackHolePlacerConfiguration(val chance: Float, val minMatter: Decimal, val maxMatter: Decimal) : FeatureConfiguration { object BlackHolePlacerFeature : Feature<BlackHolePlacerFeature.Config>(
companion object { RecordCodecBuilder.create {
val CODEC: Codec<BlackHolePlacerConfiguration> = RecordCodecBuilder.create { it.group(
t -> t.group( Codec.floatRange(0.0F, 1.0F).fieldOf("chance").forGetter(Config::chance),
Codec.floatRange(0.0F, 1.0F).fieldOf("chance").forGetter(BlackHolePlacerConfiguration::chance), DecimalCodec.fieldOf("matter_min").forGetter(Config::minMatter),
DecimalCodec.fieldOf("matter_min").forGetter(BlackHolePlacerConfiguration::minMatter), DecimalCodec.fieldOf("matter_max").forGetter(Config::maxMatter)
DecimalCodec.fieldOf("matter_max").forGetter(BlackHolePlacerConfiguration::maxMatter) ).apply(it, ::Config)
).apply(t, ::BlackHolePlacerConfiguration)
}
} }
} ) {
data class Config(val chance: Float, val minMatter: Decimal, val maxMatter: Decimal) : FeatureConfiguration
class BlackHolePlacerFeature(val codec: Codec<BlackHolePlacerConfiguration>) : Feature<BlackHolePlacerConfiguration>(codec) { override fun place(context: FeaturePlaceContext<Config>): Boolean {
override fun place(context: FeaturePlaceContext<BlackHolePlacerConfiguration?>): Boolean {
val random = context.random() val random = context.random()
val level = context.level() val level = context.level()
val pos = context.origin() val pos = context.origin()
val config = context.config() val config = context.config()
if (config == null) return false
if (level.getBlockState(pos).isAir && random.nextDouble() < config.chance.toDouble()) { if (level.getBlockState(pos).isAir && random.nextDouble() < config.chance.toDouble()) {
if (level.setBlock(pos, MBlocks.BLACK_HOLE.defaultBlockState(), 2)) { if (level.setBlock(pos, MBlocks.BLACK_HOLE.defaultBlockState(), 2)) {
val entity = level.getBlockEntity(pos) val entity = level.getBlockEntity(pos)
if (entity is BlackHoleBlockEntity) { if (entity is BlackHoleBlockEntity) {
entity.mass = random.nextDecimal(config.minMatter, config.maxMatter) entity.mass = random.nextDecimal(config.minMatter, config.maxMatter) * ServerConfig.Blackhole.BASELINE_MASS
} }
return true return true

Binary file not shown.

Before

Width:  |  Height:  |  Size: 898 B