Move everything to lootpools

Fixes #191
This commit is contained in:
DBotThePony 2022-10-27 15:57:35 +07:00
parent 228cc3eab5
commit 4dc132a8ec
Signed by: DBot
GPG Key ID: DCC23B5715498507
15 changed files with 205 additions and 249 deletions

View File

@ -2,6 +2,7 @@ package ru.dbotthepony.mc.otm.datagen.loot
import net.minecraft.advancements.critereon.StatePropertiesPredicate import net.minecraft.advancements.critereon.StatePropertiesPredicate
import net.minecraft.util.StringRepresentable import net.minecraft.util.StringRepresentable
import net.minecraft.world.item.Rarity
import net.minecraft.world.level.ItemLike import net.minecraft.world.level.ItemLike
import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.properties.Property import net.minecraft.world.level.block.state.properties.Property
@ -16,10 +17,28 @@ import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue import net.minecraft.world.level.storage.loot.providers.number.ConstantValue
import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator
import ru.dbotthepony.mc.otm.data.condition.ChanceCondition import ru.dbotthepony.mc.otm.data.condition.ChanceCondition
import ru.dbotthepony.mc.otm.data.loot.RandomizerFunction
inline fun LootTable.Builder.lootPool(configurator: LootPool.Builder.() -> Unit): LootTable.Builder = withPool(LootPool.lootPool().also(configurator)) inline fun LootTable.Builder.lootPool(configurator: LootPool.Builder.() -> Unit): LootTable.Builder = withPool(LootPool.lootPool().also(configurator))
inline fun LootTable.Builder.singleItem(item: ItemLike, configurator: LootPoolSingletonContainer.Builder<*>.() -> Unit): LootTable.Builder {
return withPool(LootPool.lootPool().also {
it.item(item, configurator)
})
}
inline fun lootPool(configurator: LootPool.Builder.() -> Unit): LootPool.Builder = LootPool.lootPool().also(configurator) 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) {
apply(RandomizerFunction.valueOf(rarity))
if (chance != null) {
chanceCondition(chance)
}
}
}
}
inline fun LootPool.Builder.item(item: ItemLike, configurator: LootPoolSingletonContainer.Builder<*>.() -> Unit) { inline fun LootPool.Builder.item(item: ItemLike, configurator: LootPoolSingletonContainer.Builder<*>.() -> Unit) {
add(LootItem.lootTableItem(item).also(configurator)) add(LootItem.lootTableItem(item).also(configurator))
@ -43,6 +62,11 @@ fun <T : LootPoolSingletonContainer.Builder<*>> T.setCount(minimal: Int, maximal
return this return this
} }
fun <T : LootPoolSingletonContainer.Builder<*>> T.chanceCondition(chance: Double): T {
condition(ChanceCondition(chance))
return this
}
fun <T : LootPoolSingletonContainer.Builder<*>> T.setCount(minimal: Float, maximal: Float, configurator: LootItemConditionalFunction.Builder<*>.() -> Unit = {}): T { fun <T : LootPoolSingletonContainer.Builder<*>> T.setCount(minimal: Float, maximal: Float, configurator: LootItemConditionalFunction.Builder<*>.() -> Unit = {}): T {
apply(SetItemCountFunction.setCount(UniformGenerator.between(minimal, maximal)).also(configurator)) apply(SetItemCountFunction.setCount(UniformGenerator.between(minimal, maximal)).also(configurator))
return this return this

View File

@ -19,7 +19,7 @@ fun BasicLootAppender(
item(it.item) { item(it.item) {
setCount(it.count) setCount(it.count)
} }
}.build() }
}) })
} }
@ -41,7 +41,7 @@ fun PlainLootAppender(
} }
chanceCondition(it.second) chanceCondition(it.second)
}.build() }
}) })
} }

View File

@ -6,13 +6,14 @@ import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Rarity import net.minecraft.world.item.Rarity
import net.minecraft.world.level.storage.loot.BuiltInLootTables import net.minecraft.world.level.storage.loot.BuiltInLootTables
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
import net.minecraftforge.common.loot.LootModifier
import net.minecraftforge.common.loot.LootTableIdCondition import net.minecraftforge.common.loot.LootTableIdCondition
import ru.dbotthepony.mc.otm.data.condition.ChanceWithPlaytimeCondition import ru.dbotthepony.mc.otm.data.condition.ChanceWithPlaytimeCondition
import ru.dbotthepony.mc.otm.data.condition.HasExosuitCondition import ru.dbotthepony.mc.otm.data.condition.HasExosuitCondition
import ru.dbotthepony.mc.otm.data.loot.IRandomizableItem
import ru.dbotthepony.mc.otm.data.condition.ItemInInventoryCondition import ru.dbotthepony.mc.otm.data.condition.ItemInInventoryCondition
import ru.dbotthepony.mc.otm.data.condition.KilledByRealPlayerOrIndirectly import ru.dbotthepony.mc.otm.data.condition.KilledByRealPlayerOrIndirectly
import ru.dbotthepony.mc.otm.data.loot.RandomizableItemLootAppender import ru.dbotthepony.mc.otm.data.loot.LootPoolAppender
import ru.dbotthepony.mc.otm.data.loot.RandomizerFunction
import ru.dbotthepony.mc.otm.registry.MItems import ru.dbotthepony.mc.otm.registry.MItems
@Suppress("FunctionName") @Suppress("FunctionName")
@ -25,7 +26,50 @@ fun LootTableIdCondition(location: ResourceLocation): LootItemCondition {
return LootTableIdCondition.Builder(location).build() return LootTableIdCondition.Builder(location).build()
} }
private fun exosuitModifiers(it: LootModifiers) {
it.add("dungeon_exosuit", LootPoolAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.SIMPLE_DUNGEON)),
singleItem(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL) {
chanceCondition(0.2)
apply(RandomizerFunction.COMMON)
},
singleItem(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL) {
chanceCondition(0.05)
apply(RandomizerFunction.UNCOMMON)
},
))
it.add("mineshaft_exosuit", LootPoolAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.ABANDONED_MINESHAFT)),
singleRandomizedItem(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL, chance = 0.1),
singleRandomizedItem(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL, chance = 0.1, rarity = Rarity.UNCOMMON)
))
it.add("desert_pyramid_exosuit", LootPoolAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.DESERT_PYRAMID)),
singleRandomizedItem(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL, chance = 0.15, rarity = Rarity.UNCOMMON),
singleRandomizedItem(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL, chance = 0.25, rarity = Rarity.COMMON)
))
it.add("jungle_temple_exosuit", LootPoolAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.JUNGLE_TEMPLE)),
singleRandomizedItem(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL, chance = 0.15, rarity = Rarity.UNCOMMON),
singleRandomizedItem(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL, chance = 0.35, rarity = Rarity.RARE)
))
it.add("end_city_exosuit", LootPoolAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.END_CITY_TREASURE)),
singleRandomizedItem(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL, chance = 0.4, rarity = Rarity.UNCOMMON),
singleRandomizedItem(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL, chance = 0.2, rarity = Rarity.RARE),
singleRandomizedItem(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL, chance = 0.15, rarity = Rarity.EPIC),
))
}
fun addLootModifiers(it: LootModifiers) { fun addLootModifiers(it: LootModifiers) {
exosuitModifiers(it)
it.add("dungeon_pill", PlainLootAppender( it.add("dungeon_pill", PlainLootAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.SIMPLE_DUNGEON)), arrayOf(LootTableIdCondition(BuiltInLootTables.SIMPLE_DUNGEON)),
ItemStack(MItems.PILL_ANDROID, 1) to 0.4, ItemStack(MItems.PILL_ANDROID, 1) to 0.4,
@ -33,15 +77,6 @@ fun addLootModifiers(it: LootModifiers) {
ItemStack(MItems.PILL_HEAL, 1) to 0.75, ItemStack(MItems.PILL_HEAL, 1) to 0.75,
)) ))
it.add("dungeon_exosuit", RandomizableItemLootAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.SIMPLE_DUNGEON)),
listOf(
RandomizableItemLootAppender.Entry(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL as IRandomizableItem, chance = 0.2),
RandomizableItemLootAppender.Entry(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL as IRandomizableItem, chance = 0.05, rarity = Rarity.UNCOMMON)
)
)
)
it.add("mineshaft_pill", PlainLootAppender( it.add("mineshaft_pill", PlainLootAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.ABANDONED_MINESHAFT)), arrayOf(LootTableIdCondition(BuiltInLootTables.ABANDONED_MINESHAFT)),
ItemStack(MItems.PILL_ANDROID, 1) to 0.075, ItemStack(MItems.PILL_ANDROID, 1) to 0.075,
@ -49,15 +84,6 @@ fun addLootModifiers(it: LootModifiers) {
ItemStack(MItems.PILL_HEAL, 1) to 0.4, ItemStack(MItems.PILL_HEAL, 1) to 0.4,
)) ))
it.add("mineshaft_exosuit", RandomizableItemLootAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.ABANDONED_MINESHAFT)),
listOf(
RandomizableItemLootAppender.Entry(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL as IRandomizableItem, chance = 0.1),
RandomizableItemLootAppender.Entry(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL as IRandomizableItem, chance = 0.01, rarity = Rarity.UNCOMMON)
)
)
)
it.add("mineshaft_nutrient_paste", PlainLootAppender( it.add("mineshaft_nutrient_paste", PlainLootAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.ABANDONED_MINESHAFT)), arrayOf(LootTableIdCondition(BuiltInLootTables.ABANDONED_MINESHAFT)),
ItemStack(MItems.NUTRIENT_PASTE, 6) to 0.5, ItemStack(MItems.NUTRIENT_PASTE, 6) to 0.5,
@ -71,29 +97,11 @@ fun addLootModifiers(it: LootModifiers) {
ItemStack(MItems.PILL_HEAL, 1) to 0.3, ItemStack(MItems.PILL_HEAL, 1) to 0.3,
)) ))
it.add("desert_pyramid_exosuit", RandomizableItemLootAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.DESERT_PYRAMID)),
listOf(
RandomizableItemLootAppender.Entry(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL as IRandomizableItem, chance = 0.15, rarity = Rarity.UNCOMMON),
RandomizableItemLootAppender.Entry(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL as IRandomizableItem, chance = 0.25, rarity = Rarity.COMMON),
)
)
)
it.add("jungle_temple_pill", PlainLootAppender( it.add("jungle_temple_pill", PlainLootAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.JUNGLE_TEMPLE)), arrayOf(LootTableIdCondition(BuiltInLootTables.JUNGLE_TEMPLE)),
ItemStack(MItems.PILL_ANDROID, 1) to 0.5 ItemStack(MItems.PILL_ANDROID, 1) to 0.5
)) ))
it.add("jungle_temple_exosuit", RandomizableItemLootAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.JUNGLE_TEMPLE)),
listOf(
RandomizableItemLootAppender.Entry(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL as IRandomizableItem, chance = 0.15, rarity = Rarity.UNCOMMON),
RandomizableItemLootAppender.Entry(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL as IRandomizableItem, chance = 0.35, rarity = Rarity.RARE)
)
)
)
it.add("end_city_modifications", PlainLootAppender( it.add("end_city_modifications", PlainLootAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.END_CITY_TREASURE)), arrayOf(LootTableIdCondition(BuiltInLootTables.END_CITY_TREASURE)),
ItemStack(MItems.PILL_ANDROID, 1) to 0.1, ItemStack(MItems.PILL_ANDROID, 1) to 0.1,
@ -102,16 +110,6 @@ fun addLootModifiers(it: LootModifiers) {
ItemStack(MItems.ZPM_BATTERY, 1) to 0.005, ItemStack(MItems.ZPM_BATTERY, 1) to 0.005,
)) ))
it.add("end_city_exosuit", RandomizableItemLootAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.END_CITY_TREASURE)),
listOf(
RandomizableItemLootAppender.Entry(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL as IRandomizableItem, chance = 0.4, rarity = Rarity.UNCOMMON),
RandomizableItemLootAppender.Entry(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL as IRandomizableItem, chance = 0.2, rarity = Rarity.RARE),
RandomizableItemLootAppender.Entry(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_PROCEDURAL as IRandomizableItem, chance = 0.15, rarity = Rarity.EPIC),
)
)
)
it.add("shipwreck_supply_pill", PlainLootAppender( it.add("shipwreck_supply_pill", PlainLootAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.SHIPWRECK_SUPPLY)), arrayOf(LootTableIdCondition(BuiltInLootTables.SHIPWRECK_SUPPLY)),
ItemStack(MItems.PILL_HUMANE, 1) to 0.4, ItemStack(MItems.PILL_HUMANE, 1) to 0.4,

View File

@ -11,6 +11,7 @@ import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType
import ru.dbotthepony.mc.otm.capability.matteryPlayer import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.core.set import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.data.get
import ru.dbotthepony.mc.otm.registry.MLootItemConditions import ru.dbotthepony.mc.otm.registry.MLootItemConditions
data class ChanceWithPlaytimeCondition( data class ChanceWithPlaytimeCondition(

View File

@ -10,6 +10,7 @@ import net.minecraft.world.level.storage.loot.predicates.InvertedLootItemConditi
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType
import ru.dbotthepony.mc.otm.capability.matteryPlayer import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.data.get
import ru.dbotthepony.mc.otm.registry.MLootItemConditions import ru.dbotthepony.mc.otm.registry.MLootItemConditions
object HasExosuitCondition : LootItemCondition, Serializer<HasExosuitCondition>, LootItemCondition.Builder { object HasExosuitCondition : LootItemCondition, Serializer<HasExosuitCondition>, LootItemCondition.Builder {

View File

@ -17,6 +17,7 @@ import net.minecraftforge.registries.ForgeRegistries
import ru.dbotthepony.mc.otm.capability.itemsStream import ru.dbotthepony.mc.otm.capability.itemsStream
import ru.dbotthepony.mc.otm.core.registryName import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.core.set import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.data.get
import ru.dbotthepony.mc.otm.registry.MLootItemConditions import ru.dbotthepony.mc.otm.registry.MLootItemConditions
data class ItemInInventoryCondition( data class ItemInInventoryCondition(

View File

@ -10,6 +10,7 @@ import net.minecraft.world.level.storage.loot.predicates.InvertedLootItemConditi
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType
import net.minecraftforge.common.util.FakePlayer import net.minecraftforge.common.util.FakePlayer
import ru.dbotthepony.mc.otm.data.get
import ru.dbotthepony.mc.otm.registry.MLootItemConditions import ru.dbotthepony.mc.otm.registry.MLootItemConditions
object KilledByRealPlayer : LootItemCondition, Serializer<KilledByRealPlayer>, LootItemCondition.Builder { object KilledByRealPlayer : LootItemCondition, Serializer<KilledByRealPlayer>, LootItemCondition.Builder {

View File

@ -12,6 +12,7 @@ import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType
import net.minecraftforge.common.util.FakePlayer import net.minecraftforge.common.util.FakePlayer
import ru.dbotthepony.mc.otm.NULLABLE_MINECRAFT_SERVER import ru.dbotthepony.mc.otm.NULLABLE_MINECRAFT_SERVER
import ru.dbotthepony.mc.otm.data.get
import ru.dbotthepony.mc.otm.registry.MLootItemConditions import ru.dbotthepony.mc.otm.registry.MLootItemConditions
object KilledByRealPlayerOrIndirectly : LootItemCondition, Serializer<KilledByRealPlayerOrIndirectly>, LootItemCondition.Builder { object KilledByRealPlayerOrIndirectly : LootItemCondition, Serializer<KilledByRealPlayerOrIndirectly>, LootItemCondition.Builder {

View File

@ -6,6 +6,6 @@ import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Rarity import net.minecraft.world.item.Rarity
import net.minecraft.world.level.ItemLike import net.minecraft.world.level.ItemLike
interface IRandomizableItem : ItemLike { interface IRandomizableItem {
fun randomize(itemStack: ItemStack, random: RandomSource, ply: Player?, rarity: Rarity = Rarity.COMMON) fun randomize(itemStack: ItemStack, random: RandomSource, ply: Player?, rarity: Rarity = Rarity.COMMON)
} }

View File

@ -8,14 +8,17 @@ import com.mojang.serialization.Dynamic
import com.mojang.serialization.JsonOps import com.mojang.serialization.JsonOps
import com.mojang.serialization.codecs.RecordCodecBuilder import com.mojang.serialization.codecs.RecordCodecBuilder
import it.unimi.dsi.fastutil.objects.ObjectArrayList import it.unimi.dsi.fastutil.objects.ObjectArrayList
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.storage.loot.Deserializers import net.minecraft.world.level.storage.loot.Deserializers
import net.minecraft.world.level.storage.loot.LootContext import net.minecraft.world.level.storage.loot.LootContext
import net.minecraft.world.level.storage.loot.LootPool import net.minecraft.world.level.storage.loot.LootPool
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
import net.minecraftforge.common.ForgeHooks
import net.minecraftforge.common.loot.IGlobalLootModifier import net.minecraftforge.common.loot.IGlobalLootModifier
import net.minecraftforge.common.loot.LootModifier import net.minecraftforge.common.loot.LootModifier
import java.util.Arrays import java.util.Arrays
import java.util.Deque
import java.util.stream.Stream import java.util.stream.Stream
class LootPoolAppender(conditions: Array<out LootItemCondition>, pools: Stream<LootPool>) : LootModifier(conditions) { class LootPoolAppender(conditions: Array<out LootItemCondition>, pools: Stream<LootPool>) : LootModifier(conditions) {
@ -36,23 +39,39 @@ class LootPoolAppender(conditions: Array<out LootItemCondition>, pools: Stream<L
return CODEC return CODEC
} }
// TODO: remove reflection once Forge implement a way to provide lootpool deserialization context in non-reflective way
companion object { companion object {
// same bro private val lootPoolCodec: Codec<List<LootPool>>
private fun getJson(it: Dynamic<Any?>): JsonElement {
val value = it.value ?: throw NullPointerException("value was null") private val forgeHooksLootContext by lazy {
return value as? JsonElement ?: it.ops.convertTo(JsonOps.INSTANCE, it) val field = ForgeHooks::class.java.getDeclaredField("lootContext")
field.isAccessible = true
field
} }
private val lootPoolCodec: Codec<List<LootPool>> private val lootTableContextConstructor by lazy {
val clazz = ForgeHooks::class.java.declaredClasses.firstOrNull { it.name.contains("LootTableContext") } ?: throw NoSuchElementException("Unable to find ForgeHooks\$LootTableContext!")
val constructor = clazz.getDeclaredConstructor(ResourceLocation::class.java, Boolean::class.java)
constructor.isAccessible = true
constructor
}
init { init {
val serializer = Deserializers.createLootTableSerializer().create() val serializer = Deserializers.createLootTableSerializer().create()
val notExistingLocation = ResourceLocation("null", "null")
lootPoolCodec = Codec.list(Codec.PASSTHROUGH.flatXmap({ lootPoolCodec = Codec.list(Codec.PASSTHROUGH.flatXmap({
val dequeueHolder = forgeHooksLootContext.get(null) as ThreadLocal<Deque<Any>>
val deque = dequeueHolder.get() ?: java.util.ArrayDeque()
dequeueHolder.set(deque)
try { try {
DataResult.success(serializer.fromJson(getJson(it as Dynamic<Any?>), LootPool::class.java)) deque.push(lootTableContextConstructor.newInstance(notExistingLocation, true))
DataResult.success(serializer.fromJson(it.convert(JsonOps.INSTANCE).value, LootPool::class.java))
} catch(err: JsonSyntaxException) { } catch(err: JsonSyntaxException) {
DataResult.error(err.message) DataResult.error(err.message)
} finally {
deque.pop()
} }
}, { }, {
try { try {

View File

@ -1,189 +0,0 @@
package ru.dbotthepony.mc.otm.data.loot
import com.google.common.collect.ImmutableList
import com.mojang.datafixers.util.Pair
import com.mojang.serialization.Codec
import com.mojang.serialization.DataResult
import com.mojang.serialization.DynamicOps
import com.mojang.serialization.codecs.RecordCodecBuilder
import it.unimi.dsi.fastutil.objects.ObjectArrayList
import net.minecraft.util.valueproviders.ConstantInt
import net.minecraft.util.valueproviders.IntProvider
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Rarity
import net.minecraft.world.level.storage.loot.LootContext
import net.minecraft.world.level.storage.loot.parameters.LootContextParams
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
import net.minecraftforge.common.loot.LootModifier
import net.minecraftforge.registries.ForgeRegistries
import ru.dbotthepony.mc.otm.data.codec
class RandomizableItemLootAppender(
conditions: Array<out LootItemCondition>,
entries: Collection<Entry>
) : LootModifier(conditions) {
data class Entry(
val item: IRandomizableItem,
val chance: Double,
val count: IntProvider = ConstantInt.of(1),
val rarity: Rarity = Rarity.COMMON,
val distinct: Boolean = true,
) {
constructor(
item: IRandomizableItem,
chance: Double,
count: Int,
rarity: Rarity = Rarity.COMMON,
distinct: Boolean = true,
) : this(item, chance, ConstantInt.of(count), rarity, distinct)
private constructor(
item: Item,
chance: Double,
count: IntProvider,
rarity: Rarity = Rarity.COMMON,
distinct: Boolean = true,
) : this(item as? IRandomizableItem ?: throw ClassCastException("$item is not a subtype of IRandomizableItem"), chance, count, rarity, distinct)
init {
require(count.minValue >= 0) { "Invalid min value ${count.minValue}" }
require(count.maxValue >= 0) { "Invalid max value ${count.maxValue}" }
require(count.maxValue >= count.minValue) { "Invalid bounds, they must be ${count.maxValue} >= ${count.minValue}!" }
}
companion object {
val CODEC: Codec<Entry> by lazy {
RecordCodecBuilder.create {
it.group(
ForgeRegistries.ITEMS.codec.fieldOf("item").forGetter { it.item.asItem() },
Codec.DOUBLE.fieldOf("chance").forGetter(Entry::chance),
IntProvider.CODEC.fieldOf("count").forGetter(Entry::count),
Rarity::class.codec().fieldOf("rarity").forGetter(Entry::rarity),
Codec.BOOL.fieldOf("distinct").forGetter(Entry::distinct),
).apply(it, RandomizableItemLootAppender::Entry)
}
}
}
}
val entries: List<Entry> = ImmutableList.copyOf(entries)
override fun codec(): Codec<RandomizableItemLootAppender> {
return Companion
}
override fun doApply(
generatedLoot: ObjectArrayList<ItemStack>,
context: LootContext
): ObjectArrayList<ItemStack> {
val random = context.random
val player =
context.getParamOrNull(LootContextParams.DIRECT_KILLER_ENTITY).let {
if (it != null)
it as? Player
else
context.getParamOrNull(LootContextParams.KILLER_ENTITY).let {
if (it != null)
it as? Player
else
context.getParamOrNull(LootContextParams.THIS_ENTITY) as? Player
}
}
for ((item, chance, count, rarity, distinct) in entries) {
if (random.nextDouble() <= chance) {
if (distinct) {
val items = ArrayList<ItemStack>()
for (i in 0 until count.sample(random)) {
val stack = ItemStack(item.asItem(), 1)
item.randomize(stack, random, player, rarity)
for (existing in items) {
if (ItemStack.isSameItemSameTags(existing, stack)) {
existing.count += stack.count
stack.count = 0
break
}
}
if (stack.count > 0) {
items.add(stack)
}
}
for (stack in items) {
while (stack.count > stack.maxStackSize && stack.maxStackSize > 0) {
val split = stack.split(stack.maxStackSize)
generatedLoot.add(split)
}
generatedLoot.add(stack)
}
} else {
val stack = ItemStack(item.asItem(), count.sample(random))
item.randomize(stack, random, player, rarity)
while (stack.count > stack.maxStackSize && stack.maxStackSize > 0) {
val split = stack.split(stack.maxStackSize)
generatedLoot.add(split)
}
generatedLoot.add(stack)
}
}
}
return generatedLoot
}
companion object : Codec<RandomizableItemLootAppender> {
override fun <T : Any> encode(
input: RandomizableItemLootAppender,
ops: DynamicOps<T>,
prefix: T
): DataResult<T> {
require(prefix == ops.empty()) { "Non-empty prefix: $prefix" }
return LOOT_CONDITIONS_CODEC.encode(input.conditions, ops, prefix).flatMap map@{
val encodedEntries = input.entries.map { Entry.CODEC.encode(it, ops, prefix) }
val firstError = encodedEntries.firstOrNull { !it.result().isPresent }
if (firstError != null) {
return@map firstError
}
return@map DataResult.success(ops.createMap(linkedMapOf(
ops.createString("conditions") to it,
ops.createString("entries") to ops.createList(encodedEntries.stream().map { it.result().get() })
)))
}
}
override fun <T : Any> decode(
ops: DynamicOps<T>,
input: T
): DataResult<Pair<RandomizableItemLootAppender, T>> {
return ops.getMap(input).flatMap { map ->
LOOT_CONDITIONS_CODEC.decode(ops, map["conditions"]).flatMap {
val conditions = it.first
ops.getStream(map["entries"]).flatMap {
val collected = it.map { Entry.CODEC.decode(ops, it) }.toList()
for ((i, element) in collected.withIndex()) {
if (element.result().isEmpty) {
return@flatMap DataResult.error("Invalid entry at $i: ${element.error().get().message()}")
}
}
return@flatMap DataResult.success(Pair.of(RandomizableItemLootAppender(conditions, collected.map { it.result().get().first }), ops.empty()))
}
}
}
}
}
}

View File

@ -0,0 +1,81 @@
package ru.dbotthepony.mc.otm.data.loot
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSyntaxException
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Rarity
import net.minecraft.world.level.storage.loot.LootContext
import net.minecraft.world.level.storage.loot.Serializer
import net.minecraft.world.level.storage.loot.functions.LootItemFunction
import net.minecraft.world.level.storage.loot.functions.LootItemFunctionType
import net.minecraft.world.level.storage.loot.parameters.LootContextParams
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.registry.MItemFunctionTypes
enum class RandomizerFunction(val rarity: Rarity) : LootItemFunction, LootItemFunction.Builder {
COMMON(Rarity.COMMON),
UNCOMMON(Rarity.UNCOMMON),
RARE(Rarity.RARE),
EPIC(Rarity.EPIC);
override fun apply(itemStack: ItemStack, context: LootContext): ItemStack {
val randomizer = itemStack.item as? IRandomizableItem
if (randomizer == null) {
LOGGER.error("${itemStack.item} does not implement ${IRandomizableItem::class.qualifiedName}! Can not randomize $itemStack!")
return itemStack
}
val random = context.random
val player =
context.getParamOrNull(LootContextParams.DIRECT_KILLER_ENTITY).let {
if (it != null)
it as? Player
else
context.getParamOrNull(LootContextParams.KILLER_ENTITY).let {
if (it != null)
it as? Player
else
context.getParamOrNull(LootContextParams.THIS_ENTITY) as? Player
}
}
randomizer.randomize(itemStack, random, player, rarity)
return itemStack
}
override fun getType(): LootItemFunctionType {
return MItemFunctionTypes.RANDOMIZER
}
override fun build(): LootItemFunction {
return this
}
companion object : Serializer<RandomizerFunction> {
fun valueOf(rarity: Rarity): RandomizerFunction {
return when(rarity) {
Rarity.COMMON -> COMMON
Rarity.UNCOMMON -> UNCOMMON
Rarity.RARE -> RARE
Rarity.EPIC -> EPIC
}
}
override fun serialize(p_79325_: JsonObject, p_79326_: RandomizerFunction, p_79327_: JsonSerializationContext) {
p_79325_["rarity"] = JsonPrimitive(p_79326_.rarity.name)
}
override fun deserialize(p_79323_: JsonObject, p_79324_: JsonDeserializationContext): RandomizerFunction {
return valueOf(Rarity.valueOf(p_79323_["rarity"]?.asString ?: throw JsonSyntaxException("Invalid rarity json element")))
}
private val LOGGER = LogManager.getLogger()
}
}

View File

@ -5,14 +5,12 @@ import net.minecraftforge.registries.DeferredRegister
import net.minecraftforge.registries.ForgeRegistries import net.minecraftforge.registries.ForgeRegistries
import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.data.loot.LootPoolAppender import ru.dbotthepony.mc.otm.data.loot.LootPoolAppender
import ru.dbotthepony.mc.otm.data.loot.RandomizableItemLootAppender
object LootModifiers { object LootModifiers {
private val registry = DeferredRegister.create(ForgeRegistries.Keys.GLOBAL_LOOT_MODIFIER_SERIALIZERS, OverdriveThatMatters.MOD_ID) private val registry = DeferredRegister.create(ForgeRegistries.Keys.GLOBAL_LOOT_MODIFIER_SERIALIZERS, OverdriveThatMatters.MOD_ID)
init { init {
registry.register("loot_appender") { LootPoolAppender.CODEC } registry.register("loot_appender") { LootPoolAppender.CODEC }
registry.register("randomized_appender") { RandomizableItemLootAppender.Companion }
} }
internal fun register(bus: IEventBus) { internal fun register(bus: IEventBus) {

View File

@ -0,0 +1,19 @@
package ru.dbotthepony.mc.otm.registry
import net.minecraft.core.Registry
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.level.storage.loot.functions.LootItemFunctionType
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType
import net.minecraftforge.registries.RegisterEvent
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.data.loot.RandomizerFunction
object MItemFunctionTypes {
val RANDOMIZER = LootItemFunctionType(RandomizerFunction.Companion)
internal fun register(event: RegisterEvent) {
if (event.getVanillaRegistry<LootItemConditionType>() == Registry.LOOT_FUNCTION_TYPE) {
Registry.LOOT_FUNCTION_TYPE.register(ResourceLocation(OverdriveThatMatters.MOD_ID, "randomizer"), RANDOMIZER)
}
}
}

View File

@ -262,6 +262,7 @@ object MRegistry {
bus.addListener(this::initializeCommon) bus.addListener(this::initializeCommon)
bus.addListener(MStats::registerVanilla) bus.addListener(MStats::registerVanilla)
bus.addListener(MLootItemConditions::register) bus.addListener(MLootItemConditions::register)
bus.addListener(MItemFunctionTypes::register)
MBlocks.register(bus) MBlocks.register(bus)
MBlockEntities.register(bus) MBlockEntities.register(bus)