From 4dc132a8ec1e04b9f219ae41c452009714d0d6aa Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Thu, 27 Oct 2022 15:57:35 +0700 Subject: [PATCH] Move everything to lootpools Fixes #191 --- .../ru/dbotthepony/mc/otm/datagen/loot/DSL.kt | 26 ++- .../mc/otm/datagen/loot/LootModifiers.kt | 4 +- .../mc/otm/datagen/loot/LootModifiersData.kt | 94 +++++---- .../condition/ChanceWithPlaytimeCondition.kt | 1 + .../otm/data/condition/HasExosuitCondition.kt | 1 + .../condition/ItemInInventoryCondition.kt | 1 + .../otm/data/condition/KilledByRealPlayer.kt | 1 + .../KilledByRealPlayerOrIndirectly.kt | 1 + .../mc/otm/data/loot/IRandomizableItem.kt | 2 +- .../mc/otm/data/loot/LootPoolAppender.kt | 31 ++- .../data/loot/RandomizableItemLootAppender.kt | 189 ------------------ .../mc/otm/data/loot/RandomizerFunction.kt | 81 ++++++++ .../mc/otm/registry/LootModifiers.kt | 2 - .../mc/otm/registry/MItemFunctionTypes.kt | 19 ++ .../dbotthepony/mc/otm/registry/MRegistry.kt | 1 + 15 files changed, 205 insertions(+), 249 deletions(-) delete mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/data/loot/RandomizableItemLootAppender.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/data/loot/RandomizerFunction.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItemFunctionTypes.kt diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/DSL.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/DSL.kt index c8e3aacf3..4ca29433b 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/DSL.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/DSL.kt @@ -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.setCount(minimal: Int, maximal return this } +fun > T.chanceCondition(chance: Double): T { + condition(ChanceCondition(chance)) + return this +} + fun > T.setCount(minimal: Float, maximal: Float, configurator: LootItemConditionalFunction.Builder<*>.() -> Unit = {}): T { apply(SetItemCountFunction.setCount(UniformGenerator.between(minimal, maximal)).also(configurator)) return this diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootModifiers.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootModifiers.kt index 77a42d003..cc119d648 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootModifiers.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootModifiers.kt @@ -19,7 +19,7 @@ fun BasicLootAppender( item(it.item) { setCount(it.count) } - }.build() + } }) } @@ -41,7 +41,7 @@ fun PlainLootAppender( } chanceCondition(it.second) - }.build() + } }) } diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootModifiersData.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootModifiersData.kt index adcff7df2..7751c5a1e 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootModifiersData.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootModifiersData.kt @@ -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, diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/ChanceWithPlaytimeCondition.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/ChanceWithPlaytimeCondition.kt index af1e4118b..bcfee2b3d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/ChanceWithPlaytimeCondition.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/ChanceWithPlaytimeCondition.kt @@ -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( diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/HasExosuitCondition.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/HasExosuitCondition.kt index 69c5b158e..d05663c51 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/HasExosuitCondition.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/HasExosuitCondition.kt @@ -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, LootItemCondition.Builder { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/ItemInInventoryCondition.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/ItemInInventoryCondition.kt index fbad4de61..39dc2ef2c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/ItemInInventoryCondition.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/ItemInInventoryCondition.kt @@ -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( diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/KilledByRealPlayer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/KilledByRealPlayer.kt index 642d14ce9..d4354c2a7 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/KilledByRealPlayer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/KilledByRealPlayer.kt @@ -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, LootItemCondition.Builder { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/KilledByRealPlayerOrIndirectly.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/KilledByRealPlayerOrIndirectly.kt index e08b59e5a..df3d9dd14 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/KilledByRealPlayerOrIndirectly.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/condition/KilledByRealPlayerOrIndirectly.kt @@ -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, LootItemCondition.Builder { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/loot/IRandomizableItem.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/loot/IRandomizableItem.kt index 42ba6ca86..b649659ac 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/data/loot/IRandomizableItem.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/loot/IRandomizableItem.kt @@ -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) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/loot/LootPoolAppender.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/loot/LootPoolAppender.kt index fea33bfb0..487b6d274 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/data/loot/LootPoolAppender.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/loot/LootPoolAppender.kt @@ -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, pools: Stream) : LootModifier(conditions) { @@ -36,23 +39,39 @@ class LootPoolAppender(conditions: Array, pools: Stream): JsonElement { - val value = it.value ?: throw NullPointerException("value was null") - return value as? JsonElement ?: it.ops.convertTo(JsonOps.INSTANCE, it) + private val lootPoolCodec: Codec> + + private val forgeHooksLootContext by lazy { + val field = ForgeHooks::class.java.getDeclaredField("lootContext") + field.isAccessible = true + field } - private val lootPoolCodec: Codec> + 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> + val deque = dequeueHolder.get() ?: java.util.ArrayDeque() + dequeueHolder.set(deque) + try { - DataResult.success(serializer.fromJson(getJson(it as Dynamic), 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 { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/loot/RandomizableItemLootAppender.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/loot/RandomizableItemLootAppender.kt deleted file mode 100644 index d78cbc834..000000000 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/data/loot/RandomizableItemLootAppender.kt +++ /dev/null @@ -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, - entries: Collection -) : 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 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 = ImmutableList.copyOf(entries) - - override fun codec(): Codec { - return Companion - } - - override fun doApply( - generatedLoot: ObjectArrayList, - context: LootContext - ): ObjectArrayList { - 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() - - 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 { - override fun encode( - input: RandomizableItemLootAppender, - ops: DynamicOps, - prefix: T - ): DataResult { - 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 decode( - ops: DynamicOps, - input: T - ): DataResult> { - 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())) - } - } - } - } - } -} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/loot/RandomizerFunction.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/loot/RandomizerFunction.kt new file mode 100644 index 000000000..b764464c2 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/loot/RandomizerFunction.kt @@ -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 { + 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() + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/LootModifiers.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/LootModifiers.kt index 4a92c8123..8ae067580 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/LootModifiers.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/LootModifiers.kt @@ -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) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItemFunctionTypes.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItemFunctionTypes.kt new file mode 100644 index 000000000..f219b1d6b --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItemFunctionTypes.kt @@ -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() == Registry.LOOT_FUNCTION_TYPE) { + Registry.LOOT_FUNCTION_TYPE.register(ResourceLocation(OverdriveThatMatters.MOD_ID, "randomizer"), RANDOMIZER) + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt index 50a835bb9..cbf2a6e68 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt @@ -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)