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.util.StringRepresentable
import net.minecraft.world.item.Rarity
import net.minecraft.world.level.ItemLike
import net.minecraft.world.level.block.Block
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.UniformGenerator
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.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) {
add(LootItem.lootTableItem(item).also(configurator))
@ -43,6 +62,11 @@ fun <T : LootPoolSingletonContainer.Builder<*>> T.setCount(minimal: Int, maximal
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 {
apply(SetItemCountFunction.setCount(UniformGenerator.between(minimal, maximal)).also(configurator))
return this

View File

@ -19,7 +19,7 @@ fun BasicLootAppender(
item(it.item) {
setCount(it.count)
}
}.build()
}
})
}
@ -41,7 +41,7 @@ fun PlainLootAppender(
}
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.level.storage.loot.BuiltInLootTables
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
import net.minecraftforge.common.loot.LootModifier
import net.minecraftforge.common.loot.LootTableIdCondition
import ru.dbotthepony.mc.otm.data.condition.ChanceWithPlaytimeCondition
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.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
@Suppress("FunctionName")
@ -25,7 +26,50 @@ fun LootTableIdCondition(location: ResourceLocation): LootItemCondition {
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) {
exosuitModifiers(it)
it.add("dungeon_pill", PlainLootAppender(
arrayOf(LootTableIdCondition(BuiltInLootTables.SIMPLE_DUNGEON)),
ItemStack(MItems.PILL_ANDROID, 1) to 0.4,
@ -33,15 +77,6 @@ fun addLootModifiers(it: LootModifiers) {
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(
arrayOf(LootTableIdCondition(BuiltInLootTables.ABANDONED_MINESHAFT)),
ItemStack(MItems.PILL_ANDROID, 1) to 0.075,
@ -49,15 +84,6 @@ fun addLootModifiers(it: LootModifiers) {
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(
arrayOf(LootTableIdCondition(BuiltInLootTables.ABANDONED_MINESHAFT)),
ItemStack(MItems.NUTRIENT_PASTE, 6) to 0.5,
@ -71,29 +97,11 @@ fun addLootModifiers(it: LootModifiers) {
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(
arrayOf(LootTableIdCondition(BuiltInLootTables.JUNGLE_TEMPLE)),
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(
arrayOf(LootTableIdCondition(BuiltInLootTables.END_CITY_TREASURE)),
ItemStack(MItems.PILL_ANDROID, 1) to 0.1,
@ -102,16 +110,6 @@ fun addLootModifiers(it: LootModifiers) {
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(
arrayOf(LootTableIdCondition(BuiltInLootTables.SHIPWRECK_SUPPLY)),
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 ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.data.get
import ru.dbotthepony.mc.otm.registry.MLootItemConditions
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.LootItemConditionType
import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.data.get
import ru.dbotthepony.mc.otm.registry.MLootItemConditions
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.core.registryName
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.data.get
import ru.dbotthepony.mc.otm.registry.MLootItemConditions
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.LootItemConditionType
import net.minecraftforge.common.util.FakePlayer
import ru.dbotthepony.mc.otm.data.get
import ru.dbotthepony.mc.otm.registry.MLootItemConditions
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.minecraftforge.common.util.FakePlayer
import ru.dbotthepony.mc.otm.NULLABLE_MINECRAFT_SERVER
import ru.dbotthepony.mc.otm.data.get
import ru.dbotthepony.mc.otm.registry.MLootItemConditions
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.level.ItemLike
interface IRandomizableItem : ItemLike {
interface IRandomizableItem {
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.codecs.RecordCodecBuilder
import it.unimi.dsi.fastutil.objects.ObjectArrayList
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.storage.loot.Deserializers
import net.minecraft.world.level.storage.loot.LootContext
import net.minecraft.world.level.storage.loot.LootPool
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.LootModifier
import java.util.Arrays
import java.util.Deque
import java.util.stream.Stream
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
}
// TODO: remove reflection once Forge implement a way to provide lootpool deserialization context in non-reflective way
companion object {
// same bro
private fun getJson(it: Dynamic<Any?>): JsonElement {
val value = it.value ?: throw NullPointerException("value was null")
return value as? JsonElement ?: it.ops.convertTo(JsonOps.INSTANCE, it)
private val lootPoolCodec: Codec<List<LootPool>>
private val forgeHooksLootContext by lazy {
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 {
val serializer = Deserializers.createLootTableSerializer().create()
val notExistingLocation = ResourceLocation("null", "null")
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 {
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) {
DataResult.error(err.message)
} finally {
deque.pop()
}
}, {
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 ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.data.loot.LootPoolAppender
import ru.dbotthepony.mc.otm.data.loot.RandomizableItemLootAppender
object LootModifiers {
private val registry = DeferredRegister.create(ForgeRegistries.Keys.GLOBAL_LOOT_MODIFIER_SERIALIZERS, OverdriveThatMatters.MOD_ID)
init {
registry.register("loot_appender") { LootPoolAppender.CODEC }
registry.register("randomized_appender") { RandomizableItemLootAppender.Companion }
}
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(MStats::registerVanilla)
bus.addListener(MLootItemConditions::register)
bus.addListener(MItemFunctionTypes::register)
MBlocks.register(bus)
MBlockEntities.register(bus)