Merge remote-tracking branch 'origin/master'

This commit is contained in:
GearShocky 2022-10-27 17:12:28 +06:00
commit 3e291ff340
35 changed files with 580 additions and 523 deletions

View File

@ -0,0 +1,114 @@
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
import net.minecraft.world.level.storage.loot.LootPool
import net.minecraft.world.level.storage.loot.LootTable
import net.minecraft.world.level.storage.loot.entries.LootItem
import net.minecraft.world.level.storage.loot.entries.LootPoolSingletonContainer
import net.minecraft.world.level.storage.loot.functions.LootItemConditionalFunction
import net.minecraft.world.level.storage.loot.functions.SetItemCountFunction
import net.minecraft.world.level.storage.loot.predicates.LootItemBlockStatePropertyCondition
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 = 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))
}
fun LootPool.Builder.setRolls(count: Int): LootPool.Builder = setRolls(ConstantValue.exactly(count.toFloat()))
fun LootPool.Builder.setRolls(count: Float): LootPool.Builder = setRolls(ConstantValue.exactly(count))
fun LootPool.Builder.setRolls(min: Int, max: Int): LootPool.Builder = setRolls(UniformGenerator.between(min.toFloat(), max.toFloat()))
fun LootPool.Builder.setRolls(min: Float, max: Float): LootPool.Builder = setRolls(UniformGenerator.between(min, max))
fun LootPool.Builder.condition(value: LootItemCondition.Builder): LootPool.Builder = `when`(value)
fun LootPool.Builder.chanceCondition(chance: Double): LootPool.Builder = condition(ChanceCondition(chance))
fun <T : LootPoolSingletonContainer.Builder<*>> T.setCount(count: Int, configurator: LootItemConditionalFunction.Builder<*>.() -> Unit = {}): T {
apply(SetItemCountFunction.setCount(ConstantValue.exactly(count.toFloat())).also(configurator))
return this
}
fun <T : LootPoolSingletonContainer.Builder<*>> T.setCount(minimal: Int, maximal: Int, configurator: LootItemConditionalFunction.Builder<*>.() -> Unit = {}): T {
apply(SetItemCountFunction.setCount(UniformGenerator.between(minimal.toFloat(), maximal.toFloat())).also(configurator))
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
}
fun <T : LootPoolSingletonContainer.Builder<*>> T.condition(condition: LootItemCondition.Builder): T {
`when`(condition)
return this
}
fun <T : LootItemConditionalFunction.Builder<*>> T.condition(condition: LootItemCondition.Builder): T {
`when`(condition)
return this
}
inline fun <T : LootPoolSingletonContainer.Builder<*>> T.blockStateCondition(block: Block, configurator: StatePropertiesPredicate.Builder.() -> Unit): T {
condition(
LootItemBlockStatePropertyCondition.hasBlockStateProperties(block)
.setProperties(StatePropertiesPredicate.Builder.properties().also(configurator)))
return this
}
inline fun <T : LootItemConditionalFunction.Builder<*>> T.blockStateCondition(block: Block, configurator: StatePropertiesPredicate.Builder.() -> Unit): T {
condition(
LootItemBlockStatePropertyCondition.hasBlockStateProperties(block)
.setProperties(StatePropertiesPredicate.Builder.properties().also(configurator)))
return this
}
fun <T : LootItemConditionalFunction.Builder<*>> T.chanceCondition(chance: Double): T {
condition(ChanceCondition(chance))
return this
}
operator fun StatePropertiesPredicate.Builder.set(property: Property<*>, value: String): StatePropertiesPredicate.Builder = hasProperty(property, value)
operator fun StatePropertiesPredicate.Builder.set(property: Property<Int>, value: Int): StatePropertiesPredicate.Builder = hasProperty(property, value)
operator fun StatePropertiesPredicate.Builder.set(property: Property<Boolean>, value: Boolean): StatePropertiesPredicate.Builder = hasProperty(property, value)
operator fun <T : Comparable<T>> StatePropertiesPredicate.Builder.set(property: Property<T>, value: T): StatePropertiesPredicate.Builder {
if (value !is StringRepresentable) {
throw ClassCastException("Provided type ${value::class.qualifiedName} is not a subtype of ${StringRepresentable::class.qualifiedName}")
}
return hasProperty(property, value.serializedName)
}

View File

@ -1,8 +1,55 @@
package ru.dbotthepony.mc.otm.datagen.loot
import net.minecraft.data.DataGenerator
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
import net.minecraftforge.common.data.GlobalLootModifierProvider
import ru.dbotthepony.mc.otm.data.loot.LootPoolAppender
import ru.dbotthepony.mc.otm.datagen.DataGen
import java.util.Arrays
import java.util.stream.Stream
@Suppress("FunctionName")
fun BasicLootAppender(
conditions: Array<out LootItemCondition>,
items: Stream<ItemStack>
): LootPoolAppender {
return LootPoolAppender(conditions, items.map {
lootPool {
item(it.item) {
setCount(it.count)
}
}
})
}
@Suppress("FunctionName")
fun BasicLootAppender(
conditions: Array<out LootItemCondition>,
vararg items: ItemStack
) = BasicLootAppender(conditions, Arrays.stream(items))
@Suppress("FunctionName")
fun PlainLootAppender(
conditions: Array<out LootItemCondition>,
items: Stream<Pair<ItemStack, Double>>
): LootPoolAppender {
return LootPoolAppender(conditions, items.map {
lootPool {
item(it.first.item) {
setCount(it.first.count)
}
chanceCondition(it.second)
}
})
}
@Suppress("FunctionName")
fun PlainLootAppender(
conditions: Array<out LootItemCondition>,
vararg items: Pair<ItemStack, Double>
) = PlainLootAppender(conditions, Arrays.stream(items))
class LootModifiers(generator: DataGenerator) : GlobalLootModifierProvider(generator, DataGen.MOD_ID) {
private val lambdas = ArrayList<(LootModifiers) -> Unit>()

View File

@ -6,27 +6,70 @@ 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.ChanceWithPlaytimeCondition
import ru.dbotthepony.mc.otm.data.HasExosuitCondition
import ru.dbotthepony.mc.otm.data.IRandomizableItem
import ru.dbotthepony.mc.otm.data.ItemInInventoryCondition
import ru.dbotthepony.mc.otm.data.KilledByRealPlayer
import ru.dbotthepony.mc.otm.data.BasicLootAppender
import ru.dbotthepony.mc.otm.data.KilledByRealPlayerOrIndirectly
import ru.dbotthepony.mc.otm.data.PlainLootAppender
import ru.dbotthepony.mc.otm.data.RandomizableItemLootAppender
import ru.dbotthepony.mc.otm.data.condition.ChanceWithPlaytimeCondition
import ru.dbotthepony.mc.otm.data.condition.HasExosuitCondition
import ru.dbotthepony.mc.otm.data.condition.ItemInInventoryCondition
import ru.dbotthepony.mc.otm.data.condition.KilledByRealPlayerOrIndirectly
import ru.dbotthepony.mc.otm.data.loot.LootPoolAppender
import ru.dbotthepony.mc.otm.data.loot.RandomizerFunction
import ru.dbotthepony.mc.otm.registry.MItems
@Suppress("FunctionName")
fun LootTableIdCondition(location: String): LootItemCondition {
return LootTableIdCondition.Builder(ResourceLocation("minecraft", location)).build()
}
@Suppress("FunctionName")
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,
@ -34,14 +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,14 +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,
@ -70,27 +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,
@ -99,15 +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,
@ -143,12 +145,18 @@ fun addLootModifiers(it: LootModifiers) {
))
it.add("wither_exosuit_upgrades", BasicLootAppender(
arrayOf(LootTableIdCondition(EntityType.WITHER.defaultLootTable)),
arrayOf(
LootTableIdCondition(EntityType.WITHER.defaultLootTable),
KilledByRealPlayerOrIndirectly,
),
ItemStack(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_WITHER),
))
it.add("ender_dragon_exosuit_upgrades", BasicLootAppender(
arrayOf(LootTableIdCondition(EntityType.ENDER_DRAGON.defaultLootTable)),
arrayOf(
LootTableIdCondition(EntityType.ENDER_DRAGON.defaultLootTable),
KilledByRealPlayerOrIndirectly,
),
ItemStack(MItems.ExosuitUpgrades.INVENTORY_UPGRADE_ENDER_DRAGON),
))
}

View File

@ -10,11 +10,9 @@ import net.minecraft.advancements.critereon.StatePropertiesPredicate
import net.minecraft.data.DataGenerator
import net.minecraft.data.loot.LootTableProvider
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.StringRepresentable
import net.minecraft.world.level.ItemLike
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.SlabBlock
import net.minecraft.world.level.block.state.properties.Property
import net.minecraft.world.level.block.state.properties.SlabType
import net.minecraft.world.level.storage.loot.LootPool
import net.minecraft.world.level.storage.loot.LootTable
@ -22,85 +20,23 @@ import net.minecraft.world.level.storage.loot.ValidationContext
import net.minecraft.world.level.storage.loot.entries.LootItem
import net.minecraft.world.level.storage.loot.entries.LootPoolSingletonContainer
import net.minecraft.world.level.storage.loot.functions.CopyNbtFunction
import net.minecraft.world.level.storage.loot.functions.LootItemConditionalFunction
import net.minecraft.world.level.storage.loot.functions.SetItemCountFunction
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSet
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets
import net.minecraft.world.level.storage.loot.predicates.LootItemBlockStatePropertyCondition
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
import net.minecraft.world.level.storage.loot.providers.nbt.ContextNbtProvider
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.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.core.registryName
import java.util.function.BiConsumer
import java.util.function.Consumer
import java.util.function.Supplier
import kotlin.reflect.full.isSubclassOf
private typealias LootTableSaver = BiConsumer<ResourceLocation, LootTable.Builder>
private typealias LootTableCallback = Consumer<LootTableSaver>
private typealias LootTableCallbackProvider = Supplier<LootTableCallback>
private typealias LootTuple = Pair<LootTableCallbackProvider, LootContextParamSet>
inline fun LootTable.Builder.lootPool(configurator: LootPool.Builder.() -> Unit): LootTable.Builder = withPool(LootPool.lootPool().also(configurator))
inline fun LootPool.Builder.item(item: ItemLike, configurator: LootPoolSingletonContainer.Builder<*>.() -> Unit) {
add(LootItem.lootTableItem(item).also(configurator))
}
fun LootPool.Builder.setRolls(count: Int): LootPool.Builder = setRolls(ConstantValue.exactly(count.toFloat()))
fun LootPool.Builder.setRolls(count: Float): LootPool.Builder = setRolls(ConstantValue.exactly(count))
fun LootPool.Builder.setRolls(min: Int, max: Int): LootPool.Builder = setRolls(UniformGenerator.between(min.toFloat(), max.toFloat()))
fun LootPool.Builder.setRolls(min: Float, max: Float): LootPool.Builder = setRolls(UniformGenerator.between(min, max))
fun <T : LootPoolSingletonContainer.Builder<*>> T.setCount(count: Int, configurator: LootItemConditionalFunction.Builder<*>.() -> Unit = {}): T {
apply(SetItemCountFunction.setCount(ConstantValue.exactly(count.toFloat())).also(configurator))
return this
}
fun <T : LootPoolSingletonContainer.Builder<*>> T.setCount(minimal: Int, maximal: Int, configurator: LootItemConditionalFunction.Builder<*>.() -> Unit = {}): T {
apply(SetItemCountFunction.setCount(UniformGenerator.between(minimal.toFloat(), maximal.toFloat())).also(configurator))
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
}
fun <T : LootPoolSingletonContainer.Builder<*>> T.condition(condition: LootItemCondition.Builder): T {
`when`(condition)
return this
}
fun <T : LootItemConditionalFunction.Builder<*>> T.condition(condition: LootItemCondition.Builder): T {
`when`(condition)
return this
}
inline fun <T : LootPoolSingletonContainer.Builder<*>> T.blockStateCondition(block: Block, configurator: StatePropertiesPredicate.Builder.() -> Unit): T {
condition(LootItemBlockStatePropertyCondition.hasBlockStateProperties(block).setProperties(StatePropertiesPredicate.Builder.properties().also(configurator)))
return this
}
inline fun <T : LootItemConditionalFunction.Builder<*>> T.blockStateCondition(block: Block, configurator: StatePropertiesPredicate.Builder.() -> Unit): T {
condition(LootItemBlockStatePropertyCondition.hasBlockStateProperties(block).setProperties(StatePropertiesPredicate.Builder.properties().also(configurator)))
return this
}
operator fun StatePropertiesPredicate.Builder.set(property: Property<*>, value: String): StatePropertiesPredicate.Builder = hasProperty(property, value)
operator fun StatePropertiesPredicate.Builder.set(property: Property<Int>, value: Int): StatePropertiesPredicate.Builder = hasProperty(property, value)
operator fun StatePropertiesPredicate.Builder.set(property: Property<Boolean>, value: Boolean): StatePropertiesPredicate.Builder = hasProperty(property, value)
operator fun <T : Comparable<T>> StatePropertiesPredicate.Builder.set(property: Property<T>, value: T): StatePropertiesPredicate.Builder {
if (value !is StringRepresentable) {
throw ClassCastException("Provided type ${value::class.qualifiedName} is not a subtype of ${StringRepresentable::class.qualifiedName}")
}
return hasProperty(property, value.serializedName)
}
data class NbtCopy(val source: String, val destination: String, val strategy: CopyNbtFunction.MergeStrategy = CopyNbtFunction.MergeStrategy.REPLACE)
fun TileNbtCopy(source: String, strategy: CopyNbtFunction.MergeStrategy = CopyNbtFunction.MergeStrategy.REPLACE): NbtCopy {
@ -158,7 +94,7 @@ class LootTables(generator: DataGenerator) : LootTableProvider(generator) {
override fun validate(map: MutableMap<ResourceLocation, LootTable>, validationtracker: ValidationContext) {}
fun createSlabItemTable(block: Block) {
fun createSlabItemTable(block: Block, configurator: LootPoolSingletonContainer.Builder<*>.() -> Unit = {}) {
builder(LootContextParamSets.BLOCK, block.lootTable) {
withPool(
LootPool.lootPool().setRolls(ConstantValue.exactly(1.0f)).add(
@ -171,14 +107,16 @@ class LootTables(generator: DataGenerator) : LootTableProvider(generator) {
.hasProperty(SlabBlock.TYPE, SlabType.DOUBLE)
)
)
)
).also(configurator)
)
)
}
}
fun createSlabItemTable(block: Collection<Block>) {
block.forEach(this::createSlabItemTable)
fun createSlabItemTable(blocks: Collection<Block>, configurator: LootPoolSingletonContainer.Builder<*>.() -> Unit = {}) {
for (block in blocks) {
createSlabItemTable(block, configurator)
}
}
fun blockProvider(block: Block, provider: () -> LootTable.Builder) {
@ -206,13 +144,23 @@ class LootTables(generator: DataGenerator) : LootTableProvider(generator) {
fun dropsSelf(vararg blocks: Block) {
for (block in blocks) {
singleLootPool(LootContextParamSets.BLOCK, block.lootTable) {
add(LootItem.lootTableItem(block))
item(block) {}
}
}
}
fun dropsSelf(blocks: Collection<Block>) {
blocks.forEach(this::dropsSelf)
fun dropsSelf(block: Block, configurator: LootPoolSingletonContainer.Builder<*>.() -> Unit = {}) {
singleLootPool(LootContextParamSets.BLOCK, block.lootTable) {
item(block, configurator)
}
}
fun dropsSelf(blocks: Collection<Block>, configurator: LootPoolSingletonContainer.Builder<*>.() -> Unit = {}) {
for (block in blocks) {
singleLootPool(LootContextParamSets.BLOCK, block.lootTable) {
item(block, configurator)
}
}
}
fun tile(block: Block, f: (CopyNbtFunction.Builder) -> Unit = {}) {

View File

@ -7,6 +7,7 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf
import net.minecraft.world.level.storage.loot.entries.LootItem
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets
import net.minecraft.world.level.storage.loot.predicates.ExplosionCondition
import ru.dbotthepony.mc.otm.block.entity.ChemicalGeneratorBlockEntity
import ru.dbotthepony.mc.otm.block.entity.EnergyCounterBlockEntity
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity.Companion.ENERGY_KEY
@ -22,45 +23,45 @@ import ru.dbotthepony.mc.otm.registry.MItems
import ru.dbotthepony.mc.otm.registry.MRegistry
fun addLootTables(lootTables: LootTables) {
lootTables.dropsSelf(MRegistry.DECORATIVE_CRATE.allBlocks.values)
lootTables.dropsSelf(MRegistry.DECORATIVE_CRATE.allBlocks.values) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MRegistry.CARGO_CRATES.allBlocks.values)
lootTables.dropsSelf(MRegistry.INDUSTRIAL_GLASS.allBlocks.values)
lootTables.dropsSelf(MRegistry.INDUSTRIAL_GLASS_PANE.allBlocks.values)
lootTables.dropsSelf(MRegistry.TRITANIUM_BLOCK.allBlocks.values)
lootTables.dropsSelf(MRegistry.TRITANIUM_WALL.allBlocks.values)
lootTables.dropsSelf(MRegistry.TRITANIUM_STAIRS.allBlocks.values)
lootTables.dropsSelf(MRegistry.CARGO_CRATES.allBlocks.values) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MRegistry.INDUSTRIAL_GLASS.allBlocks.values) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MRegistry.INDUSTRIAL_GLASS_PANE.allBlocks.values) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MRegistry.TRITANIUM_BLOCK.allBlocks.values) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MRegistry.TRITANIUM_WALL.allBlocks.values) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MRegistry.TRITANIUM_STAIRS.allBlocks.values) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.createSlabItemTable(MRegistry.TRITANIUM_SLAB.allBlocks.values)
lootTables.dropsSelf(MRegistry.VENT.allBlocks.values)
lootTables.dropsSelf(MRegistry.VENT_ALTERNATIVE.allBlocks.values)
lootTables.dropsSelf(MRegistry.FLOOR_TILES.blocks.values)
lootTables.dropsSelf(MRegistry.UNREFINED_FLOOR_TILES.blocks.values)
lootTables.dropsSelf(MRegistry.TRITANIUM_STRIPED_BLOCK.flatBlocks)
lootTables.dropsSelf(MRegistry.TRITANIUM_STRIPED_STAIRS.flatBlocks)
lootTables.dropsSelf(MRegistry.TRITANIUM_STRIPED_WALL.flatBlocks)
lootTables.createSlabItemTable(MRegistry.TRITANIUM_STRIPED_SLAB.flatBlocks)
lootTables.dropsSelf(MRegistry.VENT.allBlocks.values) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MRegistry.VENT_ALTERNATIVE.allBlocks.values) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MRegistry.FLOOR_TILES.blocks.values) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MRegistry.UNREFINED_FLOOR_TILES.blocks.values) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MRegistry.TRITANIUM_STRIPED_BLOCK.flatBlocks) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MRegistry.TRITANIUM_STRIPED_STAIRS.flatBlocks) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MRegistry.TRITANIUM_STRIPED_WALL.flatBlocks) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.createSlabItemTable(MRegistry.TRITANIUM_STRIPED_SLAB.flatBlocks) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MRegistry.FLOOR_TILES_STAIRS.blocks.values)
lootTables.createSlabItemTable(MRegistry.FLOOR_TILES_SLAB.blocks.values)
lootTables.dropsSelf(MRegistry.FLOOR_TILES_STAIRS.blocks.values) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.createSlabItemTable(MRegistry.FLOOR_TILES_SLAB.blocks.values) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MBlocks.CARBON_FIBRE_BLOCK)
lootTables.dropsSelf(MBlocks.TRITANIUM_RAW_BLOCK)
lootTables.dropsSelf(MBlocks.TRITANIUM_STRIPED_BLOCK)
lootTables.dropsSelf(MBlocks.TRITANIUM_STRIPED_WALL)
lootTables.dropsSelf(MBlocks.TRITANIUM_STRIPED_STAIRS)
lootTables.createSlabItemTable(MBlocks.TRITANIUM_STRIPED_SLAB)
lootTables.dropsSelf(MBlocks.MATTER_CABLE)
lootTables.dropsSelf(MBlocks.CARBON_FIBRE_BLOCK) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MBlocks.TRITANIUM_RAW_BLOCK) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MBlocks.TRITANIUM_STRIPED_BLOCK) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MBlocks.TRITANIUM_STRIPED_WALL) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MBlocks.TRITANIUM_STRIPED_STAIRS) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.createSlabItemTable(MBlocks.TRITANIUM_STRIPED_SLAB) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MBlocks.MATTER_CABLE) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MBlocks.GRAVITATION_STABILIZER)
lootTables.dropsOther(MBlocks.GRAVITATION_STABILIZER_LENS, MBlocks.GRAVITATION_STABILIZER)
lootTables.dropsSelf(MBlocks.LABORATORY_LAMP)
lootTables.dropsSelf(MBlocks.LABORATORY_LAMP_INVERTED)
lootTables.dropsSelf(MBlocks.DANGER_STRIPE_BLOCK)
lootTables.dropsSelf(MBlocks.METAL_BEAM)
lootTables.dropsSelf(MBlocks.TRITANIUM_INGOT_BLOCK)
lootTables.dropsSelf(MBlocks.LABORATORY_LAMP) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MBlocks.LABORATORY_LAMP_INVERTED) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MBlocks.DANGER_STRIPE_BLOCK) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MBlocks.METAL_BEAM) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MBlocks.TRITANIUM_INGOT_BLOCK) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MBlocks.TRITANIUM_TRAPDOOR)
lootTables.dropsSelf(MRegistry.TRITANIUM_PRESSURE_PLATE.allBlocks.values)
lootTables.dropsSelf(MBlocks.TRITANIUM_TRAPDOOR) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MRegistry.TRITANIUM_PRESSURE_PLATE.allBlocks.values) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.block(MBlocks.PHANTOM_ATTRACTOR) {
item(MBlocks.PHANTOM_ATTRACTOR) {
@ -68,6 +69,8 @@ fun addLootTables(lootTables: LootTables) {
this[BlockStateProperties.DOUBLE_BLOCK_HALF] = DoubleBlockHalf.LOWER
}
}
condition(ExplosionCondition.survivesExplosion())
}
lootTables.block(MBlocks.TRITANIUM_DOOR) {
@ -76,6 +79,8 @@ fun addLootTables(lootTables: LootTables) {
this[BlockStateProperties.DOUBLE_BLOCK_HALF] = DoubleBlockHalf.LOWER
}
}
condition(ExplosionCondition.survivesExplosion())
}
lootTables.builder(LootContextParamSets.ADVANCEMENT_ENTITY, modLocation("research_all_android")) {

View File

@ -43,6 +43,11 @@ fun addCraftingTableRecipes(consumer: Consumer<FinishedRecipe>) {
.unlockedBy(MItemTags.TRITANIUM_INGOTS_STORAGE)
.save(consumer, modLocation("tritanium_ingot_from_storage"))
ShapelessRecipeBuilder(MItems.ENERGY_COUNTER, 1)
.requires(MItems.ENERGY_COUNTER)
.unlockedBy(MItems.ENERGY_COUNTER)
.save(consumer, modLocation("energy_counter_reset"))
MatteryRecipe(MBlocks.PLATE_PRESS)
.row(MItems.ELECTRIC_PARTS, MItems.ENERGY_BUS, MItems.ELECTRIC_PARTS)
.row(MItemTags.TRITANIUM_INGOTS, Items.BLAST_FURNACE, MItemTags.TRITANIUM_INGOTS)
@ -52,8 +57,7 @@ fun addCraftingTableRecipes(consumer: Consumer<FinishedRecipe>) {
.build(consumer)
MatteryRecipe(MBlocks.PLATE_PRESS)
.rowB(MItemTags.BASIC_CIRCUIT)
.row(MItemTags.TRITANIUM_PLATES, MItems.MACHINE_FRAME, MItemTags.TRITANIUM_PLATES)
.rowB(MItems.MACHINE_FRAME)
.rowAC(MItemTags.PISTONS, MItemTags.PISTONS)
.unlockedBy(MItemTags.TRITANIUM_INGOTS)
.unlockedBy(MItems.ELECTRIC_PARTS)

View File

@ -208,5 +208,6 @@ fun addTags(tagsProvider: TagsProvider) {
MobEffects.ABSORPTION,
MobEffects.SATURATION,
MobEffects.DOLPHINS_GRACE,
MobEffects.CONFUSION,
)
}

View File

@ -148,6 +148,26 @@ object ServerConfig {
.comment("If this is disabled, any (technically) excess hunger will be nullified, unless playing on peaceful difficulty.")
.define("regenerateEnergy", true)
object NanobotsRegeneration {
val COOLDOWN: List<Int> by specBuilder
.comment("In ticks, time between heal ticks")
.comment("One heal tick restores 1 heart (2 health points) at most")
.comment("If not getting hurt in specified period of ticks, heal tick takes place, tick timer resets to zero and THIS array' index advances by 1")
.comment("Index inside this array can not exceed of one of ability's")
.comment("")
.comment("Wording in pseudocode:")
.comment("if (ticksSinceTakingDamage >= cooldownConfigOption[healTicks /* or config's biggest index, whichever is smaller */]) {")
.comment(" healTicks = min(healTicks + 1, this.level /* ability level */)")
.comment(" ticksSinceTakingDamage = 0")
.comment(" this.ply.heal(...)")
.comment("}")
.defineList("cooldown", { mutableListOf(80, 60, 40, 20) }) { it is Int }
val ENERGY_PER_HITPOINT by specBuilder
.comment("Energy required to regenerate 1 health point (half a heart)")
.defineImpreciseFraction("energyPerHitpoint", ImpreciseFraction(800))
}
val ANDROID_ENERGY_PER_HUNGER_POINT by specBuilder.defineImpreciseFraction("energyPerHunger", ImpreciseFraction(2000), ImpreciseFraction.ZERO)
val ANDROID_MAX_ENERGY by specBuilder.comment("Internal battery of every android has this much storage").defineImpreciseFraction("capacity", ImpreciseFraction(80_000), ImpreciseFraction.ZERO)
val NIGHT_VISION_POWER_DRAW by specBuilder.defineImpreciseFraction("nightVisionPowerDraw", ImpreciseFraction(8), ImpreciseFraction.ZERO)
@ -223,6 +243,7 @@ object ServerConfig {
init {
// access instances so spec is built
NanobotsRegeneration
EnderTeleporter
AndroidJumpBoost
AndroidItemMagnet

View File

@ -3,6 +3,7 @@ package ru.dbotthepony.mc.otm.android.feature
import net.minecraft.nbt.CompoundTag
import net.minecraft.server.level.ServerPlayer
import net.minecraftforge.event.entity.living.LivingHurtEvent
import ru.dbotthepony.mc.otm.ServerConfig
import ru.dbotthepony.mc.otm.android.AndroidFeature
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
@ -19,16 +20,16 @@ class NanobotsRegenerationFeature(android: MatteryPlayerCapability) : AndroidFea
if (ply.health > 0f && ply.health < ply.maxHealth) {
ticksPassed++
val waitTime = TICKS_BETWEEN_HEAL.getOrElse(healTicks) { TICKS_BETWEEN_HEAL.last() }
val waitTime = ServerConfig.NanobotsRegeneration.COOLDOWN.getOrElse(healTicks) { ServerConfig.NanobotsRegeneration.COOLDOWN.last() }
if (ticksPassed > waitTime) {
val missingHealth = (ply.maxHealth - ply.health).coerceAtMost(2f)
val power = ENERGY_PER_HITPOINT * missingHealth
val power = ServerConfig.NanobotsRegeneration.ENERGY_PER_HITPOINT * missingHealth
val extracted = android.androidEnergy.extractEnergyInner(power, false)
if (extracted.isPositive) {
healTicks = (healTicks + 1).coerceAtMost(level)
val healed = (extracted / ENERGY_PER_HITPOINT).toFloat()
val healed = (extracted / ServerConfig.NanobotsRegeneration.ENERGY_PER_HITPOINT).toFloat()
ply.heal(healed)
(ply as ServerPlayer?)?.awardStat(StatNames.HEALTH_REGENERATED, (healed * 10f).roundToInt())
ticksPassed = 0
@ -59,15 +60,4 @@ class NanobotsRegenerationFeature(android: MatteryPlayerCapability) : AndroidFea
ticksPassed = nbt.getInt("ticksPassed")
healTicks = nbt.getInt("healTicks")
}
companion object {
private val ENERGY_PER_HITPOINT = ImpreciseFraction(800)
private val TICKS_BETWEEN_HEAL = listOf(
100, // 5 seconds
80, // 4 seconds
60, // 3 seconds
40, // 2 seconds
)
}
}

View File

@ -16,7 +16,6 @@ import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.energy.IEnergyStorage
import net.minecraftforge.network.PacketDistributor
import ru.dbotthepony.mc.otm.*
import ru.dbotthepony.mc.otm.block.EnergyCounterBlock
import ru.dbotthepony.mc.otm.capability.*
@ -126,13 +125,13 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
tickOnceServer(this::checkSurroundings)
}
private inner class EnergyCounterCap(val is_input: Boolean) : IMatteryEnergyStorage {
private inner class EnergyCounterCap(val isInput: Boolean) : IMatteryEnergyStorage {
override fun extractEnergyOuter(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
return extractEnergyInner(howMuch, simulate)
}
override fun extractEnergyInner(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
if (is_input)
if (isInput)
return ImpreciseFraction.ZERO
if (inputCapability.isPresent) {
@ -164,7 +163,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
}
override fun receiveEnergyInner(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
if (!is_input)
if (!isInput)
return ImpreciseFraction.ZERO
if (outputCapability.isPresent) {
@ -193,7 +192,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
override val batteryLevel: ImpreciseFraction
get() {
if (is_input) {
if (isInput) {
if (outputCapability.isPresent) {
val it = outputCapability.resolve().get()
@ -220,7 +219,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
override val maxBatteryLevel: ImpreciseFraction
get() {
if (is_input) {
if (isInput) {
if (outputCapability.isPresent) {
val it = outputCapability.resolve().get()
@ -247,7 +246,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
override val missingPower: ImpreciseFraction
get() {
if (is_input) {
if (isInput) {
if (outputCapability.isPresent) {
val it = outputCapability.resolve().get()
@ -272,15 +271,15 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
return ImpreciseFraction.ZERO
}
override fun canExtract() = !is_input
override fun canReceive() = is_input
override fun canExtract() = !isInput
override fun canReceive() = isInput
}
private var resolverInput = LazyOptional.of<IMatteryEnergyStorage> { energyInput }
private var resolverOutput = LazyOptional.of<IMatteryEnergyStorage> { energyOutput }
private var resolverInputMekanism = if (isMekanismLoaded) LazyOptional.of { Mattery2MekanismEnergyWrapper(energyInput) } else null
private var resolverOutputMekanism = if (isMekanismLoaded) LazyOptional.of { Mattery2MekanismEnergyWrapper(energyInput) } else null
private var resolverOutputMekanism = if (isMekanismLoaded) LazyOptional.of { Mattery2MekanismEnergyWrapper(energyOutput) } else null
private var valid = true
@ -301,7 +300,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
if (isMekanismLoaded) {
resolverInputMekanism = LazyOptional.of { Mattery2MekanismEnergyWrapper(energyInput) }
resolverOutputMekanism = LazyOptional.of { Mattery2MekanismEnergyWrapper(energyInput) }
resolverOutputMekanism = LazyOptional.of { Mattery2MekanismEnergyWrapper(energyOutput) }
}
}

View File

@ -19,6 +19,7 @@ import net.minecraft.client.renderer.FogRenderer
import net.minecraft.client.renderer.GameRenderer
import net.minecraft.core.Vec3i
import net.minecraft.world.level.levelgen.XoroshiroRandomSource
import net.minecraft.world.level.material.FogType
import org.lwjgl.opengl.GL14
import ru.dbotthepony.mc.otm.ClientConfig
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
@ -465,7 +466,7 @@ object GlitchRenderer {
makeColorGlitch()
}
if (!Minecraft.useShaderTransparency()) {
if (!Minecraft.useShaderTransparency() && minecraft.gameRenderer.mainCamera.fluidInCamera == FogType.NONE) {
glitchBuffer.bindWrite(true)
RenderSystem.clearColor(FogRenderer.fogRed, FogRenderer.fogGreen, FogRenderer.fogBlue, 1f)
RenderSystem.clear(GlConst.GL_COLOR_BUFFER_BIT, Minecraft.ON_OSX)

View File

@ -216,6 +216,7 @@ class MatterPanelScreen(
val input = object : EditBoxPanel<MatterPanelScreen>(this@MatterPanelScreen, rowInput) {
init {
dock = Dock.FILL
requestFocus()
}
override fun configureNew(widget: EditBox, recreation: Boolean) {

View File

@ -1576,7 +1576,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
open fun tick() {
tick++
for (child in Array(childrenInternal.size) { childrenInternal[it] }) {
for (child in Array(visibleChildrenInternal.size) { visibleChildrenInternal[it] }) {
child.tick()
}
}

View File

@ -10,11 +10,28 @@ import java.util.function.Predicate
import kotlin.NoSuchElementException
import kotlin.math.absoluteValue
/**
* Represents value which can be encoded onto or decoded from stream.
*
* Also provides [copy] and [compare] methods
*/
interface IStreamCodec<V> {
fun read(stream: DataInputStream): V
fun write(stream: DataOutputStream, value: V)
/**
* if value is immutable, return it right away
*/
fun copy(value: V): V
fun compare(a: V, b: V): Boolean
/**
* Optional equality check override. Utilized to determine whenever e.g. network value is different from new value
*
* By default uses [Any.equals]
*/
fun compare(a: V, b: V): Boolean {
return a == b
}
}
class StreamCodec<V>(

View File

@ -1,54 +0,0 @@
package ru.dbotthepony.mc.otm.data
import com.google.common.collect.ImmutableList
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import it.unimi.dsi.fastutil.objects.ObjectArrayList
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.storage.loot.LootContext
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
import net.minecraftforge.common.loot.IGlobalLootModifier
import net.minecraftforge.common.loot.LootModifier
import java.util.Arrays
import java.util.stream.Stream
class BasicLootAppender(
conditions: Array<out LootItemCondition>,
items: Stream<ItemStack>
) : LootModifier(conditions) {
constructor(
conditions: Array<out LootItemCondition>,
items: Collection<ItemStack>
) : this(conditions, items.stream())
constructor(
conditions: Array<out LootItemCondition>,
vararg items: ItemStack
) : this(conditions, Arrays.stream(items))
private val items = items.map { it.copy() }.collect(ImmutableList.toImmutableList())
override fun codec(): Codec<out IGlobalLootModifier> {
return CODEC
}
override fun doApply(
generatedLoot: ObjectArrayList<ItemStack>,
context: LootContext
): ObjectArrayList<ItemStack> {
for (item in items)
generatedLoot.add(item.copy())
return generatedLoot
}
companion object {
val CODEC: Codec<BasicLootAppender> by lazy {
RecordCodecBuilder.create {
codecStart(it)
.and(ItemStackCodec.LIST.fieldOf("items").forGetter(BasicLootAppender::items))
.apply(it, ::BasicLootAppender)
}
}
}
}

View File

@ -1,52 +0,0 @@
package ru.dbotthepony.mc.otm.data
import com.google.common.collect.ImmutableList
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import it.unimi.dsi.fastutil.objects.ObjectArrayList
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.storage.loot.LootContext
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
import net.minecraftforge.common.loot.IGlobalLootModifier
import net.minecraftforge.common.loot.LootModifier
import java.util.*
import java.util.stream.Stream
class PlainLootAppender(conditions: Array<out LootItemCondition>, entries: Stream<Pair<ItemStack, Double>>) : LootModifier(conditions) {
constructor(conditions: Array<out LootItemCondition>, vararg entries: Pair<ItemStack, Double>) : this(conditions, Arrays.stream(entries))
constructor(conditions: Array<out LootItemCondition>, entries: Collection<Pair<ItemStack, Double>>) : this(conditions, entries.stream())
private val entries = entries.map { it.first.copy() to it.second }.collect(ImmutableList.toImmutableList())
override fun doApply(generatedLoot: ObjectArrayList<ItemStack>, context: LootContext): ObjectArrayList<ItemStack> {
for ((item, chance) in entries) {
if (context.random.nextDouble() <= chance) {
generatedLoot.add(item.copy())
}
}
return generatedLoot
}
override fun codec(): Codec<out IGlobalLootModifier> {
return CODEC
}
companion object {
private val paircodec: Codec<Pair<ItemStack, Double>> = RecordCodecBuilder.create {
it.group(
ItemStackCodec.fieldOf("item").forGetter { it.first },
Codec.DOUBLE.fieldOf("chance").forGetter { it.second }
).apply(it, ::Pair)
}
private val pairlistcodec = Codec.list(paircodec)
val CODEC: Codec<PlainLootAppender> =
RecordCodecBuilder.create {
codecStart(it).and(
pairlistcodec.fieldOf("entries").forGetter(PlainLootAppender::entries)
).apply(it, ::PlainLootAppender)
}
}
}

View File

@ -1,195 +0,0 @@
package ru.dbotthepony.mc.otm.data
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.RandomSource
import net.minecraft.util.valueproviders.ConstantInt
import net.minecraft.util.valueproviders.IntProvider
import net.minecraft.util.valueproviders.UniformInt
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.ItemLike
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
interface IRandomizableItem : ItemLike {
fun randomize(itemStack: ItemStack, random: RandomSource, ply: Player?, rarity: Rarity = Rarity.COMMON)
}
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, ::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,44 @@
package ru.dbotthepony.mc.otm.data.condition
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.level.storage.loot.LootContext
import net.minecraft.world.level.storage.loot.Serializer
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.registry.MLootItemConditions
/**
* u serious?
*/
data class ChanceCondition(val chance: Double) : LootItemCondition, LootItemCondition.Builder {
init {
require(chance in 0.0 .. 1.0) { "Invalid chance: $chance" }
}
override fun test(t: LootContext): Boolean {
return t.random.nextDouble() < chance
}
override fun getType(): LootItemConditionType {
return MLootItemConditions.CHANCE
}
override fun build(): LootItemCondition {
return this
}
companion object : Serializer<ChanceCondition> {
override fun serialize(p_79325_: JsonObject, p_79326_: ChanceCondition, p_79327_: JsonSerializationContext) {
p_79325_["chance"] = JsonPrimitive(p_79326_.chance)
}
override fun deserialize(p_79323_: JsonObject, p_79324_: JsonDeserializationContext): ChanceCondition {
return ChanceCondition(p_79323_["chance"]?.asDouble ?: throw JsonSyntaxException("Invalid chance json element"))
}
}
}

View File

@ -1,4 +1,4 @@
package ru.dbotthepony.mc.otm.data
package ru.dbotthepony.mc.otm.data.condition
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonObject
@ -11,9 +11,10 @@ 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
class ChanceWithPlaytimeCondition(
data class ChanceWithPlaytimeCondition(
val minPlaytime: Int = 0,
val maxPlaytime: Int,
val minProbability: Double,

View File

@ -1,4 +1,4 @@
package ru.dbotthepony.mc.otm.data
package ru.dbotthepony.mc.otm.data.condition
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonObject
@ -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

@ -1,4 +1,4 @@
package ru.dbotthepony.mc.otm.data
package ru.dbotthepony.mc.otm.data.condition
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonObject
@ -17,9 +17,10 @@ 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
class ItemInInventoryCondition(
data class ItemInInventoryCondition(
val item: ItemStack,
val matchDamage: Boolean = false,
val matchNBT: Boolean = false,

View File

@ -1,4 +1,4 @@
package ru.dbotthepony.mc.otm.data
package ru.dbotthepony.mc.otm.data.condition
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonObject
@ -10,7 +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.capability.matteryPlayer
import ru.dbotthepony.mc.otm.data.get
import ru.dbotthepony.mc.otm.registry.MLootItemConditions
object KilledByRealPlayer : LootItemCondition, Serializer<KilledByRealPlayer>, LootItemCondition.Builder {

View File

@ -1,4 +1,4 @@
package ru.dbotthepony.mc.otm.data
package ru.dbotthepony.mc.otm.data.condition
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonObject
@ -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

@ -0,0 +1,11 @@
package ru.dbotthepony.mc.otm.data.loot
import net.minecraft.util.RandomSource
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Rarity
import net.minecraft.world.level.ItemLike
interface IRandomizableItem {
fun randomize(itemStack: ItemStack, random: RandomSource, ply: Player?, rarity: Rarity = Rarity.COMMON)
}

View File

@ -1,4 +1,4 @@
package ru.dbotthepony.mc.otm.data
package ru.dbotthepony.mc.otm.data.loot
import com.google.common.collect.ImmutableList
import com.google.gson.*
@ -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) {
@ -25,7 +28,10 @@ class LootPoolAppender(conditions: Array<out LootItemCondition>, pools: Stream<L
constructor(conditions: Array<out LootItemCondition>, vararg pools: LootPool) : this(conditions, Arrays.stream(pools))
override fun doApply(generatedLoot: ObjectArrayList<ItemStack>, context: LootContext): ObjectArrayList<ItemStack> {
pools.forEach { it.addRandomItems(generatedLoot::add, context) }
for (pool in pools) {
pool.addRandomItems(generatedLoot::add, context)
}
return generatedLoot
}
@ -33,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

@ -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

@ -11,11 +11,12 @@ import net.minecraft.world.level.Level
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.core.tagNotNull
import ru.dbotthepony.mc.otm.data.IRandomizableItem
import ru.dbotthepony.mc.otm.data.loot.IRandomizableItem
import java.util.*
@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS") // .tab(null) is a legal statement because tab field itself is nullable
class ProceduralExoSuitSlotUpgradeItem : AbstractExoSuitSlotUpgradeItem(defaultProperties().tab(null)), IRandomizableItem {
class ProceduralExoSuitSlotUpgradeItem : AbstractExoSuitSlotUpgradeItem(defaultProperties().tab(null)),
IRandomizableItem {
override fun getRarity(itemStack: ItemStack): Rarity {
return when (slotCount(itemStack)) {
in 0 .. 9 -> Rarity.COMMON

View File

@ -34,10 +34,10 @@ private object TritaniumArmorMaterial : ArmorMaterial {
override fun getDefenseForSlot(p_40411_: EquipmentSlot): Int {
return when (p_40411_) {
EquipmentSlot.FEET -> 4
EquipmentSlot.LEGS -> 7
EquipmentSlot.CHEST -> 12
EquipmentSlot.HEAD -> 5
EquipmentSlot.FEET -> 3
EquipmentSlot.LEGS -> 6
EquipmentSlot.CHEST -> 8
EquipmentSlot.HEAD -> 3
else -> throw IllegalArgumentException("yo dude what the fuck")
}
}

View File

@ -87,7 +87,12 @@ class ExoSuitInventoryMenu(val capability: MatteryPlayerCapability) : MatteryMen
init {
for ((a, b) in curiosSlots) {
addSlot(a)
if (b != null) addSlot(b)
storage2Inventory(a)
if (b != null) {
addSlot(b)
storage2Inventory(b)
}
}
}

View File

@ -94,6 +94,24 @@ enum class MapAction {
CLEAR, ADD, REMOVE
}
/**
* Universal one-to-many value synchronizer
*
* This class represent collection of synchronizable values (similar to entitydata or menu's dataslots)
*
* Advantages of this class are:
* * It can be freely pooled. You can call [collectNetworkPayload] and [FieldSynchronizer.Endpoint.collectNetworkPayload] as much as you like,
* because they use dirty lists to track which fields need networking. **Warning**: If you attach one or more "observed" values (more on them below),
* each call to above methods will cause scan on observed fields.
* * It is universal. No, seriously, you can use it anywhere, all it needs is you encode [FastByteArrayOutputStream], send it over your
* networking channel, and create [InputStream] to feed to [FieldSynchronizer.Endpoint]
* * Its capabilities at networking are only limited by codecs you provide, see [FieldSynchronizer.Field], [IStreamCodec]
* * Provides not only single values, but also smart [FieldSynchronizer.Map] map synchronization
* * Has network version "conflict" resolver. If source FieldSynchronizer and target FieldSynchronizer have different set of fields, they can figure out how to synchronize
* only known fields. **Keep in mind this only works if you manually specify names on ALL created fields**
* * Directly bytestream embeddable. [FieldSynchronizer.Endpoint] always knows how much bytes to read from incoming stream, you can directly embed it into your data flow
* on network channel.
*/
@Suppress("unused")
class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCallback: Boolean) {
constructor() : this(Runnable {}, false)

View File

@ -4,19 +4,13 @@ import net.minecraftforge.eventbus.api.IEventBus
import net.minecraftforge.registries.DeferredRegister
import net.minecraftforge.registries.ForgeRegistries
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.data.LootPoolAppender
import ru.dbotthepony.mc.otm.data.BasicLootAppender
import ru.dbotthepony.mc.otm.data.PlainLootAppender
import ru.dbotthepony.mc.otm.data.RandomizableItemLootAppender
import ru.dbotthepony.mc.otm.data.loot.LootPoolAppender
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("loot_appender_separated") { PlainLootAppender.CODEC }
registry.register("loot_appender_basic") { BasicLootAppender.CODEC }
registry.register("randomizable_item_loot_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

@ -3,14 +3,14 @@ package ru.dbotthepony.mc.otm.registry
import net.minecraft.core.Registry
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent
import net.minecraftforge.registries.RegisterEvent
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.data.ChanceWithPlaytimeCondition
import ru.dbotthepony.mc.otm.data.HasExosuitCondition
import ru.dbotthepony.mc.otm.data.ItemInInventoryCondition
import ru.dbotthepony.mc.otm.data.KilledByRealPlayer
import ru.dbotthepony.mc.otm.data.KilledByRealPlayerOrIndirectly
import ru.dbotthepony.mc.otm.data.condition.ChanceWithPlaytimeCondition
import ru.dbotthepony.mc.otm.data.condition.HasExosuitCondition
import ru.dbotthepony.mc.otm.data.condition.ItemInInventoryCondition
import ru.dbotthepony.mc.otm.data.condition.KilledByRealPlayer
import ru.dbotthepony.mc.otm.data.condition.KilledByRealPlayerOrIndirectly
import ru.dbotthepony.mc.otm.data.condition.ChanceCondition
object MLootItemConditions {
val HAS_EXOSUIT = LootItemConditionType(HasExosuitCondition)
@ -18,6 +18,7 @@ object MLootItemConditions {
val KILLED_BY_REAL_PLAYER_OR_INDIRECTLY = LootItemConditionType(KilledByRealPlayerOrIndirectly)
val CHANCE_WITH_PLAYTIME = LootItemConditionType(ChanceWithPlaytimeCondition)
val ITEM_IN_INVENTORY = LootItemConditionType(ItemInInventoryCondition)
val CHANCE = LootItemConditionType(ChanceCondition.Companion)
internal fun register(event: RegisterEvent) {
if (event.getVanillaRegistry<LootItemConditionType>() == Registry.LOOT_CONDITION_TYPE) {
@ -26,6 +27,7 @@ object MLootItemConditions {
Registry.LOOT_CONDITION_TYPE.register(ResourceLocation(OverdriveThatMatters.MOD_ID, "item_in_inventory"), ITEM_IN_INVENTORY)
Registry.LOOT_CONDITION_TYPE.register(ResourceLocation(OverdriveThatMatters.MOD_ID, "killed_by_real_player"), KILLED_BY_REAL_PLAYER)
Registry.LOOT_CONDITION_TYPE.register(ResourceLocation(OverdriveThatMatters.MOD_ID, "killed_by_real_player_or_indirectly"), KILLED_BY_REAL_PLAYER_OR_INDIRECTLY)
Registry.LOOT_CONDITION_TYPE.register(ResourceLocation(OverdriveThatMatters.MOD_ID, "chance"), CHANCE)
}
}
}

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)

View File

@ -2,7 +2,7 @@
"type": "minecraft:crafting_shaped",
"pattern": [
"GBG",
"BGB",
"PEP",
"TET"
],
@ -31,6 +31,6 @@
"result": {
"item": "overdrive_that_matters:machine_frame",
"count": 1
"count": 2
}
}