diff --git a/src/main/kotlin/dev/zxq5/items/FantasysmpItemRegistry.kt b/src/main/kotlin/dev/zxq5/items/FantasysmpItemRegistry.kt new file mode 100644 index 0000000..7871391 --- /dev/null +++ b/src/main/kotlin/dev/zxq5/items/FantasysmpItemRegistry.kt @@ -0,0 +1,54 @@ +package dev.zxq5.items + +import dev.zxq5.Fantasysmp +import dev.zxq5.items.core.GenericGearSet +import dev.zxq5.items.core.GenericItemSet +import net.fabricmc.fabric.api.creativetab.v1.FabricCreativeModeTab +import net.minecraft.core.Registry +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.core.registries.Registries +import net.minecraft.network.chat.Component +import net.minecraft.resources.Identifier +import net.minecraft.resources.ResourceKey +import net.minecraft.world.item.CreativeModeTab +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack + +object ModItems { + fun register( + name: String, + itemFactory: (Item.Properties) -> T, + settings: Item.Properties + ): T { + val itemKey = ResourceKey.create(Registries.ITEM, Identifier.fromNamespaceAndPath(Fantasysmp.MOD_ID, name)) + val item = itemFactory(settings.setId(itemKey)) + Registry.register(BuiltInRegistries.ITEM, itemKey, item) + return item + } + + val CUSTOM_CREATIVE_TAB_KEY: ResourceKey = ResourceKey.create( + BuiltInRegistries.CREATIVE_MODE_TAB.key(), Identifier.fromNamespaceAndPath(Fantasysmp.MOD_ID, "creative_tab") + ) + + val CUSTOM_CREATIVE_TAB: CreativeModeTab = FabricCreativeModeTab.builder() + .icon { ItemStack(Witherite.SWORD) } + .title(Component.translatable("Fantasysmp")) + .displayItems { _, output -> + GenericItemSet.all.forEach { + it.items.forEach(output::accept) + } + } + .build() + + private val eagerLoad = listOf( + Witherite, + Dragonite, + ) + + fun onLoad() { + // ensure that implementations such as creative tab are loaded and initialised + eagerLoad + GenericItemSet.all.forEach { it.onLoad() } + Registry.register(BuiltInRegistries.CREATIVE_MODE_TAB, CUSTOM_CREATIVE_TAB_KEY, CUSTOM_CREATIVE_TAB); + } +} \ No newline at end of file diff --git a/src/main/kotlin/dev/zxq5/items/Items.kt b/src/main/kotlin/dev/zxq5/items/Items.kt deleted file mode 100644 index e91d935..0000000 --- a/src/main/kotlin/dev/zxq5/items/Items.kt +++ /dev/null @@ -1,290 +0,0 @@ -package dev.zxq5.items - -import dev.zxq5.Fantasysmp -import dev.zxq5.util.id -import dev.zxq5.util.resourceKey -import kotlinx.serialization.descriptors.StructureKind -import net.fabricmc.fabric.api.creativetab.v1.FabricCreativeModeTab -import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagsProvider -import net.minecraft.advancements.criterion.InventoryChangeTrigger -import net.minecraft.advancements.criterion.ItemPredicate -import net.minecraft.core.Holder -import net.minecraft.core.HolderGetter -import net.minecraft.core.HolderLookup -import net.minecraft.core.Registry -import net.minecraft.core.registries.BuiltInRegistries -import net.minecraft.core.registries.Registries -import net.minecraft.data.recipes.RecipeCategory -import net.minecraft.data.recipes.RecipeOutput -import net.minecraft.data.recipes.RecipeProvider -import net.minecraft.data.recipes.ShapedRecipeBuilder -import net.minecraft.data.recipes.SmithingTransformRecipeBuilder -import net.minecraft.network.chat.Component -import net.minecraft.resources.Identifier -import net.minecraft.resources.ResourceKey -import net.minecraft.sounds.SoundEvent -import net.minecraft.tags.TagKey -import net.minecraft.world.entity.EquipmentSlot -import net.minecraft.world.entity.player.Player -import net.minecraft.world.item.* -import net.minecraft.world.item.crafting.Ingredient -import net.minecraft.world.item.equipment.ArmorMaterial -import net.minecraft.world.item.equipment.ArmorMaterials -import net.minecraft.world.item.equipment.ArmorType -import net.minecraft.world.item.equipment.EquipmentAsset -import kotlin.reflect.KProperty - -abstract class ItemImplementation { - // list of all items part of the class - abstract val items: List - - abstract fun onLoad() - - open fun generateRecipes(registries: HolderLookup.Provider, exporter: RecipeOutput) {} - - open fun generateItemTags(tags: ItemTagAdder) {} -// open fun generateItemModels(models: ItemModelGenerators) {} - - init { - register(this) - } - - companion object { - private val _all = mutableListOf() - - val all: List get() = _all - - fun register(impl: ItemImplementation) = _all.add(impl) - } -} - -abstract class GenericGearSet: ItemImplementation() { - abstract val materialName: String - - abstract val armorSet: ArmorSet - - open val materialKey: ResourceKey - get() = materialName.resourceKey() - - open val materialDefinition: GearMaterialDef - get() = GearMaterialDef.NETHERITE - - val armorMaterial: ArmorMaterial by lazy { - materialDefinition.toArmorMaterial() - } - - private val itemRecipes = mutableMapOf>() - - private val itemTags = mutableMapOf>>() - - private val itemModels = mutableMapOf() - - internal fun registerDatagen(item: Item, recipes: List, tags: List>, model: ModelSpec) { - itemRecipes[item] = recipes - itemTags[item] = tags - itemModels[item] = model - } - - - val modelSpecs: Map get() = itemModels - - override fun generateRecipes(registries: HolderLookup.Provider, exporter: RecipeOutput) { - val itemLookup: HolderGetter = registries.lookupOrThrow(Registries.ITEM) - - itemRecipes.forEach { (item, specs) -> - specs.forEach { spec -> - when (spec) { - is RecipeSpec.Smithing -> SmithingTransformRecipeBuilder.smithing( - Ingredient.of(spec.template), Ingredient.of(spec.base), Ingredient.of(spec.addition), - RecipeCategory.COMBAT, item - ).unlocks("has_addition", InventoryChangeTrigger.TriggerInstance.hasItems(spec.addition)) - .save(exporter, BuiltInRegistries.ITEM.getKey(item).path) - - is RecipeSpec.Shaped -> { - val builder = ShapedRecipeBuilder.shaped(itemLookup, RecipeCategory.MISC, item) - spec.pattern.forEach { builder.pattern(it) } - spec.key.forEach { (char, ingredientItem) -> - builder.define(char, ingredientItem) - - } - builder - .unlockedBy("has_ingredient", RecipeProvider.inventoryTrigger( - ItemPredicate.Builder().of(itemLookup, *spec.key.values.toTypedArray() ) - )) - .save(exporter) - } - } - } - } - } - - override fun generateItemTags(tags: ItemTagAdder) { - itemTags.forEach { (item, tagList) -> tagList.forEach { tags.add(it, item) } } - } - - fun fullSetEquippedBy(p: Player) = - p.getItemBySlot(EquipmentSlot.HEAD).item == armorSet.helmet && - p.getItemBySlot(EquipmentSlot.CHEST).item == armorSet.chestplate && - p.getItemBySlot(EquipmentSlot.LEGS).item == armorSet.leggings && - p.getItemBySlot(EquipmentSlot.FEET).item == armorSet.boots -} - -data class ArmorSet( - val helmet: Item, - val chestplate: Item, - val leggings: Item, - val boots: Item, -) - -object ModItems { - fun register( - name: String, - itemFactory: (Item.Properties) -> T, - settings: Item.Properties - ): T { - val itemKey = ResourceKey.create(Registries.ITEM, Identifier.fromNamespaceAndPath(Fantasysmp.MOD_ID, name)) - val item = itemFactory(settings.setId(itemKey)) - Registry.register(BuiltInRegistries.ITEM, itemKey, item) - return item - } - - val CUSTOM_CREATIVE_TAB_KEY: ResourceKey = ResourceKey.create( - BuiltInRegistries.CREATIVE_MODE_TAB.key(), Identifier.fromNamespaceAndPath(Fantasysmp.MOD_ID, "creative_tab") - ) - - val CUSTOM_CREATIVE_TAB: CreativeModeTab = FabricCreativeModeTab.builder() - .icon { ItemStack(Witherite.SWORD) } - .title(Component.translatable("Fantasysmp")) - .displayItems { _, output -> - ItemImplementation.all.forEach { - it.items.forEach(output::accept) - } - } - .build() - - private val eagerLoad = listOf( - Witherite, - Dragonite, - ) - - fun onLoad() { - // ensure that implementations such as creative tab are loaded and initialised - eagerLoad - ItemImplementation.all.forEach { it.onLoad() } - Registry.register(BuiltInRegistries.CREATIVE_MODE_TAB, CUSTOM_CREATIVE_TAB_KEY, CUSTOM_CREATIVE_TAB); - } -} - -data class GearMaterialDef( - val durability: Int = ArmorMaterials.NETHERITE.durability, - val defence: Map = ArmorMaterials.NETHERITE.defense, - val enchantability: Int = ArmorMaterials.NETHERITE.enchantmentValue, - val equipSound: Holder = ArmorMaterials.NETHERITE.equipSound, - val toughness: Float = ArmorMaterials.NETHERITE.toughness, - val knockbackResistance: Float = ArmorMaterials.NETHERITE.knockbackResistance, - val repairIngredient: TagKey = ArmorMaterials.NETHERITE.repairIngredient, - val assetId: ResourceKey = ArmorMaterials.NETHERITE.assetId, -) { - companion object { - val NETHERITE = GearMaterialDef( - durability = ArmorMaterials.NETHERITE.durability, - defence = ArmorMaterials.NETHERITE.defense, - enchantability = ArmorMaterials.NETHERITE.enchantmentValue, - equipSound = ArmorMaterials.NETHERITE.equipSound, - toughness = ArmorMaterials.NETHERITE.toughness, - knockbackResistance = ArmorMaterials.NETHERITE.knockbackResistance, - repairIngredient = ArmorMaterials.NETHERITE.repairIngredient, - assetId = ArmorMaterials.NETHERITE.assetId, - ) - val DIAMOND = GearMaterialDef( - durability = ArmorMaterials.DIAMOND.durability, - defence = ArmorMaterials.DIAMOND.defense, - enchantability = ArmorMaterials.DIAMOND.enchantmentValue, - equipSound = ArmorMaterials.DIAMOND.equipSound, - toughness = ArmorMaterials.DIAMOND.toughness, - knockbackResistance = ArmorMaterials.DIAMOND.knockbackResistance, - repairIngredient = ArmorMaterials.DIAMOND.repairIngredient, - assetId = ArmorMaterials.DIAMOND.assetId, - ) - } -} - - -fun GearMaterialDef.toArmorMaterial(): ArmorMaterial { - return ArmorMaterial( - durability, - defence, - enchantability, - equipSound, - toughness, - knockbackResistance, - repairIngredient, - assetId, - ) -} - -class ItemTagAdder(private val provider: FabricTagsProvider.ItemTagsProvider) { - fun add(tag: TagKey, vararg items: Item) { - provider.apply { items.forEach { add(tag, it) } } - } -} - -sealed interface RecipeSpec { - data class Smithing(val template: Item, val base: Item, val addition: Item) : RecipeSpec - data class Shaped(val pattern: List, val key: Map) : RecipeSpec -} - -sealed interface ModelSpec { - data object Generated : ModelSpec // plain flat 2D item icon - data object Handheld : ModelSpec // flat icon, held like a tool - data object None : ModelSpec // hand-written JSON already exists, skip auto-gen -} - -class GearItemBuilder( - private val owner: GenericGearSet, - val name: String, - private val itemFactory: (Item.Properties) -> T, -) { - private var properties = Item.Properties() - private val recipes = mutableListOf() - private val tags = mutableListOf>() - private var modelSpec: ModelSpec = ModelSpec.None - - fun model(spec: ModelSpec) { - modelSpec = spec - } - - fun properties(block: Item.Properties.() -> Item.Properties) { - properties = properties.block() - } - - fun smithingUpgrade(template: Item, base: Item, addition: Item) { - recipes += RecipeSpec.Smithing(template, base, addition) - } - - fun shapedRecipe(vararg pattern: String, key: Map) { - recipes += RecipeSpec.Shaped(pattern.toList(), key) - } - - fun tags(vararg tag: TagKey) { - tags += tag - } - - internal fun build(): T { - val item = ModItems.register(name, itemFactory, properties) - owner.registerDatagen(item, recipes, tags, modelSpec) - return item - } -} - -fun GenericGearSet.gearItem( - name: String, - itemFactory: (Item.Properties) -> T, - block: GearItemBuilder.() -> Unit = {}, -): GearItemBuilder = GearItemBuilder(this, name, itemFactory).apply(block) - -operator fun GearItemBuilder.provideDelegate( - thisRef: Any?, property: KProperty<*> -): Lazy = lazy { build() } - -operator fun Lazy.getValue(thisRef: Any?, property: KProperty<*>): T = value \ No newline at end of file diff --git a/src/main/kotlin/dev/zxq5/items/Dragonite.kt b/src/main/kotlin/dev/zxq5/items/sets/Dragonite.kt similarity index 92% rename from src/main/kotlin/dev/zxq5/items/Dragonite.kt rename to src/main/kotlin/dev/zxq5/items/sets/Dragonite.kt index f06491e..5583ce6 100644 --- a/src/main/kotlin/dev/zxq5/items/Dragonite.kt +++ b/src/main/kotlin/dev/zxq5/items/sets/Dragonite.kt @@ -1,16 +1,11 @@ -package dev.zxq5.items +package dev.zxq5.items.sets -import dev.zxq5.Fantasysmp +import dev.zxq5.items.core.ArmorSet +import dev.zxq5.items.core.GenericGearSet import dev.zxq5.items.ModItems.register import net.minecraft.core.component.DataComponents -import net.minecraft.core.particles.ParticleOptions -import net.minecraft.core.particles.ParticleTypes -import net.minecraft.resources.Identifier -import net.minecraft.resources.ResourceKey -import net.minecraft.server.level.ServerLevel import net.minecraft.sounds.SoundEvents import net.minecraft.sounds.SoundSource -import net.minecraft.tags.ItemTags import net.minecraft.util.Unit import net.minecraft.world.InteractionHand import net.minecraft.world.InteractionResult diff --git a/src/main/kotlin/dev/zxq5/items/Witherite.kt b/src/main/kotlin/dev/zxq5/items/sets/Witherite.kt similarity index 69% rename from src/main/kotlin/dev/zxq5/items/Witherite.kt rename to src/main/kotlin/dev/zxq5/items/sets/Witherite.kt index d34fe4f..038b79f 100644 --- a/src/main/kotlin/dev/zxq5/items/Witherite.kt +++ b/src/main/kotlin/dev/zxq5/items/sets/Witherite.kt @@ -1,11 +1,10 @@ -package dev.zxq5.items +package dev.zxq5.items.sets -import dev.zxq5.items.ModItems.register -import dev.zxq5.util.mcItemTag +import dev.zxq5.items.core.ArmorSet +import dev.zxq5.items.core.GenericGearSet +import dev.zxq5.items.core.gearItem import net.minecraft.core.component.DataComponents import net.minecraft.sounds.SoundEvents -import net.minecraft.tags.ItemTags -import net.minecraft.tags.TagKey import net.minecraft.world.InteractionHand import net.minecraft.world.InteractionResult import net.minecraft.world.effect.MobEffectInstance @@ -20,7 +19,6 @@ import net.minecraft.world.item.Items import net.minecraft.world.item.ToolMaterial import net.minecraft.world.item.component.BlocksAttacks import net.minecraft.world.item.component.UseCooldown -import net.minecraft.world.item.equipment.ArmorMaterial import net.minecraft.world.item.equipment.ArmorType import net.minecraft.world.level.Level import java.util.* @@ -31,9 +29,9 @@ object Witherite: GenericGearSet() { override val materialDefinition get() = super.materialDefinition.copy(assetId = materialKey) - val SHIELD = register("witherite_shield", ::Item, - Item.Properties() - .component(DataComponents.BLOCKS_ATTACKS, BlocksAttacks( + val SHIELD by gearItem("witherite_shield", ::Item) { + properties { + component(DataComponents.BLOCKS_ATTACKS, BlocksAttacks( 0.25f, // 5 tick warmup 1.0f, // standard disable cooldown listOf( @@ -49,19 +47,8 @@ object Witherite: GenericGearSet() { Optional.empty(), // default block sound Optional.empty() // default disable sound )) - ) - -// val SWORD = register("witherite_sword", ::WitheriteSword, -// Item.Properties() -// .enchantable(armorMaterial.enchantmentValue) -// .component(DataComponents.USE_COOLDOWN, UseCooldown(2f)) -// .sword(ToolMaterial.NETHERITE, 3F, -2.4F) -// ) - -// val AXE = register("witherite_axe", ::WitheriteWeapon, -// Item.Properties() -// .enchantable(armorMaterial.enchantmentValue) -// .axe(ToolMaterial.NETHERITE, 5F, -3F)) + } + } val SWORD by gearItem("witherite_sword", ::WitheriteSword) { properties { @@ -79,9 +66,6 @@ object Witherite: GenericGearSet() { .axe(ToolMaterial.NETHERITE, 3F, -3F) } smithingUpgrade(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE, Items.NETHERITE_AXE, Items.WITHER_SKELETON_SKULL) - tags( - mcItemTag("axes") - ) } val HELMET by gearItem("witherite_helmet", ::Item) { @@ -112,46 +96,12 @@ object Witherite: GenericGearSet() { smithingUpgrade(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE, Items.NETHERITE_BOOTS, Items.WITHER_SKELETON_SKULL) } -// -// val HELMET = register( -// "witherite_helmet", -// ::Item, -// Item.Properties().humanoidArmor(armorMaterial , ArmorType.HELMET) -// .enchantable(armorMaterial.enchantmentValue) -// .durability(ArmorType.HELMET.getDurability(armorMaterial.durability)) -// ) -// -// val CHESTPLATE = register( -// "witherite_chestplate", -// ::Item, -// -// Item.Properties() -// .enchantable(armorMaterial.enchantmentValue) -// .humanoidArmor(armorMaterial , ArmorType.CHESTPLATE) -// .durability(ArmorType.CHESTPLATE.getDurability(armorMaterial.durability)) -// ) -// -// val LEGGINGS = register( -// "witherite_leggings", -// ::Item, -// Item.Properties() -// .enchantable(armorMaterial.enchantmentValue) -// .humanoidArmor(armorMaterial , ArmorType.LEGGINGS) -// .durability(ArmorType.LEGGINGS.getDurability(armorMaterial.durability)) -// ) -// -// val BOOTS = register( -// "witherite_boots", -// ::Item, -// Item.Properties() -// .enchantable(armorMaterial.enchantmentValue) -// .humanoidArmor(armorMaterial , ArmorType.BOOTS) -// .durability(ArmorType.BOOTS.getDurability(armorMaterial.durability)) -// ) - override val armorSet: ArmorSet = ArmorSet(HELMET, CHESTPLATE, LEGGINGS, BOOTS) override val items = listOf(SWORD, AXE, SHIELD, HELMET, CHESTPLATE, LEGGINGS, BOOTS) - override fun onLoad() {} + + override fun onLoad() { + // empty function - this must be called to initialise the above properties ^ + } } open class WitheriteWeapon(properties: Properties): Item(properties) { diff --git a/src/main/kotlin/dev/zxq5/items/template/GenericGearSet.kt b/src/main/kotlin/dev/zxq5/items/template/GenericGearSet.kt new file mode 100644 index 0000000..c3494c9 --- /dev/null +++ b/src/main/kotlin/dev/zxq5/items/template/GenericGearSet.kt @@ -0,0 +1,93 @@ +package dev.zxq5.items.core + +import dev.zxq5.util.resourceKey +import net.minecraft.advancements.criterion.InventoryChangeTrigger +import net.minecraft.advancements.criterion.ItemPredicate +import net.minecraft.core.HolderGetter +import net.minecraft.core.HolderLookup +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.core.registries.Registries +import net.minecraft.data.recipes.RecipeCategory +import net.minecraft.data.recipes.RecipeOutput +import net.minecraft.data.recipes.RecipeProvider +import net.minecraft.data.recipes.ShapedRecipeBuilder +import net.minecraft.data.recipes.SmithingTransformRecipeBuilder +import net.minecraft.resources.ResourceKey +import net.minecraft.tags.TagKey +import net.minecraft.world.entity.EquipmentSlot +import net.minecraft.world.entity.player.Player +import net.minecraft.world.item.Item +import net.minecraft.world.item.crafting.Ingredient +import net.minecraft.world.item.equipment.ArmorMaterial +import net.minecraft.world.item.equipment.EquipmentAsset + +abstract class GenericGearSet: GenericItemSet() { + abstract val materialName: String + + abstract val armorSet: ArmorSet + + open val materialKey: ResourceKey + get() = materialName.resourceKey() + + open val materialDefinition: GearMaterialDef + get() = GearMaterialDef.NETHERITE + + val armorMaterial: ArmorMaterial by lazy { + materialDefinition.toArmorMaterial() + } + + private val itemRecipes = mutableMapOf>() + + private val itemTags = mutableMapOf>>() + + private val itemModels = mutableMapOf() + + internal fun registerDatagen(item: Item, recipes: List, tags: List>, model: ModelSpec) { + itemRecipes[item] = recipes + itemTags[item] = tags + itemModels[item] = model + } + + + val modelSpecs: Map get() = itemModels + + override fun generateRecipes(registries: HolderLookup.Provider, exporter: RecipeOutput) { + val itemLookup: HolderGetter = registries.lookupOrThrow(Registries.ITEM) + + itemRecipes.forEach { (item, specs) -> + specs.forEach { spec -> + when (spec) { + is RecipeSpec.Smithing -> SmithingTransformRecipeBuilder.smithing( + Ingredient.of(spec.template), Ingredient.of(spec.base), Ingredient.of(spec.addition), + RecipeCategory.COMBAT, item + ).unlocks("has_addition", InventoryChangeTrigger.TriggerInstance.hasItems(spec.addition)) + .save(exporter, BuiltInRegistries.ITEM.getKey(item).path) + + is RecipeSpec.Shaped -> { + val builder = ShapedRecipeBuilder.shaped(itemLookup, RecipeCategory.MISC, item) + spec.pattern.forEach { builder.pattern(it) } + spec.key.forEach { (char, ingredientItem) -> + builder.define(char, ingredientItem) + + } + builder + .unlockedBy("has_ingredient", RecipeProvider.inventoryTrigger( + ItemPredicate.Builder().of(itemLookup, *spec.key.values.toTypedArray() ) + )) + .save(exporter) + } + } + } + } + } + + override fun generateItemTags(tags: ItemTagAdder) { + itemTags.forEach { (item, tagList) -> tagList.forEach { tags.add(it, item) } } + } + + fun fullSetEquippedBy(p: Player) = + p.getItemBySlot(EquipmentSlot.HEAD).item == armorSet.helmet && + p.getItemBySlot(EquipmentSlot.CHEST).item == armorSet.chestplate && + p.getItemBySlot(EquipmentSlot.LEGS).item == armorSet.leggings && + p.getItemBySlot(EquipmentSlot.FEET).item == armorSet.boots +} \ No newline at end of file diff --git a/src/main/kotlin/dev/zxq5/items/template/GenericItemSet.kt b/src/main/kotlin/dev/zxq5/items/template/GenericItemSet.kt new file mode 100644 index 0000000..4e75e92 --- /dev/null +++ b/src/main/kotlin/dev/zxq5/items/template/GenericItemSet.kt @@ -0,0 +1,29 @@ +package dev.zxq5.items.core + +import net.minecraft.core.HolderLookup +import net.minecraft.data.recipes.RecipeOutput +import net.minecraft.world.item.Item + +abstract class GenericItemSet { + // list of all items part of the class + abstract val items: List + + abstract fun onLoad() + + open fun generateRecipes(registries: HolderLookup.Provider, exporter: RecipeOutput) {} + + open fun generateItemTags(tags: ItemTagAdder) {} +// open fun generateItemModels(models: ItemModelGenerators) {} + + init { + register(this) + } + + companion object { + private val _all = mutableListOf() + + val all: List get() = _all + + fun register(impl: GenericItemSet) = _all.add(impl) + } +} \ No newline at end of file