Android config values, move immune effect list to tags

Clear immune effects before ticking player
This commit is contained in:
DBotThePony 2022-09-17 21:22:11 +07:00
parent 57d7c7f908
commit ca70916331
Signed by: DBot
GPG Key ID: DCC23B5715498507
15 changed files with 417 additions and 78 deletions

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.datagen.tags
import net.minecraft.world.effect.MobEffects
import net.minecraft.world.item.Items
import net.minecraft.world.item.Tiers
import ru.dbotthepony.mc.otm.registry.MBlocks
@ -97,4 +98,20 @@ fun addTags(tagsProvider: TagsProvider) {
tagsProvider.witherImmune.add(
MBlocks.BLACK_HOLE,
)
tagsProvider.androidImmuneEffects.add(
MobEffects.CONDUIT_POWER,
MobEffects.HEAL,
// maybe it makes things go haywire idk
// MobEffects.HARM,
MobEffects.REGENERATION,
MobEffects.WATER_BREATHING,
MobEffects.POISON,
// even skeletons can be withered
// MobEffects.WITHER,
MobEffects.HEALTH_BOOST,
MobEffects.ABSORPTION,
MobEffects.SATURATION,
MobEffects.DOLPHINS_GRACE,
)
}

View File

@ -12,6 +12,7 @@ import net.minecraft.world.item.Tiers
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.gameevent.GameEvent
import net.minecraftforge.data.event.GatherDataEvent
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
import ru.dbotthepony.mc.otm.datagen.DataGen
import net.minecraft.data.tags.TagsProvider as MinecraftTagsProvider
@ -57,6 +58,10 @@ class TagsProvider(
private val event: GatherDataEvent
) {
inner class Delegate<T>(registry: Registry<T>) : MinecraftTagsProvider<T>(event.generator, registry, DataGen.MOD_ID, event.existingFileHelper) {
init {
event.generator.addProvider(true, this)
}
private val tags = HashMap<TagKey<T>, ObjectArraySet<T>>()
override fun addTags() {
@ -165,6 +170,9 @@ class TagsProvider(
val blocks = Delegate(Registry.BLOCK)
val items = Delegate(Registry.ITEM)
val mobEffects = Delegate(Registry.MOB_EFFECT)
val androidImmuneEffects = mobEffects.appender(MatteryPlayerCapability.ANDROID_IMMUNE_EFFECTS)
val requiresShovel = blocks.appender(BlockTags.MINEABLE_WITH_SHOVEL)
val requiresAxe = blocks.appender(BlockTags.MINEABLE_WITH_AXE)
@ -216,11 +224,6 @@ class TagsProvider(
val gameEvents = Delegate(Registry.GAME_EVENT)
val vibrations = gameEvents.appender(GameEventTags.VIBRATIONS)
init {
event.generator.addProvider(true, blocks)
event.generator.addProvider(true, items)
}
fun requiresPickaxe(block: Block, tier: Tier? = null): TagsProvider {
requiresPickaxe.add(block)

View File

@ -122,8 +122,6 @@ public final class OverdriveThatMatters {
EVENT_BUS.addListener(EventPriority.NORMAL, QuantumBatteryItem.Companion::clientDisconnect);
EVENT_BUS.addListener(EventPriority.LOWEST, PortableCondensationDriveItem.Companion::onPickupEvent);
MatteryPlayerCapability.Companion.registerEffects(event);
MatteryPlayerNetworkChannel.INSTANCE.register();
MenuNetworkChannel.INSTANCE.register();
WeaponNetworkChannel.INSTANCE.register();

View File

@ -0,0 +1,171 @@
package ru.dbotthepony.mc.otm
import it.unimi.dsi.fastutil.ints.IntArrayList
import net.minecraft.resources.ResourceLocation
import net.minecraftforge.common.ForgeConfigSpec
import net.minecraftforge.registries.IForgeRegistry
import java.util.LinkedList
abstract class ObservedConfigList<V : Any>(val parent: ForgeConfigSpec.ConfigValue<MutableList<String>>, private val allowNulls: Boolean = false) : AbstractMutableList<V>(), RandomAccess {
private val rawValue: MutableList<String> by parent
private val observedValue = LinkedList<String>()
private val parsedView: ArrayList<V> = ArrayList()
protected abstract fun toString(value: V): Pair<String, V>?
protected abstract fun fromString(value: String): V?
private fun load() {
parsedView.clear()
observedValue.clear()
val removeIndexes = IntArrayList()
for ((i, value) in rawValue.withIndex()) {
val parsedValue = fromString(value)
if (parsedValue != null || allowNulls) {
observedValue.add(value)
}
if (parsedValue != null) {
parsedView.add(parsedValue)
} else if (!allowNulls) {
removeIndexes.add(i)
}
}
for (i in removeIndexes.size - 1 downTo 0) {
rawValue.removeAt(removeIndexes.getInt(i))
}
if (removeIndexes.isNotEmpty()) {
parent.save()
}
}
private fun observe() {
val observedValue = observedValue
val rawValue = rawValue
if (observedValue.size != rawValue.size) {
load()
} else {
val iter1 = rawValue.iterator()
val iter2 = observedValue.iterator()
while (iter1.hasNext() && iter2.hasNext()) {
if (iter1.next() != iter2.next()) {
load()
return
}
}
if (iter1.hasNext() || iter2.hasNext()) {
load()
}
}
}
final override fun add(index: Int, element: V) {
observe()
val (serialized, newObject) = toString(element) ?: return
rawValue.add(index, serialized)
parsedView.add(index, newObject)
observedValue.add(index, serialized)
parent.save()
}
final override fun removeAt(index: Int): V {
observe()
val old = parsedView[index]
val (serialized, _) = toString(old) ?: throw RuntimeException("Serializer did not accept element already present in list")
val oldStr = rawValue.getOrNull(index)
if (oldStr == serialized) {
rawValue.removeAt(index)
observedValue.removeAt(index)
} else {
val serializedIndex = rawValue.indexOf(serialized)
if (serializedIndex == -1) {
throw NoSuchElementException("Element $old is present in parsed list at index $index with serialized form of '$serialized', but it is not found in Forge's config list")
}
rawValue.removeAt(serializedIndex)
observedValue.removeAt(serializedIndex)
}
parsedView.removeAt(index)
parent.save()
return old
}
final override fun set(index: Int, element: V): V {
observe()
val old = parsedView[index]
val (serializedNew, newElement) = toString(element) ?: return old
if (old === newElement) {
return old
}
val (serialized, _) = toString(old) ?: throw RuntimeException("Serializer did not accept element already present in list")
check(rawValue[index] == serialized) { "Old value serialized representation $serialized does not match already stored representation ${rawValue[index]} at index $index" }
parsedView[index] = newElement
observedValue[index] = serializedNew
rawValue[index] = serializedNew
parent.save()
return old
}
final override val size: Int get() {
observe()
return parsedView.size
}
override fun get(index: Int): V {
observe()
return parsedView[index]
}
}
fun <T : Any> ForgeConfigSpec.Builder.defineObjectList(
name: String,
defaultValues: () -> List<T>,
write: (value: T) -> Pair<String, T>?,
read: (value: String) -> T?
): ObservedConfigList<T> {
val parent = defineListAllowEmpty(name.split('.'), { defaultValues.invoke().mapNotNull { write.invoke(it)?.first }.toMutableList()}, { it is String }) as ForgeConfigSpec.ConfigValue<MutableList<String>>
return object : ObservedConfigList<T>(parent) {
override fun toString(value: T): Pair<String, T>? {
return write.invoke(value)
}
override fun fromString(value: String): T? {
return read.invoke(value)
}
}
}
@Deprecated("Obviously better use tags")
fun <T : Any> ForgeConfigSpec.Builder.defineForgeObjectList(
name: String,
defaultValues: () -> List<T>,
registry: IForgeRegistry<T>
): ObservedConfigList<T> {
return defineObjectList(name, defaultValues,
write = { (registry.getKey(it)?.toString() ?: return@defineObjectList null) to it },
read = { registry.getValue(ResourceLocation(it)) })
}

View File

@ -12,20 +12,20 @@ abstract class ObservedConfigValue<V : Any>(val parent: ConfigValue<String>) : R
private var observedValue: String? = null
private var cachedValue: V? = null
protected abstract fun fromValue(value: String): V?
protected abstract fun fromString(value: String): V?
/**
* value [V] -> newRawValue [String], newValue [V]
*/
protected abstract fun toValue(value: V): Pair<String, V>
protected abstract fun toString(value: V): Pair<String, V>
override fun get(): V {
if (cachedValue == null || rawValue != observedValue) {
var deserialized = fromValue(rawValue)
var deserialized = fromString(rawValue)
if (deserialized == null) {
val default = parent.default
deserialized = fromValue(default)
deserialized = fromString(default)
if (deserialized == null) {
throw RuntimeException("Deserializer did not accept default value: $default")
@ -49,12 +49,12 @@ abstract class ObservedConfigValue<V : Any>(val parent: ConfigValue<String>) : R
override fun accept(value: V) {
if (cachedValue == null || rawValue != observedValue) {
cachedValue = fromValue(rawValue)
cachedValue = fromString(rawValue)
observedValue = rawValue
}
if (cachedValue != value) {
val (newRaw, newValue) = toValue(value)
val (newRaw, newValue) = toString(value)
if (cachedValue != newValue) {
rawValue = newRaw

View File

@ -1,8 +1,11 @@
package ru.dbotthepony.mc.otm
import net.minecraft.world.effect.MobEffect
import net.minecraft.world.effect.MobEffects
import net.minecraftforge.common.ForgeConfigSpec
import net.minecraftforge.fml.ModLoadingContext
import net.minecraftforge.fml.config.ModConfig
import net.minecraftforge.registries.ForgeRegistries
import ru.dbotthepony.mc.otm.block.entity.AndroidStationBlockEntity
import ru.dbotthepony.mc.otm.block.entity.ChemicalGeneratorBlockEntity
import ru.dbotthepony.mc.otm.block.entity.matter.MatterBottlerBlockEntity
@ -125,6 +128,23 @@ object ServerConfig {
val DRIVE_VIEWER = BlockEnergyStorageImpl.makeConfigEntry(specBuilder, MNames.DRIVE_VIEWER)
val DRIVE_RACK = BlockEnergyStorageImpl.makeConfigEntry(specBuilder, MNames.DRIVE_RACK, capacity = ImpreciseFraction(80_000))
init {
specBuilder.pop()
specBuilder.comment("Tweaking of android players").push("android_player")
}
val ANDROID_ENERGY_PER_HUNGER_POINT by specBuilder.defineImpreciseFraction("energy_per_hunger", ImpreciseFraction(1000), ImpreciseFraction.ZERO)
val ANDROID_MAX_ENERGY by specBuilder.comment("Internal battery of every android has this much storage").defineImpreciseFraction("capacity", ImpreciseFraction(80_000), ImpreciseFraction.ZERO)
init {
specBuilder.pop()
specBuilder.comment("Tweaking of exosuits").push("exosuit_player")
}
val INFINITE_EXOSUIT_UPGRADES: Boolean by specBuilder.comment("Allows to apply the same upgrade over and over again.", "Obviously completely breaks balance.").define("infinite_upgrades", false)
init {
specBuilder.pop()
}

View File

@ -20,9 +20,11 @@ import ru.dbotthepony.mc.otm.core.defineImpreciseFraction
import ru.dbotthepony.mc.otm.core.ifPresentK
import ru.dbotthepony.mc.otm.menu.AndroidStationMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MBlocks
import ru.dbotthepony.mc.otm.registry.MNames
import ru.dbotthepony.mc.otm.registry.WriteOnce
@Suppress("ObjectPropertyName")
class AndroidStationBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
MatteryPoweredBlockEntity(MBlockEntities.ANDROID_STATION, p_155229_, p_155230_), MenuProvider {
@ -31,7 +33,7 @@ class AndroidStationBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
}
override val defaultDisplayName: Component
get() = MACHINE_NAME
get() = MBlocks.ANDROID_STATION.name
override val energy = object : WorkerEnergyStorage(this@AndroidStationBlockEntity::setChangedLight, ::CAPACITY, ::MAX_IO, { null }) {
override fun extractEnergyInner(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
@ -96,8 +98,6 @@ class AndroidStationBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
}
companion object {
private val MACHINE_NAME = TranslatableComponent("block.overdrive_that_matters.android_station")
private var _CAPACITY: ImpreciseFractionConfigValue by WriteOnce()
private var _MAX_IO: ImpreciseFractionConfigValue by WriteOnce()
private var _ENERGY_PER_OPERATION: ImpreciseFractionConfigValue by WriteOnce()

View File

@ -1,7 +1,6 @@
package ru.dbotthepony.mc.otm.capability
import it.unimi.dsi.fastutil.objects.Object2IntAVLTreeMap
import it.unimi.dsi.fastutil.objects.ObjectArraySet
import net.minecraft.ChatFormatting
import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag
@ -10,8 +9,8 @@ import net.minecraft.nbt.StringTag
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.tags.TagKey
import net.minecraft.world.effect.MobEffect
import net.minecraft.world.effect.MobEffects
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.MobSpawnType
@ -35,7 +34,7 @@ import net.minecraftforge.event.entity.living.LivingSpawnEvent
import net.minecraftforge.event.entity.player.EntityItemPickupEvent
import net.minecraftforge.event.entity.player.PlayerEvent
import net.minecraftforge.eventbus.api.Event
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent
import net.minecraftforge.registries.ForgeRegistries
import org.apache.commons.lang3.mutable.MutableInt
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.*
@ -190,7 +189,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
var willBecomeAndroid by synchronizer.bool()
var isAndroid by synchronizer.bool()
val androidEnergy = SynchronizedPowerWithBattery(ply, synchronizer, DEFAULT_MAX_ANDROID_POWER, DEFAULT_MAX_ANDROID_POWER)
val androidEnergy = SynchronizedPowerWithBattery(ply, synchronizer, ServerConfig.ANDROID_MAX_ENERGY, ServerConfig.ANDROID_MAX_ENERGY)
fun invalidateNetworkState() {
invalidateNetworkIn = 10
@ -210,8 +209,8 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
shouldPlaySound = false
iteration = 0
deathLog.clear()
androidEnergy.batteryLevel = DEFAULT_MAX_ANDROID_POWER
androidEnergy.maxBatteryLevel = DEFAULT_MAX_ANDROID_POWER
androidEnergy.batteryLevel = ServerConfig.ANDROID_MAX_ENERGY
androidEnergy.maxBatteryLevel = ServerConfig.ANDROID_MAX_ENERGY
}
fun becomeAndroidAndKill() {
@ -230,7 +229,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
iteration = 0
deathLog.clear()
androidEnergy.batteryLevel = ImpreciseFraction.ZERO
androidEnergy.maxBatteryLevel = DEFAULT_MAX_ANDROID_POWER
androidEnergy.maxBatteryLevel = ServerConfig.ANDROID_MAX_ENERGY
dropBattery()
}
@ -521,6 +520,17 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
tickInventory()
}
fun preTick() {
if (!ply.isAlive) return
if (isAndroid) {
ForgeRegistries.MOB_EFFECTS.tags()?.getTag(ANDROID_IMMUNE_EFFECTS)?.forEach {
if (ply.hasEffect(it))
ply.removeEffect(it)
}
}
}
fun tick() {
if (!ply.isAlive) return
@ -538,10 +548,6 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
if (ply.airSupply < ply.maxAirSupply)
ply.airSupply = ply.maxAirSupply
for (effect in UNAFFECTED_EFFECTS)
if (ply.hasEffect(effect))
ply.removeEffect(effect)
// TODO: Maybe passive drain?
// extractEnergyInner(BigDecimal.valueOf(new Random().nextDouble()), false);
if (ply.isSwimming && !hasFeature(AndroidFeatures.AIR_BAGS)) {
@ -552,28 +558,28 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
val stats = ply.foodData
while (stats.foodLevel < 18 && androidEnergy.extractEnergyInner(ENERGY_FOR_HUNGER_POINT, true) >= ENERGY_FOR_HUNGER_POINT) {
androidEnergy.extractEnergyInner(ENERGY_FOR_HUNGER_POINT, false)
while (stats.foodLevel < 18 && androidEnergy.extractEnergyInner(ServerConfig.ANDROID_ENERGY_PER_HUNGER_POINT, true) >= ServerConfig.ANDROID_ENERGY_PER_HUNGER_POINT) {
androidEnergy.extractEnergyInner(ServerConfig.ANDROID_ENERGY_PER_HUNGER_POINT, false)
stats.foodLevel = stats.foodLevel + 1
}
// "block" quick regeneration
// also cause power to generate while in peaceful
while (stats.foodLevel > 18 && androidEnergy.receiveEnergyInner(ENERGY_FOR_HUNGER_POINT, true) >= ENERGY_FOR_HUNGER_POINT) {
androidEnergy.receiveEnergyInner(ENERGY_FOR_HUNGER_POINT, false)
while (stats.foodLevel > 18 && androidEnergy.receiveEnergyInner(ServerConfig.ANDROID_ENERGY_PER_HUNGER_POINT, true) >= ServerConfig.ANDROID_ENERGY_PER_HUNGER_POINT) {
androidEnergy.receiveEnergyInner(ServerConfig.ANDROID_ENERGY_PER_HUNGER_POINT, false)
stats.foodLevel = stats.foodLevel - 1
}
val foodLevel = stats.foodLevel.toFloat()
if (stats.saturationLevel < foodLevel) {
val extracted = androidEnergy.extractEnergyInner(ENERGY_FOR_HUNGER_POINT * (foodLevel - stats.saturationLevel), false)
stats.setSaturation(stats.saturationLevel + (extracted / ENERGY_FOR_HUNGER_POINT).toFloat())
val extracted = androidEnergy.extractEnergyInner(ServerConfig.ANDROID_ENERGY_PER_HUNGER_POINT * (foodLevel - stats.saturationLevel), false)
stats.setSaturation(stats.saturationLevel + (extracted / ServerConfig.ANDROID_ENERGY_PER_HUNGER_POINT).toFloat())
}
if (stats.exhaustionLevel > 0f) {
val extracted = androidEnergy.extractEnergyInner(ENERGY_FOR_HUNGER_POINT * (stats.exhaustionLevel / 4f), false)
stats.setExhaustion(stats.exhaustionLevel - (extracted / ENERGY_FOR_HUNGER_POINT).toFloat() * 4f)
val extracted = androidEnergy.extractEnergyInner(ServerConfig.ANDROID_ENERGY_PER_HUNGER_POINT * (stats.exhaustionLevel / 4f), false)
stats.setExhaustion(stats.exhaustionLevel - (extracted / ServerConfig.ANDROID_ENERGY_PER_HUNGER_POINT).toFloat() * 4f)
}
for (feature in features.values) {
@ -653,37 +659,26 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
@Suppress("unused")
companion object {
val UNAFFECTED_EFFECTS = ObjectArraySet<MobEffect>()
fun registerEffects(event: FMLCommonSetupEvent) {
UNAFFECTED_EFFECTS.add(MobEffects.CONDUIT_POWER)
UNAFFECTED_EFFECTS.add(MobEffects.HEAL)
// maybe it makes things go haywire idk
// UNAFFECTED_EFFECTS.add(MobEffects.HARM);
UNAFFECTED_EFFECTS.add(MobEffects.REGENERATION)
UNAFFECTED_EFFECTS.add(MobEffects.WATER_BREATHING)
UNAFFECTED_EFFECTS.add(MobEffects.POISON)
// even skeletons can be withered
// UNAFFECTED_EFFECTS.add(MobEffects.WITHER);
UNAFFECTED_EFFECTS.add(MobEffects.HEALTH_BOOST)
UNAFFECTED_EFFECTS.add(MobEffects.ABSORPTION)
UNAFFECTED_EFFECTS.add(MobEffects.SATURATION)
UNAFFECTED_EFFECTS.add(MobEffects.DOLPHINS_GRACE)
}
val ANDROID_IMMUNE_EFFECTS: TagKey<MobEffect> = TagKey.create(ForgeRegistries.MOB_EFFECTS.registryKey, ResourceLocation(OverdriveThatMatters.MOD_ID, "android_immune_effects"))
fun onPlayerTick(event: PlayerTickEvent) {
if (event.phase == TickEvent.Phase.START)
return
val ent = event.player
if (ent.level.isClientSide) {
ent.getCapability(MatteryCapability.MATTERY_PLAYER).ifPresentK {
it.tickClient()
if (event.phase == TickEvent.Phase.START) {
if (!ent.level.isClientSide) {
ent.getCapability(MatteryCapability.MATTERY_PLAYER).ifPresentK {
it.preTick()
}
}
} else {
ent.getCapability(MatteryCapability.MATTERY_PLAYER).ifPresentK {
it.tick()
if (ent.level.isClientSide) {
ent.getCapability(MatteryCapability.MATTERY_PLAYER).ifPresentK {
it.tickClient()
}
} else {
ent.getCapability(MatteryCapability.MATTERY_PLAYER).ifPresentK {
it.tick()
}
}
}
}
@ -794,7 +789,6 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
event.original.invalidateCaps()
}
val ENERGY_FOR_HUNGER_POINT = ImpreciseFraction(1000)
const val SLEEP_TICKS_LIMIT = 80
private val itemPickupTicks = WeakHashMap<Player, WeakHashMap<ItemEntity, MutableInt>>()
@ -839,7 +833,5 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
}
}
}
val DEFAULT_MAX_ANDROID_POWER = ImpreciseFraction(60000)
}
}

View File

@ -14,7 +14,7 @@ import ru.dbotthepony.mc.otm.client.screen.widget.TallHorizontalPowerGaugePanel
import ru.dbotthepony.mc.otm.menu.EnergyServoMenu
class EnergyServoScreen(menu: EnergyServoMenu, inventory: Inventory, title: Component) : MatteryScreen<EnergyServoMenu>(menu, inventory, title) {
override fun makeMainFrame(): FramePanel<MatteryScreen<*>>? {
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
val frame = FramePanel.padded(this,
width = AbstractSlotPanel.SIZE * 2f + HorizontalPowerGaugePanel.GAUGE_BACKGROUND_TALL.width + 8f + ProgressGaugePanel.GAUGE_BACKGROUND.width * 2f,
AbstractSlotPanel.SIZE, title)

View File

@ -13,7 +13,7 @@ import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
class MatterDecomposerScreen(p_97741_: MatterDecomposerMenu, p_97742_: Inventory, p_97743_: Component) :
MatteryScreen<MatterDecomposerMenu>(p_97741_, p_97742_, p_97743_) {
override fun makeMainFrame(): FramePanel<out MatteryScreen<*>> {
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
val frame = super.makeMainFrame()!!
val m = PowerGaugePanel(this, frame, menu.powerWidget, LEFT_MARGIN, GAUGE_TOP_WITH_SLOT)

View File

@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.core
import com.google.common.collect.ImmutableList
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import java.math.BigDecimal
import java.math.BigInteger
@ -348,7 +347,8 @@ fun ImpreciseFraction.formatSi(decimalPlaces: Int = 2): String {
fun Int.formatSiComponent(suffix: Any = "", decimalPlaces: Int = 2): Component {
require(decimalPlaces >= 0) { "Invalid amount of decimal places required: $decimalPlaces" }
val prefix = determineSiPrefix() ?: return if (suffix == "") TextComponent(toString()) else if (suffix is Component) TextComponent(toString() + " " + suffix.string) else TextComponent(toString() + " " + suffix)
val prefix = determineSiPrefix() ?: return if (suffix == "") TextComponent(toString()) else if (suffix is Component) TextComponent(
toString() + " " + suffix.string) else TextComponent(toString() + " " + suffix)
return TranslatableComponent(prefix.formatLocaleKey, "%f.$decimalPlaces".format(this.toFloat() / prefix.int!!.toFloat()), suffix)
}

View File

@ -874,7 +874,7 @@ class ImpreciseFractionConfigValue(
val minimum: ImpreciseFraction? = null,
val maximum: ImpreciseFraction? = null,
) : ObservedConfigValue<ImpreciseFraction>(parent) {
override fun fromValue(value: String): ImpreciseFraction? {
override fun fromString(value: String): ImpreciseFraction? {
try {
val parsed = ImpreciseFraction(value)
@ -890,7 +890,13 @@ class ImpreciseFractionConfigValue(
}
}
override fun toValue(value: ImpreciseFraction): Pair<String, ImpreciseFraction> {
override fun toString(value: ImpreciseFraction): Pair<String, ImpreciseFraction> {
if (minimum != null && minimum > value) {
return minimum.toString() to minimum
} else if (maximum != null && maximum < value) {
return maximum.toString() to maximum
}
return value.toString() to value
}
}

View File

@ -14,6 +14,7 @@ import net.minecraft.world.item.TooltipFlag
import net.minecraft.world.item.UseAnim
import net.minecraft.world.level.Level
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.ServerConfig
import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.core.TranslatableComponent
@ -46,7 +47,7 @@ class ExoSuitSlotUpgradeItem(
}
}
val alreadyHas = id != null && runIfClient(false) {
val alreadyHas = (id != null && !ServerConfig.INFINITE_EXOSUIT_UPGRADES) && runIfClient(false) {
minecraft.player?.matteryPlayer?.exoSuitSlotCountModifiers?.contains(id) == true
}
@ -60,7 +61,7 @@ class ExoSuitSlotUpgradeItem(
override fun use(p_41432_: Level, player: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
val matteryPlayer = player.matteryPlayer ?: return super.use(p_41432_, player, hand)
if (matteryPlayer.hasExoSuit && (id == null || id !in matteryPlayer.exoSuitSlotCountModifiers)) {
if (matteryPlayer.hasExoSuit && ((id == null || ServerConfig.INFINITE_EXOSUIT_UPGRADES) || id !in matteryPlayer.exoSuitSlotCountModifiers)) {
player.startUsingItem(hand)
return InteractionResultHolder.consume(player.getItemInHand(hand))
}
@ -72,7 +73,7 @@ class ExoSuitSlotUpgradeItem(
if (player !is Player) return super.finishUsingItem(itemStack, level, player)
val matteryPlayer = player.matteryPlayer ?: return super.finishUsingItem(itemStack, level, player)
if (!matteryPlayer.hasExoSuit || (id != null && id in matteryPlayer.exoSuitSlotCountModifiers)) {
if (!matteryPlayer.hasExoSuit || (!ServerConfig.INFINITE_EXOSUIT_UPGRADES && id != null && id in matteryPlayer.exoSuitSlotCountModifiers)) {
return super.finishUsingItem(itemStack, level, player)
}
@ -81,7 +82,11 @@ class ExoSuitSlotUpgradeItem(
if (player is ServerPlayer) {
if (id != null) {
matteryPlayer.exoSuitSlotCountModifiers[id] = slotCount
if (ServerConfig.INFINITE_EXOSUIT_UPGRADES && id in matteryPlayer.exoSuitSlotCountModifiers) {
matteryPlayer.exoSuitSlotCountModifiers[UUID.randomUUID()] = slotCount
} else {
matteryPlayer.exoSuitSlotCountModifiers[id] = slotCount
}
} else {
matteryPlayer.exoSuitSlotCountModifiers[UUID.randomUUID()] = slotCount
}

View File

@ -24,6 +24,128 @@ class LazyList<T> : AbstractList<T> {
}
}
class ConditionalSet<T> : AbstractSet<T> {
// method without boxing
fun interface Condition {
fun check(): Boolean
}
private val getters: Array<Pair<Condition, T>>
constructor(vararg getters: Pair<Condition, T>) : super() {
this.getters = Array(getters.size) { getters[it] }
}
constructor(getters: List<Pair<Condition, T>>) : super() {
this.getters = Array(getters.size) { getters[it] }
}
override val size: Int get() {
var i = 0
for (pair in getters) {
if (pair.first.check()) {
i++
}
}
return i
}
override fun iterator(): Iterator<T> {
return object : Iterator<T> {
val parent = getters.iterator()
private var pair: Pair<Condition, T>? = null
private fun search() {
for (pair in parent) {
if (pair.first.check()) {
this.pair = pair
return
}
}
this.pair = null
}
init {
search()
}
override fun hasNext(): Boolean {
return pair != null
}
override fun next(): T {
val pair = pair ?: throw NoSuchElementException()
search()
return pair.second
}
}
}
}
class ConditionalLazySet<T> : AbstractSet<T> {
// method without boxing
fun interface Condition {
fun check(): Boolean
}
private val getters: Array<Pair<Condition, () -> T>>
constructor(vararg getters: Pair<Condition, () -> T>) : super() {
this.getters = Array(getters.size) { getters[it] }
}
constructor(getters: List<Pair<Condition, () -> T>>) : super() {
this.getters = Array(getters.size) { getters[it] }
}
override val size: Int get() {
var i = 0
for (pair in getters) {
if (pair.first.check()) {
i++
}
}
return i
}
override fun iterator(): Iterator<T> {
return object : Iterator<T> {
val parent = getters.iterator()
private var pair: Pair<Condition, () -> T>? = null
private fun search() {
for (pair in parent) {
if (pair.first.check()) {
this.pair = pair
return
}
}
this.pair = null
}
init {
search()
}
override fun hasNext(): Boolean {
return pair != null
}
override fun next(): T {
val pair = pair ?: throw NoSuchElementException()
search()
return pair.second.invoke()
}
}
}
}
class LazyMap<K, T> : AbstractMap<K, T> {
override val entries: Set<Map.Entry<K, T>>

View File

@ -30,6 +30,7 @@ import ru.dbotthepony.mc.otm.registry.objects.CrateProperties
import ru.dbotthepony.mc.otm.registry.objects.DecorativeBlock
import ru.dbotthepony.mc.otm.registry.objects.StripedColoredDecorativeBlock
import java.util.function.Supplier
import kotlin.properties.ReadOnlyProperty
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
@ -41,15 +42,19 @@ fun <T> Registry<T>.register(key: ResourceLocation, value: T): Holder<T> {
return (this as WritableRegistry<T>).register(ResourceKey.create(key(), key), value, Lifecycle.stable())
}
private class RegistryDelegate<T>(key: String) {
private class RegistryDelegate<T>(key: String) : ReadOnlyProperty<Any, ForgeRegistry<T>> {
private var value: Supplier<IForgeRegistry<T>?>? = null
val location = ResourceLocation(OverdriveThatMatters.MOD_ID, key)
val key: ResourceKey<Registry<T>> = ResourceKey.createRegistryKey(location)
fun get(): IForgeRegistry<T> {
fun get(): ForgeRegistry<T> {
val supp = value ?: throw IllegalStateException("Tried to access uninitialized registry $location")
return supp.get() ?: throw IllegalStateException("Accessing registry $location too early")
return supp.get() as ForgeRegistry<T>? ?: throw IllegalStateException("Accessing registry $location too early")
}
override fun getValue(thisRef: Any, property: KProperty<*>): ForgeRegistry<T> {
return get()
}
fun build(event: NewRegistryEvent) {
@ -82,8 +87,8 @@ object MRegistry {
private val features = RegistryDelegate<AndroidFeatureType<*>>("android_features")
private val research = RegistryDelegate<AndroidResearchType<*>>("android_research")
val ANDROID_FEATURES get() = features.get() as ForgeRegistry<AndroidFeatureType<*>>
val ANDROID_RESEARCH get() = research.get() as ForgeRegistry<AndroidResearchType<*>>
val ANDROID_FEATURES by features
val ANDROID_RESEARCH by research
val ANDROID_FEATURES_LOCATION get() = features.location
val ANDROID_RESEARCH_LOCATION get() = research.location