Procedural batteries in dungeons!

This commit is contained in:
DBotThePony 2023-05-06 19:55:02 +07:00
parent 1a7f88211b
commit 738479b6ee
Signed by: DBot
GPG Key ID: DCC23B5715498507
12 changed files with 321 additions and 12 deletions

View File

@ -453,6 +453,9 @@ private fun blocks(provider: MatteryLanguageProvider) {
private fun items(provider: MatteryLanguageProvider) {
with(provider.english) {
add(MItems.PROCEDURAL_BATTERY, "Mythical Battery")
add(MItems.PROCEDURAL_BATTERY, "desc", "These batteries are found in dungeons with randomized stats")
add(MItems.EXPLOSIVE_HAMMER, "Explosive Hammer")
add(MItems.EXPLOSIVE_HAMMER, "desc", "For those who feel bored")
add(MItems.EXPLOSIVE_HAMMER, "primed", "Primed!")

View File

@ -458,6 +458,9 @@ private fun blocks(provider: MatteryLanguageProvider) {
private fun items(provider: MatteryLanguageProvider) {
with(provider.russian) {
add(MItems.PROCEDURAL_BATTERY, "Загадочный аккумулятор")
add(MItems.PROCEDURAL_BATTERY, "desc", "Данные аккумуляторы можно найти в подземельях, со случайными характеристиками")
add(MItems.EXPLOSIVE_HAMMER, "Молоток-убийца")
add(MItems.EXPLOSIVE_HAMMER, "desc", "Для тех, кому стало скучно")
add(MItems.EXPLOSIVE_HAMMER, "primed", "Заряжен!")

View File

@ -28,6 +28,7 @@ inline fun LootTable.Builder.singleItem(item: ItemLike, configurator: LootPoolSi
inline fun lootPool(configurator: LootPool.Builder.() -> Unit): LootPool = LootPool.lootPool().also(configurator).build()
inline fun singleItem(item: ItemLike, configurator: LootPoolSingletonContainer.Builder<*>.() -> Unit): LootPool = lootPool { item(item, configurator) }
fun singleRandomizedItem(item: ItemLike, rarity: Rarity = Rarity.COMMON, chance: Double? = null): LootPool {
return lootPool {
item(item) {

View File

@ -7,12 +7,15 @@ import net.minecraft.world.item.Rarity
import net.minecraft.world.level.storage.loot.BuiltInLootTables
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
import net.minecraftforge.common.loot.LootTableIdCondition
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.data.UniformDecimal
import ru.dbotthepony.mc.otm.data.condition.ChanceWithPlaytimeCondition
import ru.dbotthepony.mc.otm.data.condition.HasExoPackCondition
import ru.dbotthepony.mc.otm.data.condition.ItemInInventoryCondition
import ru.dbotthepony.mc.otm.data.condition.KilledByRealPlayerOrIndirectly
import ru.dbotthepony.mc.otm.data.loot.LootPoolAppender
import ru.dbotthepony.mc.otm.data.loot.RandomizerFunction
import ru.dbotthepony.mc.otm.item.ProceduralBatteryItem
import ru.dbotthepony.mc.otm.registry.MItems
@Suppress("FunctionName")
@ -25,7 +28,7 @@ fun LootTableIdCondition(location: ResourceLocation): LootItemCondition {
return LootTableIdCondition.Builder(location).build()
}
private fun exosuitModifiers(it: LootModifiers) {
fun addLootModifiers(it: LootModifiers) {
it.add("dungeon_exosuit", LootPoolAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.SIMPLE_DUNGEON)),
@ -40,10 +43,20 @@ private fun exosuitModifiers(it: LootModifiers) {
},
))
it.add("mineshaft_exosuit", LootPoolAppender(
it.add("mineshaft_additions", LootPoolAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.ABANDONED_MINESHAFT)),
singleRandomizedItem(MItems.ExopackUpgrades.INVENTORY_UPGRADE_PROCEDURAL, chance = 0.1),
singleRandomizedItem(MItems.ExopackUpgrades.INVENTORY_UPGRADE_PROCEDURAL, chance = 0.1, rarity = Rarity.UNCOMMON)
singleRandomizedItem(MItems.ExopackUpgrades.INVENTORY_UPGRADE_PROCEDURAL, chance = 0.1, rarity = Rarity.UNCOMMON),
singleItem(MItems.PROCEDURAL_BATTERY) {
chanceCondition(0.15)
apply(ProceduralBatteryItem.Randomizer(
maxBatteryLevel = UniformDecimal(Decimal(8_000_000), Decimal(40_000_000)),
batteryLevel = UniformDecimal(Decimal(0), Decimal(20_000_000)),
maxInput = UniformDecimal(Decimal(700), Decimal(4_000)),
))
}
))
it.add("desert_pyramid_exosuit", LootPoolAppender(
@ -64,10 +77,6 @@ private fun exosuitModifiers(it: LootModifiers) {
singleRandomizedItem(MItems.ExopackUpgrades.INVENTORY_UPGRADE_PROCEDURAL, chance = 0.2, rarity = Rarity.RARE),
singleRandomizedItem(MItems.ExopackUpgrades.INVENTORY_UPGRADE_PROCEDURAL, chance = 0.15, rarity = Rarity.EPIC),
))
}
fun addLootModifiers(it: LootModifiers) {
exosuitModifiers(it)
it.add("dungeon_pill", PlainLootAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.SIMPLE_DUNGEON)),

View File

@ -0,0 +1,21 @@
package ru.dbotthepony.mc.otm.data
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonObject
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSyntaxException
import com.mojang.serialization.Codec
import net.minecraft.world.level.storage.loot.Serializer
import ru.dbotthepony.mc.otm.core.fromJsonStrict
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.core.toJsonStrict
class Codec2Serializer<T : Any>(val codec: Codec<T>) : Serializer<T> {
override fun serialize(p_79325_: JsonObject, p_79326_: T, p_79327_: JsonSerializationContext) {
p_79325_["value"] = codec.toJsonStrict(p_79326_)
}
override fun deserialize(p_79323_: JsonObject, p_79324_: JsonDeserializationContext): T {
return codec.fromJsonStrict(p_79323_["value"] ?: throw JsonSyntaxException("Missing 'value' element"))
}
}

View File

@ -0,0 +1,23 @@
package ru.dbotthepony.mc.otm.data
import com.mojang.datafixers.util.Pair
import com.mojang.serialization.Codec
import com.mojang.serialization.DataResult
import com.mojang.serialization.DynamicOps
import ru.dbotthepony.mc.otm.core.math.Decimal
object DecimalCodec : Codec<Decimal> {
override fun <T : Any> encode(input: Decimal, ops: DynamicOps<T>, prefix: T): DataResult<T> {
return DataResult.success(ops.createString(input.toString()))
}
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<Decimal, T>> {
return ops.getStringValue(input).flatMap {
try {
DataResult.success(Pair(Decimal(it), ops.empty()))
} catch (err: NumberFormatException) {
DataResult.error { "Not a number: $it" }
}
}
}
}

View File

@ -0,0 +1,117 @@
package ru.dbotthepony.mc.otm.data
import com.mojang.datafixers.util.Either
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.RandomSource
import net.minecraftforge.eventbus.api.IEventBus
import net.minecraftforge.registries.DeferredRegister
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.nextDecimal
import ru.dbotthepony.mc.otm.registry.RegistryDelegate
fun interface SampledDecimal {
fun sample(source: RandomSource): Decimal
}
abstract class DecimalProvider : SampledDecimal {
interface Type<T : DecimalProvider> {
val codec: Codec<T>
}
abstract val minValue: Decimal
abstract val maxValue: Decimal
abstract val type: Type<*>
companion object {
private val registryHolder = RegistryDelegate<Type<*>>("decimal_provider_type") {
setDefaultKey(ResourceLocation(OverdriveThatMatters.MOD_ID, "zero"))
}
val CODEC: Codec<DecimalProvider> by lazy {
Codec
.either(DecimalCodec, registry.codec.dispatch({ it.type }, { it.codec }))
.xmap(
{ c -> c.map(::ConstantDecimal, { it }) },
{ if (it.type === ConstantDecimal.Companion) Either.left(it.minValue) else Either.right(it) }
)
}
val registry by registryHolder
val registryKey get() = registryHolder.key
private val registror = DeferredRegister.create(registryKey, OverdriveThatMatters.MOD_ID)
init {
registror.register("zero") { ConstantDecimal.Zero }
registror.register("constant") { ConstantDecimal.Companion }
registror.register("uniform") { UniformDecimal.Companion }
}
internal fun register(bus: IEventBus) {
bus.addListener(registryHolder::build)
registror.register(bus)
}
}
}
class ConstantDecimal(val value: Decimal) : DecimalProvider() {
object Zero : DecimalProvider(), Type<Zero> {
override fun sample(source: RandomSource): Decimal {
return Decimal.ZERO
}
override val codec: Codec<Zero> = Codec.unit(this)
override val minValue: Decimal
get() = Decimal.ZERO
override val maxValue: Decimal
get() = Decimal.ZERO
override val type: Type<*>
get() = this
}
override fun sample(source: RandomSource): Decimal {
return value
}
override val minValue: Decimal
get() = value
override val maxValue: Decimal
get() = value
override val type: Type<*>
get() = Companion
companion object : Type<ConstantDecimal> {
override val codec: Codec<ConstantDecimal> = RecordCodecBuilder.create {
it.group(DecimalCodec.fieldOf("value").forGetter(ConstantDecimal::value)).apply(it, ::ConstantDecimal)
}
}
}
class UniformDecimal(override val minValue: Decimal, override val maxValue: Decimal, val round: Boolean = true) : DecimalProvider() {
constructor(range: ClosedRange<Decimal>, round: Boolean = true) : this(range.start, range.endInclusive, round)
init {
require(minValue <= maxValue) { "Invalid range provided: $minValue .. $maxValue" }
}
override fun sample(source: RandomSource): Decimal {
return source.nextDecimal(minValue, maxValue, round = round)
}
override val type: Type<*>
get() = Companion
companion object : Type<UniformDecimal> {
override val codec: Codec<UniformDecimal> = RecordCodecBuilder.create {
it.group(
DecimalCodec.fieldOf("minValue").forGetter(UniformDecimal::minValue),
DecimalCodec.fieldOf("maxValue").forGetter(UniformDecimal::maxValue),
Codec.BOOL.optionalFieldOf("round", true).forGetter(UniformDecimal::round)
).apply(it, ::UniformDecimal)
}
}
}

View File

@ -0,0 +1,126 @@
package ru.dbotthepony.mc.otm.item
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.ChatFormatting
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.TooltipFlag
import net.minecraft.world.level.Level
import net.minecraft.world.level.storage.loot.LootContext
import net.minecraft.world.level.storage.loot.functions.LootItemFunction
import net.minecraft.world.level.storage.loot.functions.LootItemFunctionType
import net.minecraftforge.common.capabilities.ICapabilityProvider
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.energy.ItemEnergyStorageImpl
import ru.dbotthepony.mc.otm.capability.energy.batteryLevel
import ru.dbotthepony.mc.otm.capability.matteryEnergy
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.set
import ru.dbotthepony.mc.otm.core.nbt.mapPresent
import ru.dbotthepony.mc.otm.core.tagNotNull
import ru.dbotthepony.mc.otm.data.Codec2Serializer
import ru.dbotthepony.mc.otm.data.DecimalProvider
import ru.dbotthepony.mc.otm.registry.MItemFunctionTypes
import java.util.Optional
class ProceduralBatteryItem : Item(Properties().stacksTo(1)) {
class Cap(stack: ItemStack) : ItemEnergyStorageImpl(stack) {
override var maxInput: Decimal
get() = itemStack.tag?.mapPresent("maxInput", Decimal.Companion::deserializeNBT) ?: Decimal.ZERO
set(value) { itemStack.tagNotNull["maxInput"] = value }
override var maxOutput: Decimal
get() = itemStack.tag?.mapPresent("maxOutput", Decimal.Companion::deserializeNBT) ?: Decimal.ZERO
set(value) { itemStack.tagNotNull["maxOutput"] = value }
override var maxBatteryLevel: Decimal
get() = itemStack.tag?.mapPresent("maxBatteryLevel", Decimal.Companion::deserializeNBT) ?: Decimal.ZERO
set(value) { itemStack.tagNotNull["maxBatteryLevel"] = value }
override val energyFlow: FlowDirection
get() = FlowDirection.BI_DIRECTIONAL
override val initialBatteryLevel: Decimal
get() = maxBatteryLevel
}
override fun appendHoverText(p_41421_: ItemStack, p_41422_: Level?, p_41423_: MutableList<Component>, p_41424_: TooltipFlag) {
super.appendHoverText(p_41421_, p_41422_, p_41423_, p_41424_)
val energy = p_41421_.matteryEnergy
if (energy is Cap) {
if (energy.maxInput.isZero && energy.maxOutput.isZero && energy.maxBatteryLevel.isZero) {
p_41423_.add(TranslatableComponent("$descriptionId.desc").withStyle(ChatFormatting.GRAY))
} else {
batteryLevel(energy, p_41423_)
}
}
}
data class Randomizer(
val maxBatteryLevel: DecimalProvider,
val batteryLevel: Optional<DecimalProvider>,
val maxInput: DecimalProvider,
val maxOutput: Optional<DecimalProvider>
) : LootItemFunction, LootItemFunction.Builder {
constructor(
maxBatteryLevel: DecimalProvider,
batteryLevel: DecimalProvider? = null,
maxInput: DecimalProvider,
maxOutput: DecimalProvider? = null
) : this(maxBatteryLevel, Optional.ofNullable(batteryLevel), maxInput, Optional.ofNullable(maxOutput))
override fun apply(t: ItemStack, u: LootContext): ItemStack {
val data = Cap(t)
if (maxOutput.isPresent) {
data.maxInput = maxInput.sample(u.random)
data.maxOutput = maxOutput.get().sample(u.random)
} else {
data.maxInput = maxInput.sample(u.random)
data.maxOutput = data.maxInput
}
data.maxBatteryLevel = maxBatteryLevel.sample(u.random)
if (batteryLevel.isPresent) {
data.batteryLevel = batteryLevel.get().sample(u.random).coerceAtMost(data.maxBatteryLevel)
} else {
data.batteryLevel = data.maxBatteryLevel
}
return t
}
override fun getType(): LootItemFunctionType {
return MItemFunctionTypes.PROCEDURAL_BATTERY
}
override fun build(): LootItemFunction {
return this
}
companion object {
val SERIALIZER by lazy {
Codec2Serializer<Randomizer>(
RecordCodecBuilder.create {
it.group(
DecimalProvider.CODEC.fieldOf("maxBatteryLevel").forGetter(Randomizer::maxBatteryLevel),
DecimalProvider.CODEC.optionalFieldOf("batteryLevel").forGetter(Randomizer::batteryLevel),
DecimalProvider.CODEC.fieldOf("maxInput").forGetter(Randomizer::maxInput),
DecimalProvider.CODEC.optionalFieldOf("maxOutput").forGetter(Randomizer::maxOutput),
).apply(it, ::Randomizer)
}
)
}
}
}
override fun initCapabilities(stack: ItemStack, nbt: CompoundTag?): ICapabilityProvider {
return Cap(stack)
}
}

View File

@ -1,6 +1,5 @@
package ru.dbotthepony.mc.otm.registry
import net.minecraft.core.Registry
import net.minecraft.core.registries.Registries
import net.minecraft.world.level.storage.loot.functions.LootItemFunctionType
import net.minecraftforge.eventbus.api.IEventBus
@ -8,12 +7,14 @@ import net.minecraftforge.registries.DeferredRegister
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.data.loot.CopyTileNbtFunction
import ru.dbotthepony.mc.otm.data.loot.RandomizerFunction
import ru.dbotthepony.mc.otm.item.ProceduralBatteryItem
object MItemFunctionTypes {
private val registry = DeferredRegister.create(Registries.LOOT_FUNCTION_TYPE, OverdriveThatMatters.MOD_ID)
val RANDOMIZER: LootItemFunctionType by registry.register("randomizer") { LootItemFunctionType(RandomizerFunction.Companion) }
val COPY_TILE_NBT: LootItemFunctionType by registry.register("copy_tile_nbt") { LootItemFunctionType(CopyTileNbtFunction.Companion) }
val PROCEDURAL_BATTERY: LootItemFunctionType by registry.register(MNames.PROCEDURAL_BATTERY) { LootItemFunctionType(ProceduralBatteryItem.Randomizer.SERIALIZER) }
internal fun register(bus: IEventBus) {
registry.register(bus)

View File

@ -289,10 +289,11 @@ object MItems {
val BATTERY_CAPACITOR: Item by registry.register(MNames.BATTERY_CAPACITOR) { BatteryItem(ItemsConfig.Batteries.CAPACITOR) }
val BATTERY_CREATIVE: Item by registry.register(MNames.BATTERY_CREATIVE) { BatteryItem() }
val QUANTUM_BATTERY: Item by registry.register(MNames.QUANTUM_BATTERY) { QuantumBatteryItem(MNames.QUANTUM_BATTERY, ItemsConfig.Batteries.QUANTUM_BATTERY) }
val QUANTUM_CAPACITOR: Item by registry.register(MNames.QUANTUM_CAPACITOR) { QuantumBatteryItem(MNames.QUANTUM_CAPACITOR, ItemsConfig.Batteries.QUANTUM_CAPACITOR) }
val QUANTUM_BATTERY_CREATIVE: Item by registry.register(MNames.QUANTUM_BATTERY_CREATIVE) { QuantumBatteryItem(MNames.QUANTUM_BATTERY_CREATIVE) }
val ZPM_BATTERY: Item by registry.register(MNames.ZPM_BATTERY) { ZPMItem() }
val QUANTUM_BATTERY: QuantumBatteryItem by registry.register(MNames.QUANTUM_BATTERY) { QuantumBatteryItem(MNames.QUANTUM_BATTERY, ItemsConfig.Batteries.QUANTUM_BATTERY) }
val QUANTUM_CAPACITOR: QuantumBatteryItem by registry.register(MNames.QUANTUM_CAPACITOR) { QuantumBatteryItem(MNames.QUANTUM_CAPACITOR, ItemsConfig.Batteries.QUANTUM_CAPACITOR) }
val QUANTUM_BATTERY_CREATIVE: QuantumBatteryItem by registry.register(MNames.QUANTUM_BATTERY_CREATIVE) { QuantumBatteryItem(MNames.QUANTUM_BATTERY_CREATIVE) }
val ZPM_BATTERY: ZPMItem by registry.register(MNames.ZPM_BATTERY) { ZPMItem() }
val PROCEDURAL_BATTERY: ProceduralBatteryItem by registry.register(MNames.PROCEDURAL_BATTERY) { ProceduralBatteryItem() }
val BATTERIES = SupplierList(
::BATTERY_CRUDE,

View File

@ -111,6 +111,7 @@ object MNames {
const val PATTERN_DRIVE_CREATIVE2 = "pattern_drive_creative2"
const val ZPM_BATTERY = "zpm_battery"
const val PROCEDURAL_BATTERY = "procedural_battery"
const val NUTRIENT_PASTE = "nutrient_paste"

View File

@ -27,6 +27,7 @@ import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock
import ru.dbotthepony.mc.otm.block.decorative.TritaniumPressurePlate
import ru.dbotthepony.mc.otm.capability.matteryEnergy
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.data.DecimalProvider
import ru.dbotthepony.mc.otm.item.weapon.EnergySwordItem
import ru.dbotthepony.mc.otm.registry.objects.ColoredDecorativeBlock
import ru.dbotthepony.mc.otm.registry.objects.DecorativeBlock
@ -193,6 +194,8 @@ object MRegistry {
bus.addListener(this::initializeCommon)
bus.addListener(MStats::registerVanilla)
DecimalProvider.register(bus)
MBlocks.register(bus)
MBlockEntities.register(bus)
MEntityTypes.register(bus)