diff --git a/src/client/kotlin/dev/zxq5/client/FantasysmpDataGenerator.kt b/src/client/kotlin/dev/zxq5/client/FantasysmpDataGenerator.kt index 50a124c..780b18f 100644 --- a/src/client/kotlin/dev/zxq5/client/FantasysmpDataGenerator.kt +++ b/src/client/kotlin/dev/zxq5/client/FantasysmpDataGenerator.kt @@ -1,9 +1,8 @@ package dev.zxq5.client -import dev.zxq5.ModItemTagProvider import dev.zxq5.ModRecipeProvider -import dev.zxq5.items.GenericGearSet -import dev.zxq5.items.ItemImplementation +import dev.zxq5.items.template.GenericItemSet +import dev.zxq5.items.template.ModelSpec import net.fabricmc.fabric.api.client.datagen.v1.provider.FabricModelProvider import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator @@ -17,27 +16,27 @@ object FantasysmpDataGenerator : DataGeneratorEntrypoint { val pack = fabricDataGenerator.createPack() pack.addProvider(::ModRecipeProvider) // main-safe, can live in either sourceSet // pack.addProvider(::ModItemTagProvider) // main-safe -// pack.addProvider(::ModModelProvider) // needs client, since ItemModelGenerators is client-only + pack.addProvider(::ModModelProvider) // needs client, since ItemModelGenerators is client-only } } // client sourceSet -//class ModModelProvider(output: FabricPackOutput) : FabricModelProvider(output) { -// override fun generateBlockStateModels(blockModels: BlockModelGenerators) {} -// -// override fun generateItemModels(itemModels: ItemModelGenerators) { -// ItemImplementation.all.forEach { it.generateModels(itemModels) } -// } -//} +class ModModelProvider(output: FabricPackOutput) : FabricModelProvider(output) { + override fun generateBlockStateModels(blockModels: BlockModelGenerators) {} + + override fun generateItemModels(itemModels: ItemModelGenerators) { + GenericItemSet.all.forEach { it.generateModels(itemModels) } + } +} // client sourceSet -//fun ItemImplementation.generateModels(itemModels: ItemModelGenerators) { -// if (this !is GenericGearSet) return -// modelSpecs.forEach { (item, spec) -> -// when (spec) { -// ModelSpec.Generated -> itemModels.generateFlatItem(item, ModelTemplates.FLAT_ITEM) -// ModelSpec.Handheld -> itemModels.generateFlatItem(item, ModelTemplates.FLAT_HANDHELD_ITEM) -// ModelSpec.None -> {} // nothing to do — hand-authored JSON covers it -// } -// } -//} \ No newline at end of file +fun GenericItemSet.generateModels(itemModels: ItemModelGenerators) { + if (this !is GenericItemSet) return + modelSpecs.forEach { (item, spec) -> + when (spec) { + ModelSpec.Generated -> itemModels.generateFlatItem(item, ModelTemplates.FLAT_ITEM) + ModelSpec.Handheld -> itemModels.generateFlatItem(item, ModelTemplates.FLAT_HANDHELD_ITEM) + ModelSpec.None -> {} // nothing to do — hand-authored JSON covers it + } + } +} \ No newline at end of file diff --git a/src/main/generated/data/fantasysmp/advancement/recipes/combat/dragonite_boots.json b/src/main/generated/data/fantasysmp/advancement/recipes/combat/dragonite_boots.json new file mode 100644 index 0000000..6535eac --- /dev/null +++ b/src/main/generated/data/fantasysmp/advancement/recipes/combat/dragonite_boots.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_addition": { + "conditions": { + "items": [ + { + "items": "fantasysmp:dragonite_ingot" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "fantasysmp:dragonite_boots" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_addition" + ] + ], + "rewards": { + "recipes": [ + "fantasysmp:dragonite_boots" + ] + } +} \ No newline at end of file diff --git a/src/main/generated/data/fantasysmp/advancement/recipes/combat/dragonite_chestplate.json b/src/main/generated/data/fantasysmp/advancement/recipes/combat/dragonite_chestplate.json new file mode 100644 index 0000000..d554f84 --- /dev/null +++ b/src/main/generated/data/fantasysmp/advancement/recipes/combat/dragonite_chestplate.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_addition": { + "conditions": { + "items": [ + { + "items": "fantasysmp:dragonite_ingot" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "fantasysmp:dragonite_chestplate" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_addition" + ] + ], + "rewards": { + "recipes": [ + "fantasysmp:dragonite_chestplate" + ] + } +} \ No newline at end of file diff --git a/src/main/generated/data/fantasysmp/advancement/recipes/combat/dragonite_helmet.json b/src/main/generated/data/fantasysmp/advancement/recipes/combat/dragonite_helmet.json new file mode 100644 index 0000000..69e28d3 --- /dev/null +++ b/src/main/generated/data/fantasysmp/advancement/recipes/combat/dragonite_helmet.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_addition": { + "conditions": { + "items": [ + { + "items": "fantasysmp:dragonite_ingot" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "fantasysmp:dragonite_helmet" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_addition" + ] + ], + "rewards": { + "recipes": [ + "fantasysmp:dragonite_helmet" + ] + } +} \ No newline at end of file diff --git a/src/main/generated/data/fantasysmp/advancement/recipes/combat/dragonite_leggings.json b/src/main/generated/data/fantasysmp/advancement/recipes/combat/dragonite_leggings.json new file mode 100644 index 0000000..0a084de --- /dev/null +++ b/src/main/generated/data/fantasysmp/advancement/recipes/combat/dragonite_leggings.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_addition": { + "conditions": { + "items": [ + { + "items": "fantasysmp:dragonite_ingot" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "fantasysmp:dragonite_leggings" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_addition" + ] + ], + "rewards": { + "recipes": [ + "fantasysmp:dragonite_leggings" + ] + } +} \ No newline at end of file diff --git a/src/main/generated/data/fantasysmp/advancement/recipes/combat/dragonite_sword.json b/src/main/generated/data/fantasysmp/advancement/recipes/combat/dragonite_sword.json new file mode 100644 index 0000000..7d41805 --- /dev/null +++ b/src/main/generated/data/fantasysmp/advancement/recipes/combat/dragonite_sword.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_addition": { + "conditions": { + "items": [ + { + "items": "fantasysmp:dragonite_ingot" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "fantasysmp:dragonite_sword" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_addition" + ] + ], + "rewards": { + "recipes": [ + "fantasysmp:dragonite_sword" + ] + } +} \ No newline at end of file diff --git a/src/main/generated/data/fantasysmp/recipe/dragonite_boots.json b/src/main/generated/data/fantasysmp/recipe/dragonite_boots.json new file mode 100644 index 0000000..9feef94 --- /dev/null +++ b/src/main/generated/data/fantasysmp/recipe/dragonite_boots.json @@ -0,0 +1,9 @@ +{ + "type": "minecraft:smithing_transform", + "addition": "fantasysmp:dragonite_ingot", + "base": "minecraft:netherite_boots", + "result": { + "id": "fantasysmp:dragonite_boots" + }, + "template": "minecraft:crying_obsidian" +} \ No newline at end of file diff --git a/src/main/generated/data/fantasysmp/recipe/dragonite_chestplate.json b/src/main/generated/data/fantasysmp/recipe/dragonite_chestplate.json new file mode 100644 index 0000000..cc1f142 --- /dev/null +++ b/src/main/generated/data/fantasysmp/recipe/dragonite_chestplate.json @@ -0,0 +1,9 @@ +{ + "type": "minecraft:smithing_transform", + "addition": "fantasysmp:dragonite_ingot", + "base": "minecraft:netherite_chestplate", + "result": { + "id": "fantasysmp:dragonite_chestplate" + }, + "template": "minecraft:elytra" +} \ No newline at end of file diff --git a/src/main/generated/data/fantasysmp/recipe/dragonite_helmet.json b/src/main/generated/data/fantasysmp/recipe/dragonite_helmet.json new file mode 100644 index 0000000..40f0721 --- /dev/null +++ b/src/main/generated/data/fantasysmp/recipe/dragonite_helmet.json @@ -0,0 +1,9 @@ +{ + "type": "minecraft:smithing_transform", + "addition": "fantasysmp:dragonite_ingot", + "base": "minecraft:netherite_helmet", + "result": { + "id": "fantasysmp:dragonite_helmet" + }, + "template": "minecraft:dragon_head" +} \ No newline at end of file diff --git a/src/main/generated/data/fantasysmp/recipe/dragonite_leggings.json b/src/main/generated/data/fantasysmp/recipe/dragonite_leggings.json new file mode 100644 index 0000000..ded850e --- /dev/null +++ b/src/main/generated/data/fantasysmp/recipe/dragonite_leggings.json @@ -0,0 +1,9 @@ +{ + "type": "minecraft:smithing_transform", + "addition": "fantasysmp:dragonite_ingot", + "base": "minecraft:netherite_leggings", + "result": { + "id": "fantasysmp:dragonite_leggings" + }, + "template": "minecraft:crying_obsidian" +} \ No newline at end of file diff --git a/src/main/generated/data/fantasysmp/recipe/dragonite_sword.json b/src/main/generated/data/fantasysmp/recipe/dragonite_sword.json new file mode 100644 index 0000000..81f9522 --- /dev/null +++ b/src/main/generated/data/fantasysmp/recipe/dragonite_sword.json @@ -0,0 +1,9 @@ +{ + "type": "minecraft:smithing_transform", + "addition": "fantasysmp:dragonite_ingot", + "base": "minecraft:netherite_sword", + "result": { + "id": "fantasysmp:dragonite_sword" + }, + "template": "minecraft:end_crystal" +} \ No newline at end of file diff --git a/src/main/kotlin/dev/zxq5/Fantasysmp.kt b/src/main/kotlin/dev/zxq5/Fantasysmp.kt index dd6093e..722bc64 100644 --- a/src/main/kotlin/dev/zxq5/Fantasysmp.kt +++ b/src/main/kotlin/dev/zxq5/Fantasysmp.kt @@ -1,10 +1,10 @@ package dev.zxq5 -import dev.zxq5.items.ItemImplementation -import dev.zxq5.items.ItemTagAdder +import dev.zxq5.items.template.GenericItemSet +import dev.zxq5.items.template.ItemTagAdder import dev.zxq5.items.ModItems -import dev.zxq5.items.Witherite -//import dev.zxq5.items.Witherite +import dev.zxq5.items.sets.Witherite +//import dev.zxq5.items.sets.Witherite import net.fabricmc.api.ModInitializer import net.fabricmc.fabric.api.datagen.v1.FabricPackOutput import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider @@ -51,7 +51,7 @@ class ModRecipeProvider( return object : RecipeProvider(registries, exporter) { override fun buildRecipes() { print("GENERATING ALL RECIPES") - ItemImplementation.all.forEach { + GenericItemSet.all.forEach { print("GENERATING RECIPES FOR" + it.items.toString()) it.generateRecipes(registries, exporter) } @@ -66,6 +66,6 @@ class ModItemTagProvider(output: FabricPackOutput, registriesFuture: Completable FabricTagsProvider.ItemTagsProvider(output, registriesFuture) { override fun addTags(provider: HolderLookup.Provider) { val adder = ItemTagAdder(this) - ItemImplementation.all.forEach { it.generateItemTags(adder) } + GenericItemSet.all.forEach { it.generateItemTags(adder) } } } \ No newline at end of file diff --git a/src/main/kotlin/dev/zxq5/items/FantasysmpItemRegistry.kt b/src/main/kotlin/dev/zxq5/items/FantasysmpItemRegistry.kt index 7871391..03df05e 100644 --- a/src/main/kotlin/dev/zxq5/items/FantasysmpItemRegistry.kt +++ b/src/main/kotlin/dev/zxq5/items/FantasysmpItemRegistry.kt @@ -1,8 +1,9 @@ package dev.zxq5.items import dev.zxq5.Fantasysmp -import dev.zxq5.items.core.GenericGearSet -import dev.zxq5.items.core.GenericItemSet +import dev.zxq5.items.template.GenericItemSet +import dev.zxq5.items.sets.Dragonite +import dev.zxq5.items.sets.Witherite import net.fabricmc.fabric.api.creativetab.v1.FabricCreativeModeTab import net.minecraft.core.Registry import net.minecraft.core.registries.BuiltInRegistries @@ -18,7 +19,7 @@ object ModItems { fun register( name: String, itemFactory: (Item.Properties) -> T, - settings: Item.Properties + settings: Item.Properties = Item.Properties(), ): T { val itemKey = ResourceKey.create(Registries.ITEM, Identifier.fromNamespaceAndPath(Fantasysmp.MOD_ID, name)) val item = itemFactory(settings.setId(itemKey)) @@ -40,7 +41,7 @@ object ModItems { } .build() - private val eagerLoad = listOf( + private val eagerLoad = listOf( Witherite, Dragonite, ) diff --git a/src/main/kotlin/dev/zxq5/items/sets/Dragonite.kt b/src/main/kotlin/dev/zxq5/items/sets/Dragonite.kt index 5583ce6..c05ffa5 100644 --- a/src/main/kotlin/dev/zxq5/items/sets/Dragonite.kt +++ b/src/main/kotlin/dev/zxq5/items/sets/Dragonite.kt @@ -1,20 +1,29 @@ package dev.zxq5.items.sets -import dev.zxq5.items.core.ArmorSet -import dev.zxq5.items.core.GenericGearSet +import dev.zxq5.items.template.ArmorSet +import dev.zxq5.items.template.GenericGearSet import dev.zxq5.items.ModItems.register +import dev.zxq5.items.template.registerItem +import dev.zxq5.items.template.provideDelegate import net.minecraft.core.component.DataComponents +import net.minecraft.core.particles.ParticleTypes +import net.minecraft.core.particles.PowerParticleOption +import net.minecraft.server.level.ServerLevel import net.minecraft.sounds.SoundEvents import net.minecraft.sounds.SoundSource import net.minecraft.util.Unit import net.minecraft.world.InteractionHand import net.minecraft.world.InteractionResult +import net.minecraft.world.damagesource.DamageSource +import net.minecraft.world.entity.Entity import net.minecraft.world.entity.player.Player import net.minecraft.world.item.Item +import net.minecraft.world.item.Items import net.minecraft.world.item.ToolMaterial import net.minecraft.world.item.component.UseCooldown import net.minecraft.world.item.equipment.* import net.minecraft.world.level.Level +import kotlin.getValue object Dragonite: GenericGearSet() { @@ -22,60 +31,55 @@ object Dragonite: GenericGearSet() { override val materialDefinition get() = super.materialDefinition.copy(assetId = materialKey) - val SWORD = register("dragonite_sword", ::DragoniteSword, - Item.Properties() - .enchantable(armorMaterial.enchantmentValue) - .component(DataComponents.USE_COOLDOWN, UseCooldown(2f)) - .sword(ToolMaterial.NETHERITE, 3.0F, -2.4F) - ) - val HELMET = register( - "dragonite_helmet", - ::Item, - Item.Properties() - .enchantable(armorMaterial.enchantmentValue) - .humanoidArmor(armorMaterial , ArmorType.HELMET) - .durability(ArmorType.HELMET.getDurability(armorMaterial.durability)) - ) + val SCALE by registerItem("dragon_scale", ::Item) + + val INGOT by registerItem("dragonite_ingot", ::Item) { + shapedRecipe("SBS", "CXC", "SBS", key=mapOf( + 'S' to Items.SHULKER_SHELL, + 'X' to SCALE, + 'B' to Items.DRAGON_BREATH, + 'C' to Items.CHORUS_FRUIT + )) + } - val CHESTPLATE = register( - "dragonite_chestplate", - ::Item, - Item.Properties() - .enchantable(armorMaterial.enchantmentValue) - .humanoidArmor(armorMaterial , ArmorType.CHESTPLATE) - .component(DataComponents.GLIDER, Unit.INSTANCE) - .durability(ArmorType.CHESTPLATE.getDurability(armorMaterial.durability)) - ) + val SWORD by registerItem("dragonite_sword", ::DragoniteSword) { + properties { + enchantable(armorMaterial.enchantmentValue) + .component(DataComponents.USE_COOLDOWN, UseCooldown(2f)) + .sword(ToolMaterial.NETHERITE, 3F, -2.4F) + } + smithingUpgrade(Items.END_CRYSTAL, Items.NETHERITE_SWORD, INGOT) + } - val LEGGINGS = register( - "dragonite_leggings", - ::Item, - Item.Properties() - .enchantable(armorMaterial.enchantmentValue) - .humanoidArmor(armorMaterial , ArmorType.LEGGINGS) - .durability(ArmorType.LEGGINGS.getDurability(armorMaterial.durability)) - ) + val HELMET by registerItem("dragonite_helmet", ::Item) { + properties { + humanoidArmor(armorMaterial, ArmorType.HELMET) + } + smithingUpgrade(Items.DRAGON_HEAD, Items.NETHERITE_HELMET, INGOT) + } - val BOOTS = register( - "dragonite_boots", - ::Item, - Item.Properties() - .enchantable(armorMaterial.enchantmentValue) - .humanoidArmor(armorMaterial , ArmorType.BOOTS) - .durability(ArmorType.BOOTS.getDurability(armorMaterial.durability)) - ) + val CHESTPLATE by registerItem("dragonite_chestplate", ::Item) { + properties { + humanoidArmor(armorMaterial, ArmorType.CHESTPLATE) + .component(DataComponents.GLIDER, Unit.INSTANCE) + .component(DataComponents.UNBREAKABLE, Unit.INSTANCE) + } + smithingUpgrade(Items.ELYTRA, Items.NETHERITE_CHESTPLATE, INGOT) + } - val INGOT = register( - "dragonite_ingot", - ::Item, - Item.Properties() - ) + val LEGGINGS by registerItem("dragonite_leggings", ::Item) { + properties { + humanoidArmor(armorMaterial, ArmorType.LEGGINGS) + } + smithingUpgrade(Items.CRYING_OBSIDIAN, Items.NETHERITE_LEGGINGS, INGOT) + } - val SCALE = register( - "dragon_scale", - ::Item, - Item.Properties() - ) + val BOOTS by registerItem("dragonite_boots", ::Item) { + properties { + humanoidArmor(armorMaterial, ArmorType.BOOTS) + } + smithingUpgrade(Items.CRYING_OBSIDIAN, Items.NETHERITE_BOOTS, INGOT) + } override val armorSet: ArmorSet = ArmorSet(HELMET, CHESTPLATE, LEGGINGS, BOOTS) override val items = listOf(SWORD, HELMET, CHESTPLATE, LEGGINGS, BOOTS, SCALE, INGOT) @@ -95,13 +99,41 @@ class DragoniteSword(properties: Properties) : Item(properties) { level.playSound(null, user.x, user.y, user.z, SoundEvents.ENDER_DRAGON_FLAP, SoundSource.PLAYERS, 1.0f, 1.0f) - // TODO("fix this broken ahh code. WHY TF DOES FABRIC HAVE NO DOCUMENTATION FOR BASICALLY ANYTHING.") -// (level as? ServerLevel)?.sendParticles( -// ParticleTypes.DRAGON_BREATH as ParticleOptions, -// user.x, user.y, user.z, -// 100, 0.5, 0.5, 0.5, 0.5 -// ) + (level as? ServerLevel)?.sendParticles( + PowerParticleOption.create(ParticleTypes.DRAGON_BREATH, 1.0f), + user.x, user.y, user.z, + 100, 0.5, 0.5, 0.5, 0.5 + ) return InteractionResult.SUCCESS } + + override fun getAttackDamageBonus(victim: Entity, damage: Float, damageSource: DamageSource): Float { + val superBonus = super.getAttackDamageBonus(victim, damage, damageSource) // 0f from vanilla, but respect it anyway + val attacker = damageSource.entity as? Player ?: return superBonus + if (Dragonite.fullSetEquippedBy(attacker)) return superBonus + if (attacker.level().isClientSide) return superBonus + if (!attacker.isFallFlying) return superBonus + + val speed = attacker.deltaMovement.length() + if (speed < MIN_DIVE_SPEED) return superBonus + + val clampedSpeed = speed.coerceAtMost(MAX_DIVE_SPEED) + val t = (clampedSpeed - MIN_DIVE_SPEED) / (MAX_DIVE_SPEED - MIN_DIVE_SPEED) + val multiplier = 1.0f + (t * MAX_BONUS_MULTIPLIER).toFloat() + + // damage is the current total (base + crit + whatever ran before this hook). + // We want final = damage * multiplier, and this method adds its return value on top of damage, + // so the bonus we need to return is exactly (damage * multiplier) - damage. + val extraBonus = damage * (multiplier - 1f) + + println("base: $damage bonus: ${superBonus + extraBonus}") + return superBonus + extraBonus + } + + companion object { + const val MIN_DIVE_SPEED = 0.3 + const val MAX_DIVE_SPEED = 1.1 + const val MAX_BONUS_MULTIPLIER = 2.0f + } } diff --git a/src/main/kotlin/dev/zxq5/items/sets/Witherite.kt b/src/main/kotlin/dev/zxq5/items/sets/Witherite.kt index 038b79f..17df7be 100644 --- a/src/main/kotlin/dev/zxq5/items/sets/Witherite.kt +++ b/src/main/kotlin/dev/zxq5/items/sets/Witherite.kt @@ -1,8 +1,9 @@ package dev.zxq5.items.sets -import dev.zxq5.items.core.ArmorSet -import dev.zxq5.items.core.GenericGearSet -import dev.zxq5.items.core.gearItem +import dev.zxq5.items.template.ArmorSet +import dev.zxq5.items.template.GenericGearSet +import dev.zxq5.items.template.registerItem +import dev.zxq5.items.template.provideDelegate import net.minecraft.core.component.DataComponents import net.minecraft.sounds.SoundEvents import net.minecraft.world.InteractionHand @@ -29,7 +30,7 @@ object Witherite: GenericGearSet() { override val materialDefinition get() = super.materialDefinition.copy(assetId = materialKey) - val SHIELD by gearItem("witherite_shield", ::Item) { + val SHIELD by registerItem("witherite_shield", ::Item) { properties { component(DataComponents.BLOCKS_ATTACKS, BlocksAttacks( 0.25f, // 5 tick warmup @@ -48,9 +49,10 @@ object Witherite: GenericGearSet() { Optional.empty() // default disable sound )) } + smithingUpgrade(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE, Items.NETHERITE_SWORD, Items.WITHER_SKELETON_SKULL) } - val SWORD by gearItem("witherite_sword", ::WitheriteSword) { + val SWORD by registerItem("witherite_sword", ::WitheriteSword) { properties { enchantable(armorMaterial.enchantmentValue) .component(DataComponents.USE_COOLDOWN, UseCooldown(2f)) @@ -59,7 +61,7 @@ object Witherite: GenericGearSet() { smithingUpgrade(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE, Items.NETHERITE_SWORD, Items.WITHER_SKELETON_SKULL) } - val AXE by gearItem("witherite_axe", ::WitheriteWeapon) { + val AXE by registerItem("witherite_axe", ::WitheriteWeapon) { properties { enchantable(armorMaterial.enchantmentValue) .component(DataComponents.USE_COOLDOWN, UseCooldown(2f)) @@ -68,28 +70,28 @@ object Witherite: GenericGearSet() { smithingUpgrade(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE, Items.NETHERITE_AXE, Items.WITHER_SKELETON_SKULL) } - val HELMET by gearItem("witherite_helmet", ::Item) { + val HELMET by registerItem("witherite_helmet", ::Item) { properties { humanoidArmor(armorMaterial, ArmorType.HELMET) } smithingUpgrade(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE, Items.NETHERITE_HELMET, Items.WITHER_SKELETON_SKULL) } - val CHESTPLATE by gearItem("witherite_chestplate", ::Item) { + val CHESTPLATE by registerItem("witherite_chestplate", ::Item) { properties { humanoidArmor(armorMaterial, ArmorType.CHESTPLATE) } smithingUpgrade(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE, Items.NETHERITE_CHESTPLATE, Items.WITHER_SKELETON_SKULL) } - val LEGGINGS by gearItem("witherite_leggings", ::Item) { + val LEGGINGS by registerItem("witherite_leggings", ::Item) { properties { humanoidArmor(armorMaterial, ArmorType.LEGGINGS) } smithingUpgrade(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE, Items.NETHERITE_LEGGINGS, Items.WITHER_SKELETON_SKULL) } - val BOOTS by gearItem("witherite_boots", ::Item) { + val BOOTS by registerItem("witherite_boots", ::Item) { properties { humanoidArmor(armorMaterial, ArmorType.BOOTS) } diff --git a/src/main/kotlin/dev/zxq5/items/template/GenericGearSet.kt b/src/main/kotlin/dev/zxq5/items/template/GenericGearSet.kt index c3494c9..6204993 100644 --- a/src/main/kotlin/dev/zxq5/items/template/GenericGearSet.kt +++ b/src/main/kotlin/dev/zxq5/items/template/GenericGearSet.kt @@ -1,24 +1,16 @@ -package dev.zxq5.items.core +package dev.zxq5.items.template 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.core.Holder 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.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 abstract class GenericGearSet: GenericItemSet() { @@ -36,58 +28,64 @@ abstract class GenericGearSet: GenericItemSet() { 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 +} + +data class ArmorSet( + val helmet: Item, + val chestplate: Item, + val leggings: Item, + val boots: Item, +) + +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, + ) +} + diff --git a/src/main/kotlin/dev/zxq5/items/template/GenericItemSet.kt b/src/main/kotlin/dev/zxq5/items/template/GenericItemSet.kt index 4e75e92..a54be11 100644 --- a/src/main/kotlin/dev/zxq5/items/template/GenericItemSet.kt +++ b/src/main/kotlin/dev/zxq5/items/template/GenericItemSet.kt @@ -1,8 +1,24 @@ -package dev.zxq5.items.core +package dev.zxq5.items.template +import dev.zxq5.items.ModItems +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.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.tags.TagKey import net.minecraft.world.item.Item +import net.minecraft.world.item.crafting.Ingredient +import kotlin.collections.component1 +import kotlin.collections.component2 +import kotlin.reflect.KProperty abstract class GenericItemSet { // list of all items part of the class @@ -10,10 +26,49 @@ abstract class GenericItemSet { abstract fun onLoad() - open fun generateRecipes(registries: HolderLookup.Provider, exporter: RecipeOutput) {} + private val itemRecipes = mutableMapOf>() + private val itemTags = mutableMapOf>>() + private val itemModels = mutableMapOf() + val modelSpecs: Map get() = itemModels + internal fun registerDatagen(item: Item, recipes: List, tags: List>, model: ModelSpec) { + itemRecipes[item] = recipes + itemTags[item] = tags + itemModels[item] = model + } - open fun generateItemTags(tags: ItemTagAdder) {} -// open fun generateItemModels(models: ItemModelGenerators) {} + open fun generateItemTags(tags: ItemTagAdder) { + itemTags.forEach { (item, tagList) -> tagList.forEach { tags.add(it, item) } } + } + + open 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) + } + } + } + } + } init { register(this) @@ -26,4 +81,70 @@ abstract class GenericItemSet { fun register(impl: GenericItemSet) = _all.add(impl) } -} \ No newline at end of file +} + +class CustomItemBuilder( + private val owner: GenericItemSet, + 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 + } +} + +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 // handwritten JSON already exists, skip auto-gen +} + +fun GenericItemSet.registerItem( + name: String, + itemFactory: (Item.Properties) -> T, + block: CustomItemBuilder.() -> Unit = {}, +): CustomItemBuilder = CustomItemBuilder(this, name, itemFactory).apply(block) + +operator fun CustomItemBuilder.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/resources/data/fantasysmp/recipe/dragonite_boots.json b/src/main/resources/dragonite_boots.json similarity index 100% rename from src/main/resources/data/fantasysmp/recipe/dragonite_boots.json rename to src/main/resources/dragonite_boots.json diff --git a/src/main/resources/data/fantasysmp/recipe/dragonite_chestplate.json b/src/main/resources/dragonite_chestplate.json similarity index 100% rename from src/main/resources/data/fantasysmp/recipe/dragonite_chestplate.json rename to src/main/resources/dragonite_chestplate.json diff --git a/src/main/resources/data/fantasysmp/recipe/dragonite_helmet.json b/src/main/resources/dragonite_helmet.json similarity index 100% rename from src/main/resources/data/fantasysmp/recipe/dragonite_helmet.json rename to src/main/resources/dragonite_helmet.json diff --git a/src/main/resources/data/fantasysmp/recipe/dragonite_ingot.json b/src/main/resources/dragonite_ingot.json similarity index 100% rename from src/main/resources/data/fantasysmp/recipe/dragonite_ingot.json rename to src/main/resources/dragonite_ingot.json diff --git a/src/main/resources/data/fantasysmp/recipe/dragonite_leggings.json b/src/main/resources/dragonite_leggings.json similarity index 100% rename from src/main/resources/data/fantasysmp/recipe/dragonite_leggings.json rename to src/main/resources/dragonite_leggings.json diff --git a/src/main/resources/data/fantasysmp/recipe/dragonite_sword.json b/src/main/resources/dragonite_sword.json similarity index 100% rename from src/main/resources/data/fantasysmp/recipe/dragonite_sword.json rename to src/main/resources/dragonite_sword.json