diff --git a/gradle.properties b/gradle.properties index 0228a8f..af384f5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ loader_version_range=[2,) mod_id=l2core mod_name=L2Core mod_license=LGPL-2.1 -mod_version=3.0.7+13 +mod_version=3.0.7+18 mod_group_id=dev.xkmc mod_authors=lcy0x1 mod_description=Core Library mod for all L2 mods diff --git a/src/main/java/dev/xkmc/l2core/init/reg/registrate/LegacyHolder.java b/src/main/java/dev/xkmc/l2core/init/reg/registrate/LegacyHolder.java index 1069957..74bd0f6 100644 --- a/src/main/java/dev/xkmc/l2core/init/reg/registrate/LegacyHolder.java +++ b/src/main/java/dev/xkmc/l2core/init/reg/registrate/LegacyHolder.java @@ -74,6 +74,11 @@ public interface LegacyHolder extends Holder, Supplier { return Kind.REFERENCE; } + @Override + default Holder getDelegate() { + return holder().getDelegate(); + } + @Override default boolean canSerializeIn(HolderOwner pOwner) { return holder().canSerializeIn(pOwner); diff --git a/src/main/java/dev/xkmc/l2core/serial/config/RecordDataProvider.java b/src/main/java/dev/xkmc/l2core/serial/config/RecordDataProvider.java index 96ebac8..fc7ecb6 100644 --- a/src/main/java/dev/xkmc/l2core/serial/config/RecordDataProvider.java +++ b/src/main/java/dev/xkmc/l2core/serial/config/RecordDataProvider.java @@ -1,11 +1,13 @@ package dev.xkmc.l2core.serial.config; import com.google.gson.JsonElement; +import com.mojang.serialization.JsonOps; import dev.xkmc.l2serial.serialization.codec.JsonCodec; import net.minecraft.core.HolderLookup; import net.minecraft.data.CachedOutput; import net.minecraft.data.DataProvider; import net.minecraft.data.PackOutput; +import net.neoforged.neoforge.common.conditions.ICondition; import java.nio.file.Path; import java.util.ArrayList; @@ -37,6 +39,10 @@ public abstract class RecordDataProvider implements DataProvider { this.map.forEach((k, v) -> { JsonElement elem = new JsonCodec(pvd).toJson(v); if (elem != null) { + if (v instanceof ConditionalRecord c) { + var cond = ICondition.LIST_CODEC.encodeStart(JsonOps.INSTANCE, c.conditions()).getOrThrow(); + elem.getAsJsonObject().add("neoforge:conditions", cond); + } Path path = folder.resolve("data/" + k + ".json"); list.add(DataProvider.saveStable(cache, elem, path)); } @@ -49,4 +55,10 @@ public abstract class RecordDataProvider implements DataProvider { return this.name; } + public interface ConditionalRecord { + + List conditions(); + + } + } diff --git a/src/main/java/dev/xkmc/l2core/serial/loot/LootHelper.java b/src/main/java/dev/xkmc/l2core/serial/loot/LootHelper.java index e673582..fd48093 100644 --- a/src/main/java/dev/xkmc/l2core/serial/loot/LootHelper.java +++ b/src/main/java/dev/xkmc/l2core/serial/loot/LootHelper.java @@ -1,29 +1,59 @@ package dev.xkmc.l2core.serial.loot; import com.tterrag.registrate.providers.loot.RegistrateBlockLootTables; +import com.tterrag.registrate.providers.loot.RegistrateEntityLootTables; import net.minecraft.Util; import net.minecraft.advancements.critereon.*; import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; import net.minecraft.resources.ResourceKey; +import net.minecraft.tags.TagKey; import net.minecraft.util.StringRepresentable; +import net.minecraft.world.damagesource.DamageType; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.Item; import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.item.enchantment.Enchantments; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.properties.Property; -import net.minecraft.world.level.storage.loot.functions.ApplyBonusCount; -import net.minecraft.world.level.storage.loot.functions.LootItemFunction; -import net.minecraft.world.level.storage.loot.predicates.BonusLevelTableCondition; -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.predicates.MatchTool; +import net.minecraft.world.level.storage.loot.LootContext; +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.*; +import net.minecraft.world.level.storage.loot.predicates.*; +import net.minecraft.world.level.storage.loot.providers.number.ConstantValue; +import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator; import java.util.List; import java.util.Optional; -public record LootHelper(RegistrateBlockLootTables pvd) { +public record LootHelper(HolderLookup.Provider pvd) { + + public LootHelper(RegistrateBlockLootTables pvd) { + this(pvd.getRegistries()); + } + + public LootHelper(RegistrateEntityLootTables pvd) { + this(pvd.getRegistries()); + } public Holder resolve(ResourceKey key) { - return pvd.getRegistries().holderOrThrow(key); + return pvd.holderOrThrow(key); + } + + public LootPoolSingletonContainer.Builder item(Item item) { + return LootItem.lootTableItem(item); + } + + public LootPoolSingletonContainer.Builder item(Item item, int count) { + return LootItem.lootTableItem(item) + .apply(SetItemCountFunction.setCount(ConstantValue.exactly(count))); + } + + public LootPoolSingletonContainer.Builder item(Item item, int min, int max) { + return LootItem.lootTableItem(item) + .apply(SetItemCountFunction.setCount(UniformGenerator.between(min, max))); } public EnchantmentPredicate hasEnch(ResourceKey enchant, int min) { @@ -70,4 +100,91 @@ public record LootHelper(RegistrateBlockLootTables pvd) { return BonusLevelTableCondition.bonusLevelFlatChance(resolve(Enchantments.FORTUNE), fracs); } + public LootItemFunction.Builder fortuneBin() { + return fortuneBin(4 / 7f, 3); + } + + public LootItemFunction.Builder fortuneBin(float chance, int count) { + return ApplyBonusCount.addBonusBinomialDistributionCount(resolve(Enchantments.FORTUNE), chance, count); + } + + public LootItemFunction.Builder lootCount(float factor) { + return EnchantedCountIncreaseFunction.lootingMultiplier(pvd, UniformGenerator.between(0, factor)); + } + + public LootItemCondition.Builder lootChance(float base, float slope) { + return LootItemRandomChanceWithEnchantedBonusCondition.randomChanceAndLootingBoost(pvd, base, slope); + } + + public LootItemCondition.Builder fire(boolean fire) { + return LootItemEntityPropertyCondition.hasProperties(LootContext.EntityTarget.THIS, + EntityPredicate.Builder.entity().flags( + EntityFlagsPredicate.Builder.flags().setOnFire(fire) + ).build()); + } + + public LootItemFunction.Builder smelt() { + return SmeltItemFunction.smelted().when(fire(true)); + } + + public LootItemCondition.Builder damage(TagKey tag) { + return DamageSourceCondition.hasDamageSource(DamageSourcePredicate.Builder.damageType() + .tag(TagPredicate.is(tag))); + } + + public LootItemCondition.Builder entity(EntityType type) { + return LootItemEntityPropertyCondition.hasProperties( + LootContext.EntityTarget.THIS, + EntityPredicate.Builder.entity().entityType( + EntityTypePredicate.of(type))); + } + + public LootItemCondition.Builder entity(TagKey> tag) { + return LootItemEntityPropertyCondition.hasProperties( + LootContext.EntityTarget.THIS, + EntityPredicate.Builder.entity().entityType( + EntityTypePredicate.of(tag))); + } + + public LootItemCondition.Builder killer(EntityType type) { + return LootItemEntityPropertyCondition.hasProperties( + LootContext.EntityTarget.ATTACKER, + EntityPredicate.Builder.entity().entityType( + EntityTypePredicate.of(type))); + } + + public LootItemCondition.Builder killer(TagKey> tag) { + return LootItemEntityPropertyCondition.hasProperties( + LootContext.EntityTarget.ATTACKER, + EntityPredicate.Builder.entity().entityType( + EntityTypePredicate.of(tag))); + } + + public LootItemCondition.Builder killerItem(EquipmentSlot slot, Item item) { + return LootItemEntityPropertyCondition.hasProperties( + LootContext.EntityTarget.ATTACKER, + EntityPredicate.Builder.entity().equipment( + slot(slot, ItemPredicate.Builder.item().of(item)).build()).build()); + } + + public LootItemCondition.Builder killerItem(EquipmentSlot slot, TagKey item) { + return LootItemEntityPropertyCondition.hasProperties( + LootContext.EntityTarget.ATTACKER, + EntityPredicate.Builder.entity().equipment( + slot(slot, ItemPredicate.Builder.item().of(item)).build()).build()); + } + + private EntityEquipmentPredicate.Builder slot(EquipmentSlot slot, ItemPredicate.Builder item) { + var b = EntityEquipmentPredicate.Builder.equipment(); + return switch (slot) { + case MAINHAND -> b.mainhand(item); + case OFFHAND -> b.offhand(item); + case FEET -> b.feet(item); + case LEGS -> b.legs(item); + case CHEST -> b.chest(item); + case HEAD -> b.head(item); + case BODY -> b.body(item); + }; + } + }