From db1f6cc5ae9678b76a91639189a8dec99278526e Mon Sep 17 00:00:00 2001 From: zxq5 Date: Mon, 6 Jul 2026 07:14:36 +0100 Subject: [PATCH] slightly broken but very refactored. TODO: make the code that should run on the clientside run on the clientside --- .../zxq5/client/FantasysmpDataGenerator.kt | 18 ++ src/main/kotlin/dev/zxq5/Fantasysmp.kt | 65 ++--- src/main/kotlin/dev/zxq5/items/Dragonite.kt | 25 +- src/main/kotlin/dev/zxq5/items/Items.kt | 223 ++++++++++++++++-- src/main/kotlin/dev/zxq5/items/Witherite.kt | 114 ++++----- src/main/kotlin/dev/zxq5/util/util.kt | 12 + .../fantasysmp/textures/item/dragon_scale.png | Bin 589 -> 607 bytes .../textures/item/dragon_scale.png~ | Bin 0 -> 589 bytes .../textures/item/dragonite_ingot.png | Bin 451 -> 552 bytes .../textures/item/dragonite_ingot.png~ | Bin 0 -> 552 bytes .../tags/item/enchantable/durability.json | 12 + .../minecraft/tags/item/trimmable_armor.json | 12 + 12 files changed, 348 insertions(+), 133 deletions(-) create mode 100644 src/main/kotlin/dev/zxq5/util/util.kt create mode 100644 src/main/resources/assets/fantasysmp/textures/item/dragon_scale.png~ create mode 100644 src/main/resources/assets/fantasysmp/textures/item/dragonite_ingot.png~ create mode 100644 src/main/resources/data/minecraft/tags/item/enchantable/durability.json create mode 100644 src/main/resources/data/minecraft/tags/item/trimmable_armor.json diff --git a/src/client/kotlin/dev/zxq5/client/FantasysmpDataGenerator.kt b/src/client/kotlin/dev/zxq5/client/FantasysmpDataGenerator.kt index bbc0059..27427b7 100644 --- a/src/client/kotlin/dev/zxq5/client/FantasysmpDataGenerator.kt +++ b/src/client/kotlin/dev/zxq5/client/FantasysmpDataGenerator.kt @@ -1,11 +1,29 @@ package dev.zxq5.client +import dev.zxq5.ModItemTagProvider +import dev.zxq5.ModRecipeProvider +import dev.zxq5.items.ItemImplementation +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 +import net.fabricmc.fabric.api.datagen.v1.FabricPackOutput +import net.minecraft.client.data.models.BlockModelGenerators +import net.minecraft.client.data.models.ItemModelGenerators object FantasysmpDataGenerator : DataGeneratorEntrypoint { override fun onInitializeDataGenerator(fabricDataGenerator: FabricDataGenerator) { + 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 } } +// client sourceSet +class ModModelProvider(output: FabricPackOutput) : FabricModelProvider(output) { + override fun generateBlockStateModels(blockModels: BlockModelGenerators) {} + override fun generateItemModels(itemModels: ItemModelGenerators) { + ItemImplementation.all.forEach { it.generateItemModels(itemModels) } + } +} \ 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 d784d7e..07f43d0 100644 --- a/src/main/kotlin/dev/zxq5/Fantasysmp.kt +++ b/src/main/kotlin/dev/zxq5/Fantasysmp.kt @@ -1,30 +1,29 @@ package dev.zxq5 +import dev.zxq5.items.ItemImplementation +import dev.zxq5.items.ItemTagAdder import dev.zxq5.items.ModItems import dev.zxq5.items.Witherite -import dev.zxq5.items.isWearingFullSet import net.fabricmc.api.ModInitializer +import net.fabricmc.fabric.api.datagen.v1.FabricPackOutput +import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider +import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagsProvider import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents -import net.fabricmc.fabric.api.event.player.AttackEntityCallback -import net.minecraft.world.InteractionHand -import net.minecraft.world.InteractionResult -import net.minecraft.world.effect.MobEffectInstance +import net.minecraft.core.HolderLookup +import net.minecraft.data.recipes.RecipeOutput +import net.minecraft.data.recipes.RecipeProvider import net.minecraft.world.effect.MobEffects -import net.minecraft.world.entity.LivingEntity import org.slf4j.LoggerFactory +import java.util.concurrent.CompletableFuture object Fantasysmp : ModInitializer { - const val MOD_ID = "fantasysmp" private val logger = LoggerFactory.getLogger("fantasysmp") override fun onInitialize() { - // This code runs as soon as Minecraft is in a mod-load-ready state. - // However, some things (like resources) may still be uninitialized. - // Proceed with mild caution. logger.info("Hello Fabric world!") - ModItems.init(); + ModItems.onLoad(); // Register the group. registerItemEffects() } @@ -32,32 +31,36 @@ object Fantasysmp : ModInitializer { private fun registerItemEffects() { ServerTickEvents.END_SERVER_TICK.register { server -> for (player in server.playerList.players) { - if (player.isWearingFullSet(Witherite.ARMOUR_SET)) { + if (Witherite.fullSetEquippedBy(player)) { player.removeEffect(MobEffects.WITHER) } } } + } +} - AttackEntityCallback.EVENT.register { player, world, hand, entity, _ -> - if (world.isClientSide) return@register InteractionResult.PASS - if (hand != InteractionHand.MAIN_HAND) return@register InteractionResult.PASS - if (player.getItemInHand(hand).item != Witherite.SWORD) return@register InteractionResult.PASS - if (entity is LivingEntity) { - entity.addEffect(MobEffectInstance(MobEffects.WITHER, 60, 1), player) +class ModRecipeProvider( + output: FabricPackOutput, + registriesFuture: CompletableFuture +) : FabricRecipeProvider(output, registriesFuture) { + override fun createRecipeProvider( + registries: HolderLookup.Provider, + exporter: RecipeOutput + ): RecipeProvider { + return object : RecipeProvider(registries, exporter) { + override fun buildRecipes() { + ItemImplementation.all.forEach { it.generateRecipes(registries, exporter) } } - - InteractionResult.PASS - } - - AttackEntityCallback.EVENT.register { player, world, hand, entity, _ -> - if (world.isClientSide) return@register InteractionResult.PASS - if (hand != InteractionHand.MAIN_HAND) return@register InteractionResult.PASS - if (player.getItemInHand(hand).item != Witherite.AXE) return@register InteractionResult.PASS - if (entity is LivingEntity) { - entity.addEffect(MobEffectInstance(MobEffects.WITHER, 60, 1), player) - } - - InteractionResult.PASS } } + + override fun getName(): String = "Fantasysmp Recipes" +} + +class ModItemTagProvider(output: FabricPackOutput, registriesFuture: CompletableFuture) : + FabricTagsProvider.ItemTagsProvider(output, registriesFuture) { + override fun addTags(provider: HolderLookup.Provider) { + val adder = ItemTagAdder(this) + ItemImplementation.all.forEach { it.generateItemTags(adder) } + } } \ No newline at end of file diff --git a/src/main/kotlin/dev/zxq5/items/Dragonite.kt b/src/main/kotlin/dev/zxq5/items/Dragonite.kt index 59e33e2..e6f82b1 100644 --- a/src/main/kotlin/dev/zxq5/items/Dragonite.kt +++ b/src/main/kotlin/dev/zxq5/items/Dragonite.kt @@ -49,7 +49,7 @@ object Dragonite { val SWORD = register("dragonite_sword", ::DragoniteSword, Item.Properties() .enchantable(armorMaterial.enchantmentValue) - .component(DataComponents.USE_COOLDOWN, UseCooldown(0.0f)) + .component(DataComponents.USE_COOLDOWN, UseCooldown(2f)) .sword(ToolMaterial.NETHERITE, 3.0F, -2.4F) ) val HELMET = register( @@ -109,26 +109,21 @@ object Dragonite { class DragoniteSword(properties: Properties) : Item(properties) { override fun use(level: Level, user: Player, hand: InteractionHand): InteractionResult { - if (level.isClientSide) return InteractionResult.PASS - if (!user.isWearingFullSet(Dragonite.ARMOUR_SET)) return InteractionResult.PASS +// if (level.isClientSide || !user.isWearingFullSet(Dragonite.ARMOUR_SET)) return InteractionResult.PASS - val stack = user.getItemInHand(hand) - if (user.cooldowns.isOnCooldown(stack)) return InteractionResult.PASS - - val dir = user.lookAngle - user.deltaMovement = dir.scale(2.0) + val direction = user.lookAngle + user.deltaMovement = direction.scale(2.0) user.hurtMarked = true level.playSound(null, user.x, user.y, user.z, SoundEvents.ENDER_DRAGON_FLAP, SoundSource.PLAYERS, 1.0f, 1.0f) - (level as? ServerLevel)?.sendParticles( - ParticleTypes.DRAGON_BREATH as ParticleOptions, - user.x, user.y, user.z, - 100, 0.5, 0.5, 0.5, 0.5 - ) - - user.cooldowns.addCooldown(stack, 25) + // 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 +// ) return InteractionResult.SUCCESS } diff --git a/src/main/kotlin/dev/zxq5/items/Items.kt b/src/main/kotlin/dev/zxq5/items/Items.kt index b997a5d..fc4c1d9 100644 --- a/src/main/kotlin/dev/zxq5/items/Items.kt +++ b/src/main/kotlin/dev/zxq5/items/Items.kt @@ -1,21 +1,126 @@ package dev.zxq5.items import dev.zxq5.Fantasysmp -import dev.zxq5.items.Witherite.SWORD +import dev.zxq5.util.id +import dev.zxq5.util.resourceKey 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.client.data.models.ItemModelGenerators +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.CreativeModeTab.DisplayItemsGenerator -import net.minecraft.world.item.CreativeModeTab.ItemDisplayParameters -import java.util.function.Supplier +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 = materialName.resourceKey() + + open val materialDefinition: GearMaterialDef = GearMaterialDef() + val armorMaterial: ArmorMaterial by lazy { + materialDefinition.toArmorMaterial() + } + + private val itemRecipes = mutableMapOf>() + private val itemTags = mutableMapOf>>() + + internal fun registerDatagen(item: Item, recipes: List, tags: List>) { + itemRecipes[item] = recipes + itemTags[item] = tags + } + + 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( @@ -29,36 +134,106 @@ object ModItems { return item } - val CUSTOM_CREATIVE_TAB_KEY: ResourceKey = ResourceKey.create( + 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(Supplier { ItemStack(SWORD) }) + .icon { ItemStack(Witherite.SWORD) } .title(Component.translatable("Fantasysmp")) - .displayItems(DisplayItemsGenerator { params: ItemDisplayParameters, output: CreativeModeTab.Output -> - Witherite.ITEMS.forEach { output.accept(it) } - Dragonite.ITEMS.forEach { output.accept(it) } - }) + .displayItems { _, output -> + ItemImplementation.all.forEach { + it.items.forEach(output::accept) + } + } .build() - fun init() { - Witherite.init(); - Dragonite.init(); + 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 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, ) -fun Player.isWearingFullSet(set: ArmorSet): Boolean { - return getItemBySlot(EquipmentSlot.HEAD).item == set.helmet && - getItemBySlot(EquipmentSlot.CHEST).item == set.chestplate && - getItemBySlot(EquipmentSlot.LEGS).item == set.leggings && - getItemBySlot(EquipmentSlot.FEET).item == set.boots -} \ No newline at end of file +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 +} + +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>() + + 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) + 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/Witherite.kt b/src/main/kotlin/dev/zxq5/items/Witherite.kt index 2891f5d..50ddd6a 100644 --- a/src/main/kotlin/dev/zxq5/items/Witherite.kt +++ b/src/main/kotlin/dev/zxq5/items/Witherite.kt @@ -1,78 +1,56 @@ package dev.zxq5.items -import dev.zxq5.Fantasysmp import dev.zxq5.items.ModItems.register import net.minecraft.core.component.DataComponents -import net.minecraft.resources.Identifier -import net.minecraft.resources.ResourceKey import net.minecraft.sounds.SoundEvents -import net.minecraft.tags.ItemTags -import net.minecraft.util.Unit import net.minecraft.world.InteractionHand import net.minecraft.world.InteractionResult +import net.minecraft.world.effect.MobEffectInstance +import net.minecraft.world.effect.MobEffects import net.minecraft.world.entity.EntityType +import net.minecraft.world.entity.LivingEntity import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.projectile.hurtingprojectile.WitherSkull import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack 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.* +import net.minecraft.world.item.equipment.ArmorMaterial +import net.minecraft.world.item.equipment.ArmorType import net.minecraft.world.level.Level -import java.util.Optional +import java.util.* -object Witherite { - val base_durability = 40; - val witherite_material_key: ResourceKey = ResourceKey.create( - EquipmentAssets.ROOT_ID, - Identifier.fromNamespaceAndPath(Fantasysmp.MOD_ID, "witherite") - ) - - val armorMaterial = ArmorMaterial( - base_durability, - mapOf( - ArmorType.HELMET to 3, - ArmorType.CHESTPLATE to 8, - ArmorType.LEGGINGS to 6, - ArmorType.BOOTS to 3, - ArmorType.BODY to 19, - ), - 5, - SoundEvents.ARMOR_EQUIP_NETHERITE, - 3.0f, - 0.1f, - ItemTags.REPAIRS_NETHERITE_ARMOR, - witherite_material_key, - ) - +object Witherite: GenericGearSet() { val SHIELD = register("witherite_shield", ::Item, - Item.Properties().component(DataComponents.BLOCKS_ATTACKS, BlocksAttacks( - 0.25f, // 5 tick warmup - 1.0f, // standard disable cooldown - listOf( - BlocksAttacks.DamageReduction( - 90.0f, // horizontalBlockingAngle — 90° arc each side (180° total, same as vanilla) - Optional.empty(), // type — empty = blocks all damage types - 0.0f, // base — flat damage reduction - 1.0f // factor — multiplier (1.0 = block 100% of damage) - ) - ), // full block — check if this constant exists, else build the list manually - BlocksAttacks.ItemDamageFunction.DEFAULT, // check for this constant too - Optional.empty(), // no bypass - Optional.empty(), // default block sound - Optional.empty() // default disable sound - )) + Item.Properties() + .component(DataComponents.BLOCKS_ATTACKS, BlocksAttacks( + 0.25f, // 5 tick warmup + 1.0f, // standard disable cooldown + listOf( + BlocksAttacks.DamageReduction( + 90.0f, // horizontalBlockingAngle — 90° arc each side (180° total, same as vanilla) + Optional.empty(), // type — empty = blocks all damage types + 0.0f, // base — flat damage reduction + 1.0f // factor — multiplier (1.0 = block 100% of damage) + ) + ), // full block — check if this constant exists, else build the list manually + BlocksAttacks.ItemDamageFunction.DEFAULT, // check for this constant too + Optional.empty(), // no bypass + 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(0.0f)) + .component(DataComponents.USE_COOLDOWN, UseCooldown(2f)) .sword(ToolMaterial.NETHERITE, 3F, -2.4F) ) - val AXE = register("witherite_axe", ::Item, + val AXE = register("witherite_axe", ::WitheriteWeapon, Item.Properties() .enchantable(armorMaterial.enchantmentValue) .axe(ToolMaterial.NETHERITE, 5F, -3F)) @@ -114,21 +92,33 @@ object Witherite { .durability(ArmorType.BOOTS.getDurability(armorMaterial.durability)) ) - val ITEMS = listOf(SWORD, AXE, SHIELD, HELMET, CHESTPLATE, LEGGINGS, BOOTS) - val ARMOUR_SET = ArmorSet(HELMET, CHESTPLATE, LEGGINGS, BOOTS) + override val materialName = "witherite" + override val materialDefinition = super.materialDefinition.copy(assetId = materialKey) + override val armorSet: ArmorSet = ArmorSet(HELMET, CHESTPLATE, LEGGINGS, BOOTS) + override val items = listOf(SWORD, AXE, SHIELD, HELMET, CHESTPLATE, LEGGINGS, BOOTS) - fun init() {} + override fun onLoad() { + TODO("Not yet implemented") + } } -class WitheriteSword(properties: Properties) : Item(properties) { - override fun use(level: Level, user: Player, hand: InteractionHand): InteractionResult { - if (level.isClientSide) { - return InteractionResult.PASS; - } - if (!user.isCrouching) return InteractionResult.PASS +open class WitheriteWeapon(properties: Properties): Item(properties) { + override fun hurtEnemy(itemStack: ItemStack, mob: LivingEntity, attacker: LivingEntity) { + super.hurtEnemy(itemStack, mob, attacker) - val stack = user.getItemInHand(hand) - if (user.cooldowns.isOnCooldown(stack)) return InteractionResult.PASS + if (attacker.level().isClientSide) return + + val amplifier = if (attacker is Player) { 0 } else { 2 } + mob.addEffect(MobEffectInstance(MobEffects.WITHER, 60, amplifier), attacker) + } +} + +class WitheriteSword(properties: Properties) : WitheriteWeapon(properties) { + override fun use(level: Level, user: Player, hand: InteractionHand): InteractionResult { + if (level.isClientSide + || !user.isCrouching + || !Witherite.fullSetEquippedBy(user) + ) return InteractionResult.PASS; val skull = WitherSkull(EntityType.WITHER_SKULL, level) skull.setPos(user.eyePosition) @@ -136,8 +126,6 @@ class WitheriteSword(properties: Properties) : Item(properties) { level.addFreshEntity(skull) skull.playSound(SoundEvents.WITHER_SHOOT) - user.cooldowns.addCooldown(stack, 25) - return InteractionResult.SUCCESS; } -} +} \ No newline at end of file diff --git a/src/main/kotlin/dev/zxq5/util/util.kt b/src/main/kotlin/dev/zxq5/util/util.kt new file mode 100644 index 0000000..243e674 --- /dev/null +++ b/src/main/kotlin/dev/zxq5/util/util.kt @@ -0,0 +1,12 @@ +package dev.zxq5.util +import dev.zxq5.Fantasysmp +import net.minecraft.resources.Identifier +import net.minecraft.resources.ResourceKey +import net.minecraft.world.item.equipment.EquipmentAsset +import net.minecraft.world.item.equipment.EquipmentAssets + +fun id(path: String): Identifier = Identifier.fromNamespaceAndPath(Fantasysmp.MOD_ID, path) +fun String.resourceKey(): ResourceKey = ResourceKey.create( + EquipmentAssets.ROOT_ID, + Identifier.fromNamespaceAndPath(Fantasysmp.MOD_ID, this), +) diff --git a/src/main/resources/assets/fantasysmp/textures/item/dragon_scale.png b/src/main/resources/assets/fantasysmp/textures/item/dragon_scale.png index 14d177836a79dd302ec8aaf07114ea30fb286c24..82098642c19ba8a80aaaccb047d71e494269b63f 100644 GIT binary patch delta 562 zcmV-20?qx+1m6UZIDY~WNklPET{q>IwE8&PoMN^mdOjhj|0eyEf}DJZA~!Kz>}ZIjd*l9)+mCfAu) z5?dsQThA~s!@cL+^Ui(1|AjrlL$p^vOwCTUTkZa%F1gJILw~1eqr3v<)e#ML!LC`f zLk{Cz_L)$xw%esHD_mL}f+Tv8YCn%u{0JuB%;9Zm1|M?^ z$X7ECL4e;AK!1&FEEnEyI&k5V2j&w9sr#^6Si+=vA31g!^OV^wFiFSy^Ud%d6la|* zR6h0V`UfaZE^a>A7lbim6elbVrBV@!SNkpC^C(y_*MBfNcN-Vf1TXkK45nRyO%@*Z zOyL&0gZ^p+J_>*_2F=&H)jSXj#;Hw-QA~@|J$$3?)+B#B^d35=-y~JoMGP$@L#O?1 zeT}=qw@^nU&_xzidD8ANob8~q+K2waAa2MfogoL1`?-uS`PU8JHi5d3>qZiN@QO0l zN}u@Ni7SY&ZM*Nk{2hy&rK909O|SMpi8X8f1tn6&vFUeZasU7T07*qoM6N<$f|VHr AZvX%Q delta 544 zcmV+*0^j}L1kD7HIDY~ENkl7Z z5ytaCAb@;6&$SDM0x61uYPBkGfljBx$&xI?`exx$GKG6 zh@}s=;q`jK7BcV+e!rizISsGZE4cCQ3S2H1R^nH&l3vB``xhviCDdv)E-nZHnCNgg zM5ECdIbdMq)qkBNG;JP7M_;hpeGWrC#9nO=mNg0EXjP{el$SA ze2mK{Dt@+X$iMTN2zWdmlupXH`)mW(!%0418yv@BjelVK+FLx(@>u>c4-bh@=0vEf zdfGgo>pBI4LDJ9b^h7Ap!o>yt#A9*F#&6Q>%sEnoS&{^W5{bm*uJ9Jd2!%q_@Ar9N zk|eQqaN+wRmOpLazAwv`z%UHd>-7omjKElkxnWhh-EK6SP2M{Tj@5S7_g}s;nGB`V i>8V%ypBcoa9sUKA?#8++(LVzK00007Z5ytaC zAb@;6&$SDM0x61uYPBkGfljBx$&xI?`exx$GKG6h@}s= z;q`jK7BcV+e!rizISsGZE4cCQ3S2H1R^nH&l3vB``xhviCDdv)E-nZHnCNggM5ECd zIbdMq)tw|XZ5~HQU$EPK4nsY}UTqJSH6k(+19LNepE(`ao9sSXttPs57f#6yiybno~V0A1Ud^~_G$$Z{^G(f?8jLRn~ zezt7Lzw??1csw4IPRh9ZYy;QBNj_j39LHgeVEfuzJkau3{xJ^^iBRT5sH%F}JfQ13 z1%pA-&+GI=DAB^j1^&ciamvPT((KGRQiNHO1cefb#N@8<7RCsLLe%f~d0>(xv379b z`y!SU$Ie*$oL_t(IPt}sWZ_`i|#edJSQ`=3OmL$;fQHiKaQH2CTNPGy1 zp-8N3YzV{%8w2e816>e;nXwWAW`r0z&;f*iLM4Wxrcr3HoTPDF+j+d_;))bHBi@ts z-o3u(oO_QA|II25O-udkZrI#wg7v$}$i)Y9twi?eL`}-$Q-1_;8!ND6z4b|CxSdOu z$W+GdNeblxcFsi@+d9{l@9^-*eQN$P8A(d;du!FHS^6>}Zz`~5Bh?ma>$BtwF0J0; z6$`u^ead?4gAn_j8F}{DW$Mj3Nk7JPRdHY-oPMfWatN`fF!K0Yrm9&*%Lh%?yT`T5y+uWsq$Wl~({OUShYGp?GO3 z!)5m4VE&IEKNw6I9U0snTkdu{T5EB!JI|^h7 z2)M1$0n_i^zXQ{6-n?P(aP?q#`Q#A;*b6Yt+&nxCU%!2Yy8y&^I|*Vt479%4!NAMQ z%dlhj4u%K7FaaA-@M9)ee!a&D25v5HFdO825NH!lW#Hi8fE!XR;=#bq&JH(_1;*MH zas}*EkQZLPdVk6Aiv1|_udgh6ulT;2>j zzdvL6`}Z${pr9bb`}gl5@dJvMIgc(d2~&#_b6O`Db^(DG4w^&cof&!d*k$U?I!QmqbX9R+Ae?@xT5=8`M2@^+k;oXslH`QR+uCc|o{wo@ z5VRAH9;(vndUVyCArVEZf*~vjOp0Y!b(N^t;eBn9#>N`fF!j^Gsh%2{