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
import net.minecraft.core.HolderSet
import net.minecraft.core.RegistrySetBuilder
import net.minecraft.core.registries.Registries
import net.minecraft.data.worldgen.BootstrapContext
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.registry.MBlocks
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 {
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.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 {

View File

@ -11,7 +11,6 @@ import net.neoforged.fml.ModList;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent;
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
import net.neoforged.fml.loading.FMLLoader;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import ru.dbotthepony.mc.otm.android.AndroidResearchDescription;
@ -152,7 +151,7 @@ public final class OverdriveThatMatters {
bus.addListener(EventPriority.HIGHEST, this::setup);
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, AndroidAbilityKeyMapping.INSTANCE::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.mc.otm.client.minecraft
import kotlin.math.PI
import kotlin.math.absoluteValue
import kotlin.math.acos
import kotlin.math.cos
import kotlin.math.pow
@ -287,10 +288,31 @@ fun renderChart(
val font = levelLabels.font ?: minecraft.font
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 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(
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.core.TextComponent
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.asAngle
import ru.dbotthepony.mc.otm.core.math.clusterize
import ru.dbotthepony.mc.otm.core.util.formatPower
import ru.dbotthepony.mc.otm.core.math.times
import java.util.Random
import kotlin.math.PI
import kotlin.math.absoluteValue
class EnergyCounterRenderer(private val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer<EnergyCounterBlockEntity> {
private val random = Random()
override fun render(
tile: EnergyCounterBlockEntity,
p_112308_: Float,
@ -80,7 +86,7 @@ class EnergyCounterRenderer(private val context: BlockEntityRendererProvider.Con
if (maximum.isZero || maximum.isInfinite) {
normalized = FloatArray(chart.width) {
if (chart[it].isInfinite) 0.8f else 0.0f
if (chart[it].isInfinite) 1f else 0.0f
}
levelLabels = ChartLevelLabels(
@ -97,9 +103,25 @@ class EnergyCounterRenderer(private val context: BlockEntityRendererProvider.Con
val map = Float2ObjectArrayMap<Component>()
map[1f] = maximum.formatPower()
map[0.75f] = (maximum * 0.75).formatPower()
map[0.5f] = (maximum * 0.5).formatPower()
map[0.25f] = (maximum * 0.25).formatPower()
for (cluster in chart.asIterable().clusterize(random)) {
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(
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.renderChart
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.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent
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 kotlin.math.absoluteValue
open class DecimalHistoryChartPanel<out S : MatteryScreen<*>>(
screen: S,
@ -31,25 +35,41 @@ open class DecimalHistoryChartPanel<out S : MatteryScreen<*>>(
if (maximum.isZero || maximum.isInfinite) {
normalized = FloatArray(chart.width) {
if (chart[it].isInfinite) 0.8f else 0.0f
if (chart[it].isInfinite) 1f else 0.0f
}
levelLabels = ChartLevelLabels(
labels = mapOf(
0.8f to TextComponent(""),
1f to TextComponent(""),
// 0f to formatText(Decimal.ZERO),
),
font = font
)
} else {
normalized = FloatArray(chart.width) { (chart[it] / maximum).toFloat() * 0.9f }
normalized = FloatArray(chart.width) { (chart[it] / maximum).toFloat() }
val map = Float2ObjectArrayMap<Component>()
map[0.9f] = formatText(maximum)
map[0.9f * 0.75f] = formatText(maximum * 0.75)
map[0.9f * 0.5f] = formatText(maximum * 0.5)
map[0.9f * 0.25f] = formatText(maximum * 0.25)
map[1f] = formatText(maximum)
for (cluster in chart.asIterable().clusterize(randomGenerator)) {
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(
labels = map,
@ -63,7 +83,8 @@ open class DecimalHistoryChartPanel<out S : MatteryScreen<*>>(
graphics.pose,
listOf(normalized to RGBAColor.WHITE),
width,
height,
height - font.lineHeight - 4f,
y = font.lineHeight.toFloat() + 4f,
labels = ChartMouseLabels(
mouseX - absoluteX,
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.screen.MatteryScreen
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.flatMap
import java.util.*
import java.util.concurrent.CopyOnWriteArrayList
import java.util.function.Predicate
import java.util.random.RandomGenerator
import kotlin.collections.ArrayList
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.
*/

View File

@ -525,14 +525,14 @@ class AndroidStationScreen(p_97741_: AndroidStationMenu, p_97742_: Inventory, p_
if (isPreview && !layoutInvalidated) {
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) {
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
.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)
init {

View File

@ -1,10 +1,8 @@
package ru.dbotthepony.mc.otm.core.chart
import com.google.common.collect.ImmutableList
import com.mojang.serialization.Codec
import net.minecraft.network.RegistryFriendlyByteBuf
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.data.DecimalCodec
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
@ -33,28 +31,4 @@ class DecimalHistoryChart : AbstractHistoryChart<Decimal> {
get() = DecimalCodec
override val streamCodec: MatteryStreamCodec<RegistryFriendlyByteBuf, Decimal>
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
import ru.dbotthepony.mc.otm.core.math.Cluster
import java.util.random.RandomGenerator
/**
* 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.TranslatableComponent
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.isZero
import ru.dbotthepony.mc.otm.menu.widget.IProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import java.math.BigInteger
import java.util.function.BooleanSupplier
import kotlin.math.absoluteValue
@ -314,34 +314,34 @@ private fun formatHistoryChart(
if (widget.received[it].isInfinite && widget.transferred[it].isInfinite)
0.5f
else if (diff[it].isInfinite && diff[it].isNegative)
0.1f
0f
else if (diff[it].isInfinite && diff[it].isPositive)
0.9f
1f
else
0.5f
}
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) {
if (widget.transferred[it].isInfinite) 0.1f else 0.5f
if (widget.transferred[it].isInfinite) 0f else 0.5f
}
labels = ChartLevelLabels(
labels = mapOf(
0.5f to Decimal.ZERO.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias),
0.1f to TextComponent("-∞"),
0.9f to TextComponent(""),
0f to TextComponent("-∞"),
1f to TextComponent(""),
)
)
} else if (hasPositiveInfinity) {
normalizedDiff = FloatArray(diff.size) {
if (diff[it].isInfinite)
0.9f
1f
else
0.5f
0f
}
received = FloatArray(diff.size) {
@ -353,16 +353,16 @@ private fun formatHistoryChart(
labels = ChartLevelLabels(
labels = mapOf(
0.5f to Decimal.ZERO.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias),
0.9f to TextComponent(""),
0f to Decimal.ZERO.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias),
1f to TextComponent(""),
)
)
} else if (hasNegativeInfinity) {
normalizedDiff = FloatArray(diff.size) {
if (diff[it].isInfinite)
0.1f
0f
else
0.5f
1f
}
received = FloatArray(diff.size)
@ -374,17 +374,16 @@ private fun formatHistoryChart(
labels = ChartLevelLabels(
labels = mapOf(
0.5f to Decimal.ZERO.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias),
0.1f to TextComponent("-∞"),
1f to Decimal.ZERO.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias),
0f to TextComponent("-∞"),
)
)
} 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>()
labelNames[0.5f] = Decimal.ZERO.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias)
if (max.isZero) {
if (maxTransferred.isZero && maxReceived.isZero) {
normalizedDiff = FloatArray(diff.size)
normalizedDiff.fill(0.5f)
@ -394,25 +393,131 @@ private fun formatHistoryChart(
transferred = FloatArray(diff.size)
transferred.fill(0.5f)
labelNames[0.1f] = TextComponent("-∞")
labelNames[0.9f] = TextComponent("")
labelNames[0f] = TextComponent("-∞")
labelNames[0.5f] = Decimal.ZERO.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias)
labelNames[1f] = TextComponent("")
} 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) {
(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) {
(widget.received[it] / max).toFloat() * 0.4f + 0.5f
if (maxReceived.isNotZero) {
received = FloatArray(diff.size) {
zero + (widget.received[it] / maxReceived).toFloat() * receivedMult
}
} else {
received = FloatArray(diff.size)
received.fill(1f)
}
transferred = FloatArray(diff.size) {
(widget.transferred[it] / max).toFloat() * -0.4f + 0.5f
if (maxTransferred.isNotZero) {
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 (verbose.asBoolean) labelNames[0.3f] = (-max * Decimal.ONE_HALF).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)
if (maxTransferred.isNotZero && maxReceived.isNotZero)
labelNames[zero] = Decimal.ZERO.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)
@ -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)))
}

View File

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

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.configurations.FeatureConfiguration
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.nextDecimal
import ru.dbotthepony.mc.otm.data.DecimalCodec
import ru.dbotthepony.mc.otm.registry.MBlocks
class BlackHolePlacerConfiguration(val chance: Float, val minMatter: Decimal, val maxMatter: Decimal) : FeatureConfiguration {
companion object {
val CODEC: Codec<BlackHolePlacerConfiguration> = RecordCodecBuilder.create {
t -> t.group(
Codec.floatRange(0.0F, 1.0F).fieldOf("chance").forGetter(BlackHolePlacerConfiguration::chance),
DecimalCodec.fieldOf("matter_min").forGetter(BlackHolePlacerConfiguration::minMatter),
DecimalCodec.fieldOf("matter_max").forGetter(BlackHolePlacerConfiguration::maxMatter)
).apply(t, ::BlackHolePlacerConfiguration)
}
object BlackHolePlacerFeature : Feature<BlackHolePlacerFeature.Config>(
RecordCodecBuilder.create {
it.group(
Codec.floatRange(0.0F, 1.0F).fieldOf("chance").forGetter(Config::chance),
DecimalCodec.fieldOf("matter_min").forGetter(Config::minMatter),
DecimalCodec.fieldOf("matter_max").forGetter(Config::maxMatter)
).apply(it, ::Config)
}
}
) {
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<BlackHolePlacerConfiguration?>): Boolean {
override fun place(context: FeaturePlaceContext<Config>): Boolean {
val random = context.random()
val level = context.level()
val pos = context.origin()
val config = context.config()
if (config == null) return false
if (level.getBlockState(pos).isAir && random.nextDouble() < config.chance.toDouble()) {
if (level.setBlock(pos, MBlocks.BLACK_HOLE.defaultBlockState(), 2)) {
val entity = level.getBlockEntity(pos)
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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 898 B