parent
228cc3eab5
commit
4dc132a8ec
@ -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
|
||||
|
@ -19,7 +19,7 @@ fun BasicLootAppender(
|
||||
item(it.item) {
|
||||
setCount(it.count)
|
||||
}
|
||||
}.build()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ fun PlainLootAppender(
|
||||
}
|
||||
|
||||
chanceCondition(it.second)
|
||||
}.build()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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 {
|
||||
|
@ -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(
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user