From e09d8f30ca2cab125edb49c7bab0faab598b8c59 Mon Sep 17 00:00:00 2001 From: JustAHuman-xD Date: Tue, 23 Sep 2025 18:46:01 -0500 Subject: [PATCH 01/16] armor & tool shorthands (needs javadocs) --- .../core/item/builder/ItemStackBuilder.kt | 28 ++-- .../item/builder/PylonItemStackBuilder.kt | 136 ++++++++++++++++++ .../pylonmc/pylon/core/util/PylonUtils.kt | 21 +++ 3 files changed, 177 insertions(+), 8 deletions(-) create mode 100644 pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt index 404ac32db..5cadfaf45 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt @@ -4,6 +4,8 @@ import io.github.pylonmc.pylon.core.datatypes.PylonSerializers import io.github.pylonmc.pylon.core.i18n.PylonTranslator.Companion.translate import io.github.pylonmc.pylon.core.item.PylonItemSchema import io.github.pylonmc.pylon.core.util.editData +import io.github.pylonmc.pylon.core.util.editDataOrDefault +import io.github.pylonmc.pylon.core.util.editDataOrSet import io.github.pylonmc.pylon.core.util.fromMiniMessage import io.papermc.paper.datacomponent.DataComponentBuilder import io.papermc.paper.datacomponent.DataComponentType @@ -33,8 +35,7 @@ import java.util.function.Consumer * and annoying - it is unfortunately necessary to get around InvUI's translation system. */ @Suppress("UnstableApiUsage") -open class ItemStackBuilder private constructor(val stack: ItemStack) : ItemProvider { - +open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemProvider { fun amount(amount: Int) = apply { stack.amount = amount } @@ -77,6 +78,14 @@ open class ItemStackBuilder private constructor(val stack: ItemStack) : ItemProv stack.editData(type, block) } + fun editDataOrDefault(type: DataComponentType.Valued, block: (T) -> T) = apply { + stack.editDataOrDefault(type, block) + } + + fun editDataOrSet(type: DataComponentType.Valued, block: (T?) -> T) = apply { + stack.editDataOrSet(type, block) + } + fun name(name: Component) = set(DataComponentTypes.ITEM_NAME, name) fun name(name: String) = name(fromMiniMessage(name)) @@ -166,6 +175,9 @@ open class ItemStackBuilder private constructor(val stack: ItemStack) : ItemProv companion object { + val baseAttackDamage = NamespacedKey.minecraft("base_attack_damage") + val baseAttackSpeed = NamespacedKey.minecraft("base_attack_speed") + /** * The default name language key for a Pylon item. */ @@ -200,20 +212,20 @@ open class ItemStackBuilder private constructor(val stack: ItemStack) : ItemProv * provided [key]. */ @JvmStatic - fun pylonItem(material: Material, key: NamespacedKey): ItemStackBuilder { - return of(material) + fun pylonItem(material: Material, key: NamespacedKey): PylonItemStackBuilder { + return PylonItemStackBuilder(ItemStack(material), key) .editPdc { pdc -> pdc.set(PylonItemSchema.pylonItemKeyKey, PylonSerializers.NAMESPACED_KEY, key) } .set(DataComponentTypes.CUSTOM_MODEL_DATA, CustomModelData.customModelData().addString(key.toString())) .defaultTranslatableName(key) - .defaultTranslatableLore(key) + .defaultTranslatableLore(key) as PylonItemStackBuilder } /** * Returns an [ItemStackBuilder] with name and lore set to the default translation keys, and with the item's ID set to [key] */ @JvmStatic - fun pylonItem(stack: ItemStack, key: NamespacedKey): ItemStackBuilder { - return of(stack) + fun pylonItem(stack: ItemStack, key: NamespacedKey): PylonItemStackBuilder { + return PylonItemStackBuilder(stack, key) .editPdc { it.set(PylonItemSchema.pylonItemKeyKey, PylonSerializers.NAMESPACED_KEY, key) } .let { // Adds the pylon item key as the FIRST string in custom model data, but preserve any pre-existing data @@ -226,7 +238,7 @@ open class ItemStackBuilder private constructor(val stack: ItemStack) : ItemProv it.set(DataComponentTypes.CUSTOM_MODEL_DATA, modelData) } .defaultTranslatableName(key) - .defaultTranslatableLore(key) + .defaultTranslatableLore(key) as PylonItemStackBuilder } } } diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt new file mode 100644 index 000000000..913181743 --- /dev/null +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt @@ -0,0 +1,136 @@ +package io.github.pylonmc.pylon.core.item.builder + +import io.github.pylonmc.pylon.core.config.Settings +import io.github.pylonmc.pylon.core.config.adapter.ConfigAdapter +import io.papermc.paper.datacomponent.DataComponentTypes +import io.papermc.paper.datacomponent.item.ItemAttributeModifiers +import io.papermc.paper.datacomponent.item.Tool +import io.papermc.paper.datacomponent.item.Weapon +import io.papermc.paper.registry.keys.tags.BlockTypeTagKeys +import io.papermc.paper.registry.set.RegistryKeySet +import net.kyori.adventure.util.TriState +import org.bukkit.NamespacedKey +import org.bukkit.Registry +import org.bukkit.attribute.Attribute +import org.bukkit.attribute.AttributeModifier +import org.bukkit.block.BlockType +import org.bukkit.inventory.EquipmentSlotGroup +import org.bukkit.inventory.ItemStack + +@Suppress("UnstableApiUsage") +class PylonItemStackBuilder : ItemStackBuilder { + private val itemKey: NamespacedKey + + internal constructor(stack: ItemStack, itemKey: NamespacedKey) : super(stack) { + this.itemKey = itemKey + } + + @JvmOverloads + fun helmet( + armor: Double = Settings.get(itemKey).getOrThrow("armor", ConfigAdapter.DOUBLE), + armorToughness: Double = Settings.get(itemKey).getOrThrow("armor-toughness", ConfigAdapter.DOUBLE) + ) = armor(EquipmentSlotGroup.HEAD, armor, armorToughness) + + @JvmOverloads + fun chestPlate( + armor: Double = Settings.get(itemKey).getOrThrow("armor", ConfigAdapter.DOUBLE), + armorToughness: Double = Settings.get(itemKey).getOrThrow("armor-toughness", ConfigAdapter.DOUBLE) + ) = armor(EquipmentSlotGroup.CHEST, armor, armorToughness) + + @JvmOverloads + fun leggings( + armor: Double = Settings.get(itemKey).getOrThrow("armor", ConfigAdapter.DOUBLE), + armorToughness: Double = Settings.get(itemKey).getOrThrow("armor-toughness", ConfigAdapter.DOUBLE) + ) = armor(EquipmentSlotGroup.LEGS, armor, armorToughness) + + @JvmOverloads + fun boots( + armor: Double = Settings.get(itemKey).getOrThrow("armor", ConfigAdapter.DOUBLE), + armorToughness: Double = Settings.get(itemKey).getOrThrow("armor-toughness", ConfigAdapter.DOUBLE) + ) = armor(EquipmentSlotGroup.FEET, armor, armorToughness) + + @JvmOverloads + fun armor( + slot: EquipmentSlotGroup, + armor: Double = Settings.get(itemKey).getOrThrow("armor", ConfigAdapter.DOUBLE), + armorToughness: Double = Settings.get(itemKey).getOrThrow("armor-toughness", ConfigAdapter.DOUBLE) + ) = apply { + editDataOrSet(DataComponentTypes.ATTRIBUTE_MODIFIERS) { modifiers -> + val copying = modifiers?.modifiers()?.filter { it.modifier().key.namespace != "minecraft" || !it.modifier().key.key.contains("armor.") } + ItemAttributeModifiers.itemAttributes().copy(copying) + .addModifier(Attribute.ARMOR, AttributeModifier(itemKey, armor, AttributeModifier.Operation.ADD_NUMBER, slot)) + .addModifier(Attribute.ARMOR_TOUGHNESS, AttributeModifier(itemKey, armorToughness, AttributeModifier.Operation.ADD_NUMBER, slot)) + .build() + } + } + + @JvmOverloads + fun axe( + miningSpeed: Float = Settings.get(itemKey).getOrThrow("mining-speed", ConfigAdapter.FLOAT), + miningDurabilityDamage: Int = Settings.get(itemKey).getOrThrow("mining-durability-damage", ConfigAdapter.INT) + ) = tool(Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_AXE), miningSpeed, miningDurabilityDamage) + + @JvmOverloads + fun pickaxe( + miningSpeed: Float = Settings.get(itemKey).getOrThrow("mining-speed", ConfigAdapter.FLOAT), + miningDurabilityDamage: Int = Settings.get(itemKey).getOrThrow("mining-durability-damage", ConfigAdapter.INT) + ) = tool(Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_PICKAXE), miningSpeed, miningDurabilityDamage) + + @JvmOverloads + fun shovel( + miningSpeed: Float = Settings.get(itemKey).getOrThrow("mining-speed", ConfigAdapter.FLOAT), + miningDurabilityDamage: Int = Settings.get(itemKey).getOrThrow("mining-durability-damage", ConfigAdapter.INT) + ) = tool(Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_SHOVEL), miningSpeed, miningDurabilityDamage) + + @JvmOverloads + fun hoe( + miningSpeed: Float = Settings.get(itemKey).getOrThrow("mining-speed", ConfigAdapter.FLOAT), + miningDurabilityDamage: Int = Settings.get(itemKey).getOrThrow("mining-durability-damage", ConfigAdapter.INT) + ) = tool(Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_HOE), miningSpeed, miningDurabilityDamage) + + @JvmOverloads + fun tool( + blocks: RegistryKeySet, + miningSpeed: Float = Settings.get(itemKey).getOrThrow("mining-speed", ConfigAdapter.FLOAT), + miningDurabilityDamage: Int = Settings.get(itemKey).getOrThrow("mining-durability-damage", ConfigAdapter.INT) + ) = apply { + set(DataComponentTypes.TOOL, Tool.tool() + .defaultMiningSpeed(miningSpeed) + .damagePerBlock(miningDurabilityDamage) + .addRule(Tool.rule(blocks, miningSpeed, TriState.TRUE))) + } + + @JvmOverloads + fun weapon( + disablesShield: Boolean = false, + attackDamage: Double = Settings.get(itemKey).getOrThrow("attack-damage", ConfigAdapter.DOUBLE), + attackSpeed: Double = Settings.get(itemKey).getOrThrow("attack-speed", ConfigAdapter.DOUBLE), + attackDurabilityDamage: Int = Settings.get(itemKey).getOrThrow("attack-durability-damage", ConfigAdapter.INT), + disableShieldSeconds: Float? = null + ) = apply { + editDataOrSet(DataComponentTypes.ATTRIBUTE_MODIFIERS) { modifiers -> + val copying = modifiers?.modifiers()?.filter { it.modifier().key != baseAttackDamage && it.modifier().key != baseAttackSpeed } + ItemAttributeModifiers.itemAttributes().copy(copying) + .addModifier(Attribute.ATTACK_DAMAGE, AttributeModifier(baseAttackDamage, -1.0 + attackDamage, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlotGroup.MAINHAND)) + .addModifier(Attribute.ATTACK_SPEED, AttributeModifier(baseAttackSpeed, -4.0 + attackSpeed, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlotGroup.MAINHAND)) + .build() + } + set(DataComponentTypes.WEAPON, Weapon.weapon() + .itemDamagePerAttack(attackDurabilityDamage) + .disableBlockingForSeconds(if (disablesShield) disableShieldSeconds ?: Settings.get(itemKey).getOrThrow("disable-shield-seconds", ConfigAdapter.FLOAT) else 0f)) + } + + @JvmOverloads + fun durability( + durability: Int = Settings.get(itemKey).getOrThrow("durability", ConfigAdapter.INT) + ) = set(DataComponentTypes.MAX_DAMAGE, durability) + + companion object { + fun ItemAttributeModifiers.Builder.copy(modifiers: List?) : ItemAttributeModifiers.Builder { + modifiers?.forEach { entry -> + this.addModifier(entry.attribute(), entry.modifier(), entry.group, entry.display()) + } + return this + } + } +} \ No newline at end of file diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/PylonUtils.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/PylonUtils.kt index 35d0a098d..e9c5d1536 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/PylonUtils.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/PylonUtils.kt @@ -293,6 +293,27 @@ inline fun ItemStack.editData( return this } +@JvmSynthetic +@Suppress("UnstableApiUsage") +inline fun ItemStack.editDataOrDefault( + type: DataComponentType.Valued, + block: (T) -> T +): ItemStack { + val data = getData(type) ?: this.type.getDefaultData(type) ?: return this + setData(type, block(data)) + return this +} + +@JvmSynthetic +@Suppress("UnstableApiUsage") +inline fun ItemStack.editDataOrSet( + type: DataComponentType.Valued, + block: (T?) -> T +): ItemStack { + setData(type, block(getData(type))) + return this +} + /** * Wrapper around [PersistentDataContainer.set] that allows nullable values to be passed * From 62ef756cb2b2605c8cc589d87acfebc25896796c Mon Sep 17 00:00:00 2001 From: JustAHuman-xD Date: Tue, 23 Sep 2025 19:31:38 -0500 Subject: [PATCH 02/16] some more --- .../core/item/builder/ItemStackBuilder.kt | 25 +++++++++++++++ .../item/builder/PylonItemStackBuilder.kt | 32 +++++++++---------- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt index 5cadfaf45..3b43b96bb 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt @@ -11,6 +11,7 @@ import io.papermc.paper.datacomponent.DataComponentBuilder import io.papermc.paper.datacomponent.DataComponentType import io.papermc.paper.datacomponent.DataComponentTypes import io.papermc.paper.datacomponent.item.CustomModelData +import io.papermc.paper.datacomponent.item.ItemAttributeModifiers import io.papermc.paper.datacomponent.item.ItemLore import net.kyori.adventure.text.Component import net.kyori.adventure.text.ComponentLike @@ -18,11 +19,14 @@ import org.apache.commons.lang3.LocaleUtils import org.bukkit.Color import org.bukkit.Material import org.bukkit.NamespacedKey +import org.bukkit.attribute.Attribute +import org.bukkit.attribute.AttributeModifier import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.ItemMeta import org.bukkit.persistence.PersistentDataContainer import xyz.xenondevs.invui.item.ItemProvider import java.util.function.Consumer +import kotlin.collections.forEach /** * Helper class for creating an [ItemStack], including utilities for creating Pylon @@ -157,6 +161,20 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro fun addCustomModelDataString(color: Color) = editCustomModelData { it.addColor(color) } + @JvmOverloads + fun addAttributeModifier( + attribute: Attribute, + modifier: AttributeModifier, + replaceExisting: Boolean = true + ) = apply { + editDataOrSet(DataComponentTypes.ATTRIBUTE_MODIFIERS) { modifiers -> + val copying = modifiers?.modifiers()?.filter { !replaceExisting || it.modifier().key != modifier.key } + ItemAttributeModifiers.itemAttributes().copy(copying) + .addModifier(attribute, modifier) + .build() + } + } + fun build(): ItemStack = stack.clone() /** @@ -240,5 +258,12 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro .defaultTranslatableName(key) .defaultTranslatableLore(key) as PylonItemStackBuilder } + + fun ItemAttributeModifiers.Builder.copy(modifiers: List?) : ItemAttributeModifiers.Builder { + modifiers?.forEach { entry -> + this.addModifier(entry.attribute(), entry.modifier(), entry.group, entry.display()) + } + return this + } } } diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt index 913181743..992fbd66a 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt @@ -5,6 +5,7 @@ import io.github.pylonmc.pylon.core.config.adapter.ConfigAdapter import io.papermc.paper.datacomponent.DataComponentTypes import io.papermc.paper.datacomponent.item.ItemAttributeModifiers import io.papermc.paper.datacomponent.item.Tool +import io.papermc.paper.datacomponent.item.UseCooldown import io.papermc.paper.datacomponent.item.Weapon import io.papermc.paper.registry.keys.tags.BlockTypeTagKeys import io.papermc.paper.registry.set.RegistryKeySet @@ -100,6 +101,8 @@ class PylonItemStackBuilder : ItemStackBuilder { .addRule(Tool.rule(blocks, miningSpeed, TriState.TRUE))) } + fun noTool() = unset(DataComponentTypes.TOOL) as PylonItemStackBuilder + @JvmOverloads fun weapon( disablesShield: Boolean = false, @@ -108,29 +111,24 @@ class PylonItemStackBuilder : ItemStackBuilder { attackDurabilityDamage: Int = Settings.get(itemKey).getOrThrow("attack-durability-damage", ConfigAdapter.INT), disableShieldSeconds: Float? = null ) = apply { - editDataOrSet(DataComponentTypes.ATTRIBUTE_MODIFIERS) { modifiers -> - val copying = modifiers?.modifiers()?.filter { it.modifier().key != baseAttackDamage && it.modifier().key != baseAttackSpeed } - ItemAttributeModifiers.itemAttributes().copy(copying) - .addModifier(Attribute.ATTACK_DAMAGE, AttributeModifier(baseAttackDamage, -1.0 + attackDamage, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlotGroup.MAINHAND)) - .addModifier(Attribute.ATTACK_SPEED, AttributeModifier(baseAttackSpeed, -4.0 + attackSpeed, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlotGroup.MAINHAND)) - .build() - } + addAttributeModifier(Attribute.ATTACK_DAMAGE, AttributeModifier(baseAttackDamage, -1.0 + attackDamage, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlotGroup.MAINHAND)) + addAttributeModifier(Attribute.ATTACK_SPEED, AttributeModifier(baseAttackSpeed, -4.0 + attackSpeed, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlotGroup.MAINHAND)) set(DataComponentTypes.WEAPON, Weapon.weapon() .itemDamagePerAttack(attackDurabilityDamage) .disableBlockingForSeconds(if (disablesShield) disableShieldSeconds ?: Settings.get(itemKey).getOrThrow("disable-shield-seconds", ConfigAdapter.FLOAT) else 0f)) } + @JvmOverloads + fun attackKnockback(knockback: Double = Settings.get(itemKey).getOrThrow("attack-knockback", ConfigAdapter.DOUBLE)) = + addAttributeModifier(Attribute.ATTACK_KNOCKBACK, AttributeModifier(itemKey, knockback, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlotGroup.MAINHAND)) as PylonItemStackBuilder + @JvmOverloads fun durability( durability: Int = Settings.get(itemKey).getOrThrow("durability", ConfigAdapter.INT) - ) = set(DataComponentTypes.MAX_DAMAGE, durability) - - companion object { - fun ItemAttributeModifiers.Builder.copy(modifiers: List?) : ItemAttributeModifiers.Builder { - modifiers?.forEach { entry -> - this.addModifier(entry.attribute(), entry.modifier(), entry.group, entry.display()) - } - return this - } - } + ) = set(DataComponentTypes.MAX_DAMAGE, durability) as PylonItemStackBuilder + + @JvmOverloads + fun useCooldown( + cooldownTicks: Int = Settings.get(itemKey).getOrThrow("cooldown-ticks", ConfigAdapter.INT) + ) = set(DataComponentTypes.USE_COOLDOWN, UseCooldown.useCooldown(cooldownTicks / 20.0f).cooldownGroup(itemKey)) as PylonItemStackBuilder } \ No newline at end of file From d4441a92c4d3c83fb394ab22653a5936e0f2735c Mon Sep 17 00:00:00 2001 From: JustAHuman-xD Date: Sun, 28 Sep 2025 14:55:41 -0500 Subject: [PATCH 03/16] nuke it --- .../core/item/builder/ItemStackBuilder.kt | 132 ++++++++++++++++- .../item/builder/PylonItemStackBuilder.kt | 134 ------------------ 2 files changed, 126 insertions(+), 140 deletions(-) delete mode 100644 pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt index 3b43b96bb..9842985c1 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt @@ -13,14 +13,24 @@ import io.papermc.paper.datacomponent.DataComponentTypes import io.papermc.paper.datacomponent.item.CustomModelData import io.papermc.paper.datacomponent.item.ItemAttributeModifiers import io.papermc.paper.datacomponent.item.ItemLore +import io.papermc.paper.datacomponent.item.Tool +import io.papermc.paper.datacomponent.item.UseCooldown +import io.papermc.paper.datacomponent.item.Weapon +import io.papermc.paper.registry.keys.tags.BlockTypeTagKeys +import io.papermc.paper.registry.set.RegistryKeySet +import net.kyori.adventure.key.Key import net.kyori.adventure.text.Component import net.kyori.adventure.text.ComponentLike +import net.kyori.adventure.util.TriState import org.apache.commons.lang3.LocaleUtils import org.bukkit.Color import org.bukkit.Material import org.bukkit.NamespacedKey +import org.bukkit.Registry import org.bukkit.attribute.Attribute import org.bukkit.attribute.AttributeModifier +import org.bukkit.block.BlockType +import org.bukkit.inventory.EquipmentSlotGroup import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.ItemMeta import org.bukkit.persistence.PersistentDataContainer @@ -175,6 +185,112 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro } } + fun removeAttributeModifier( + attribute: Attribute, + modifierKey: NamespacedKey + ) = removeAttributeModifiers(attribute) { it.key == modifierKey } + + fun removeAttributeModifiers( + attribute: Attribute + ) = removeAttributeModifiers(attribute) { true } + + fun removeAttributeModifiers( + attribute: Attribute, + predicate: (AttributeModifier) -> Boolean + ) = apply { + editDataOrSet(DataComponentTypes.ATTRIBUTE_MODIFIERS) { modifiers -> + val copying = modifiers?.modifiers()?.filter { it.attribute() != attribute || !predicate(it.modifier()) } + ItemAttributeModifiers.itemAttributes().copy(copying).build() + } + } + + fun helmet( + armor: Double, + armorToughness: Double + ) = armor(EquipmentSlotGroup.HEAD, armor, armorToughness) + + fun chestPlate( + armor: Double, + armorToughness: Double + ) = armor(EquipmentSlotGroup.CHEST, armor, armorToughness) + + fun leggings( + armor: Double, + armorToughness: Double + ) = armor(EquipmentSlotGroup.LEGS, armor, armorToughness) + + fun boots( + armor: Double, + armorToughness: Double + ) = armor(EquipmentSlotGroup.FEET, armor, armorToughness) + + fun armor( + slot: EquipmentSlotGroup, + armor: Double, + armorToughness: Double + ) = apply { + removeAttributeModifiers(Attribute.ARMOR) + removeAttributeModifiers(Attribute.ARMOR_TOUGHNESS) + addAttributeModifier(Attribute.ARMOR, AttributeModifier(baseArmor, armor, AttributeModifier.Operation.ADD_NUMBER, slot)) + addAttributeModifier(Attribute.ARMOR_TOUGHNESS, AttributeModifier(baseArmorToughness, armorToughness, AttributeModifier.Operation.ADD_NUMBER, slot)) + } + + fun axe( + miningSpeed: Float, + miningDurabilityDamage: Int + ) = tool(Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_AXE), miningSpeed, miningDurabilityDamage) + + fun pickaxe( + miningSpeed: Float, + miningDurabilityDamage: Int + ) = tool(Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_PICKAXE), miningSpeed, miningDurabilityDamage) + + fun shovel( + miningSpeed: Float, + miningDurabilityDamage: Int + ) = tool(Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_SHOVEL), miningSpeed, miningDurabilityDamage) + + fun hoe( + miningSpeed: Float, + miningDurabilityDamage: Int + ) = tool(Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_HOE), miningSpeed, miningDurabilityDamage) + + fun tool( + blocks: RegistryKeySet, + miningSpeed: Float, + miningDurabilityDamage: Int + ) = apply { + set(DataComponentTypes.TOOL, Tool.tool() + .defaultMiningSpeed(miningSpeed) + .damagePerBlock(miningDurabilityDamage) + .addRule(Tool.rule(blocks, miningSpeed, TriState.TRUE))) + } + + fun noTool() = unset(DataComponentTypes.TOOL) + + fun weapon( + attackDamage: Double, + attackSpeed: Double, + attackDurabilityDamage: Int, + disableShieldSeconds: Float + ) = apply { + addAttributeModifier(Attribute.ATTACK_DAMAGE, AttributeModifier(baseAttackDamage, -1.0 + attackDamage, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlotGroup.MAINHAND)) + addAttributeModifier(Attribute.ATTACK_SPEED, AttributeModifier(baseAttackSpeed, -4.0 + attackSpeed, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlotGroup.MAINHAND)) + set(DataComponentTypes.WEAPON, Weapon.weapon() + .itemDamagePerAttack(attackDurabilityDamage) + .disableBlockingForSeconds(disableShieldSeconds)) + } + + fun attackKnockback(knockback: Double) = apply { + removeAttributeModifiers(Attribute.ATTACK_KNOCKBACK) + addAttributeModifier(Attribute.ATTACK_KNOCKBACK, AttributeModifier(baseAttackKnockback, knockback, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlotGroup.MAINHAND)) + } + + fun durability(durability: Int) = set(DataComponentTypes.MAX_DAMAGE, durability) + + fun useCooldown(cooldownTicks: Int, cooldownGroup: Key?) + = set(DataComponentTypes.USE_COOLDOWN, UseCooldown.useCooldown(cooldownTicks / 20.0f).cooldownGroup(cooldownGroup)) + fun build(): ItemStack = stack.clone() /** @@ -193,8 +309,12 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro companion object { + val baseArmor = NamespacedKey.minecraft("base_armor") + val baseArmorToughness = NamespacedKey.minecraft("base_armor_toughness") + val baseAttackDamage = NamespacedKey.minecraft("base_attack_damage") val baseAttackSpeed = NamespacedKey.minecraft("base_attack_speed") + val baseAttackKnockback = NamespacedKey.minecraft("base_attack_knockback") /** * The default name language key for a Pylon item. @@ -230,20 +350,20 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * provided [key]. */ @JvmStatic - fun pylonItem(material: Material, key: NamespacedKey): PylonItemStackBuilder { - return PylonItemStackBuilder(ItemStack(material), key) + fun pylonItem(material: Material, key: NamespacedKey): ItemStackBuilder { + return ItemStackBuilder(ItemStack(material)) .editPdc { pdc -> pdc.set(PylonItemSchema.pylonItemKeyKey, PylonSerializers.NAMESPACED_KEY, key) } .set(DataComponentTypes.CUSTOM_MODEL_DATA, CustomModelData.customModelData().addString(key.toString())) .defaultTranslatableName(key) - .defaultTranslatableLore(key) as PylonItemStackBuilder + .defaultTranslatableLore(key) } /** * Returns an [ItemStackBuilder] with name and lore set to the default translation keys, and with the item's ID set to [key] */ @JvmStatic - fun pylonItem(stack: ItemStack, key: NamespacedKey): PylonItemStackBuilder { - return PylonItemStackBuilder(stack, key) + fun pylonItem(stack: ItemStack, key: NamespacedKey): ItemStackBuilder { + return ItemStackBuilder(stack) .editPdc { it.set(PylonItemSchema.pylonItemKeyKey, PylonSerializers.NAMESPACED_KEY, key) } .let { // Adds the pylon item key as the FIRST string in custom model data, but preserve any pre-existing data @@ -256,7 +376,7 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro it.set(DataComponentTypes.CUSTOM_MODEL_DATA, modelData) } .defaultTranslatableName(key) - .defaultTranslatableLore(key) as PylonItemStackBuilder + .defaultTranslatableLore(key) } fun ItemAttributeModifiers.Builder.copy(modifiers: List?) : ItemAttributeModifiers.Builder { diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt deleted file mode 100644 index 992fbd66a..000000000 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt +++ /dev/null @@ -1,134 +0,0 @@ -package io.github.pylonmc.pylon.core.item.builder - -import io.github.pylonmc.pylon.core.config.Settings -import io.github.pylonmc.pylon.core.config.adapter.ConfigAdapter -import io.papermc.paper.datacomponent.DataComponentTypes -import io.papermc.paper.datacomponent.item.ItemAttributeModifiers -import io.papermc.paper.datacomponent.item.Tool -import io.papermc.paper.datacomponent.item.UseCooldown -import io.papermc.paper.datacomponent.item.Weapon -import io.papermc.paper.registry.keys.tags.BlockTypeTagKeys -import io.papermc.paper.registry.set.RegistryKeySet -import net.kyori.adventure.util.TriState -import org.bukkit.NamespacedKey -import org.bukkit.Registry -import org.bukkit.attribute.Attribute -import org.bukkit.attribute.AttributeModifier -import org.bukkit.block.BlockType -import org.bukkit.inventory.EquipmentSlotGroup -import org.bukkit.inventory.ItemStack - -@Suppress("UnstableApiUsage") -class PylonItemStackBuilder : ItemStackBuilder { - private val itemKey: NamespacedKey - - internal constructor(stack: ItemStack, itemKey: NamespacedKey) : super(stack) { - this.itemKey = itemKey - } - - @JvmOverloads - fun helmet( - armor: Double = Settings.get(itemKey).getOrThrow("armor", ConfigAdapter.DOUBLE), - armorToughness: Double = Settings.get(itemKey).getOrThrow("armor-toughness", ConfigAdapter.DOUBLE) - ) = armor(EquipmentSlotGroup.HEAD, armor, armorToughness) - - @JvmOverloads - fun chestPlate( - armor: Double = Settings.get(itemKey).getOrThrow("armor", ConfigAdapter.DOUBLE), - armorToughness: Double = Settings.get(itemKey).getOrThrow("armor-toughness", ConfigAdapter.DOUBLE) - ) = armor(EquipmentSlotGroup.CHEST, armor, armorToughness) - - @JvmOverloads - fun leggings( - armor: Double = Settings.get(itemKey).getOrThrow("armor", ConfigAdapter.DOUBLE), - armorToughness: Double = Settings.get(itemKey).getOrThrow("armor-toughness", ConfigAdapter.DOUBLE) - ) = armor(EquipmentSlotGroup.LEGS, armor, armorToughness) - - @JvmOverloads - fun boots( - armor: Double = Settings.get(itemKey).getOrThrow("armor", ConfigAdapter.DOUBLE), - armorToughness: Double = Settings.get(itemKey).getOrThrow("armor-toughness", ConfigAdapter.DOUBLE) - ) = armor(EquipmentSlotGroup.FEET, armor, armorToughness) - - @JvmOverloads - fun armor( - slot: EquipmentSlotGroup, - armor: Double = Settings.get(itemKey).getOrThrow("armor", ConfigAdapter.DOUBLE), - armorToughness: Double = Settings.get(itemKey).getOrThrow("armor-toughness", ConfigAdapter.DOUBLE) - ) = apply { - editDataOrSet(DataComponentTypes.ATTRIBUTE_MODIFIERS) { modifiers -> - val copying = modifiers?.modifiers()?.filter { it.modifier().key.namespace != "minecraft" || !it.modifier().key.key.contains("armor.") } - ItemAttributeModifiers.itemAttributes().copy(copying) - .addModifier(Attribute.ARMOR, AttributeModifier(itemKey, armor, AttributeModifier.Operation.ADD_NUMBER, slot)) - .addModifier(Attribute.ARMOR_TOUGHNESS, AttributeModifier(itemKey, armorToughness, AttributeModifier.Operation.ADD_NUMBER, slot)) - .build() - } - } - - @JvmOverloads - fun axe( - miningSpeed: Float = Settings.get(itemKey).getOrThrow("mining-speed", ConfigAdapter.FLOAT), - miningDurabilityDamage: Int = Settings.get(itemKey).getOrThrow("mining-durability-damage", ConfigAdapter.INT) - ) = tool(Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_AXE), miningSpeed, miningDurabilityDamage) - - @JvmOverloads - fun pickaxe( - miningSpeed: Float = Settings.get(itemKey).getOrThrow("mining-speed", ConfigAdapter.FLOAT), - miningDurabilityDamage: Int = Settings.get(itemKey).getOrThrow("mining-durability-damage", ConfigAdapter.INT) - ) = tool(Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_PICKAXE), miningSpeed, miningDurabilityDamage) - - @JvmOverloads - fun shovel( - miningSpeed: Float = Settings.get(itemKey).getOrThrow("mining-speed", ConfigAdapter.FLOAT), - miningDurabilityDamage: Int = Settings.get(itemKey).getOrThrow("mining-durability-damage", ConfigAdapter.INT) - ) = tool(Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_SHOVEL), miningSpeed, miningDurabilityDamage) - - @JvmOverloads - fun hoe( - miningSpeed: Float = Settings.get(itemKey).getOrThrow("mining-speed", ConfigAdapter.FLOAT), - miningDurabilityDamage: Int = Settings.get(itemKey).getOrThrow("mining-durability-damage", ConfigAdapter.INT) - ) = tool(Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_HOE), miningSpeed, miningDurabilityDamage) - - @JvmOverloads - fun tool( - blocks: RegistryKeySet, - miningSpeed: Float = Settings.get(itemKey).getOrThrow("mining-speed", ConfigAdapter.FLOAT), - miningDurabilityDamage: Int = Settings.get(itemKey).getOrThrow("mining-durability-damage", ConfigAdapter.INT) - ) = apply { - set(DataComponentTypes.TOOL, Tool.tool() - .defaultMiningSpeed(miningSpeed) - .damagePerBlock(miningDurabilityDamage) - .addRule(Tool.rule(blocks, miningSpeed, TriState.TRUE))) - } - - fun noTool() = unset(DataComponentTypes.TOOL) as PylonItemStackBuilder - - @JvmOverloads - fun weapon( - disablesShield: Boolean = false, - attackDamage: Double = Settings.get(itemKey).getOrThrow("attack-damage", ConfigAdapter.DOUBLE), - attackSpeed: Double = Settings.get(itemKey).getOrThrow("attack-speed", ConfigAdapter.DOUBLE), - attackDurabilityDamage: Int = Settings.get(itemKey).getOrThrow("attack-durability-damage", ConfigAdapter.INT), - disableShieldSeconds: Float? = null - ) = apply { - addAttributeModifier(Attribute.ATTACK_DAMAGE, AttributeModifier(baseAttackDamage, -1.0 + attackDamage, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlotGroup.MAINHAND)) - addAttributeModifier(Attribute.ATTACK_SPEED, AttributeModifier(baseAttackSpeed, -4.0 + attackSpeed, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlotGroup.MAINHAND)) - set(DataComponentTypes.WEAPON, Weapon.weapon() - .itemDamagePerAttack(attackDurabilityDamage) - .disableBlockingForSeconds(if (disablesShield) disableShieldSeconds ?: Settings.get(itemKey).getOrThrow("disable-shield-seconds", ConfigAdapter.FLOAT) else 0f)) - } - - @JvmOverloads - fun attackKnockback(knockback: Double = Settings.get(itemKey).getOrThrow("attack-knockback", ConfigAdapter.DOUBLE)) = - addAttributeModifier(Attribute.ATTACK_KNOCKBACK, AttributeModifier(itemKey, knockback, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlotGroup.MAINHAND)) as PylonItemStackBuilder - - @JvmOverloads - fun durability( - durability: Int = Settings.get(itemKey).getOrThrow("durability", ConfigAdapter.INT) - ) = set(DataComponentTypes.MAX_DAMAGE, durability) as PylonItemStackBuilder - - @JvmOverloads - fun useCooldown( - cooldownTicks: Int = Settings.get(itemKey).getOrThrow("cooldown-ticks", ConfigAdapter.INT) - ) = set(DataComponentTypes.USE_COOLDOWN, UseCooldown.useCooldown(cooldownTicks / 20.0f).cooldownGroup(itemKey)) as PylonItemStackBuilder -} \ No newline at end of file From 8f91e3ce3d007d1ac759d82ce2d58b5ce3595d79 Mon Sep 17 00:00:00 2001 From: JustAHuman-xD Date: Sun, 28 Sep 2025 15:13:34 -0500 Subject: [PATCH 04/16] about to rebase --- .../pylonmc/pylon/core/fluid/PylonFluid.kt | 18 +++++++- .../pylon/core/guide/button/BackButton.kt | 3 +- .../core/guide/button/CullingPresetButton.kt | 4 +- .../pylon/core/guide/button/ResearchButton.kt | 3 ++ .../guide/button/ToggleArmorTexturesButton.kt | 8 ++-- .../guide/button/ToggleBlockTexturesButton.kt | 8 ++-- .../core/guide/button/ToggleWailaButton.kt | 8 ++-- .../pages/base/SimpleDynamicGuidePage.kt | 3 +- .../pages/research/AddonResearchesPage.kt | 2 +- .../core/item/builder/ItemStackBuilder.kt | 41 ++++++++++--------- .../pylonmc/pylon/core/util/gui/GuiItems.kt | 13 +++--- 11 files changed, 70 insertions(+), 41 deletions(-) diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/fluid/PylonFluid.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/fluid/PylonFluid.kt index 6b9e4c0f1..21a13009b 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/fluid/PylonFluid.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/fluid/PylonFluid.kt @@ -1,14 +1,17 @@ package io.github.pylonmc.pylon.core.fluid import io.github.pylonmc.pylon.core.PylonCore +import io.github.pylonmc.pylon.core.datatypes.PylonSerializers import io.github.pylonmc.pylon.core.i18n.PylonTranslator.Companion.translator import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder import io.github.pylonmc.pylon.core.registry.PylonRegistry import io.github.pylonmc.pylon.core.util.getAddon +import io.github.pylonmc.pylon.core.util.pylonKey import net.kyori.adventure.text.Component import org.bukkit.Keyed import org.bukkit.Material import org.bukkit.NamespacedKey +import org.bukkit.inventory.ItemStack /** * Fluids aren't necessarily just liquids, they can also be gases or other substances that can flow. @@ -29,8 +32,9 @@ open class PylonFluid( val item by lazy { val builder = ItemStackBuilder.of(material) - .name(Component.translatable("pylon.${key.namespace}.fluid.${key.key}")) - .addCustomModelDataString(key.toString()) + .editPdc { it.set(pylonFluidKeyKey, PylonSerializers.NAMESPACED_KEY, key) } + .addCustomModelDataString("$pylonFluidKeyKey:$key") + .name(name) for (tag in tags) { builder.lore(tag.displayText) @@ -91,4 +95,14 @@ open class PylonFluid( override fun equals(other: Any?): Boolean = other is PylonFluid && key == other.key override fun hashCode(): Int = key.hashCode() override fun toString(): String = key.toString() + + companion object { + val pylonFluidKeyKey = pylonKey("pylon_fluid_key") + + fun fromStack(stack: ItemStack?): PylonFluid? { + if (stack == null || stack.isEmpty) return null + val id = stack.persistentDataContainer.get(pylonFluidKeyKey, PylonSerializers.NAMESPACED_KEY) ?: return null + return PylonRegistry.FLUIDS[id] + } + } } \ No newline at end of file diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/BackButton.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/BackButton.kt index 00b58f972..24f5104b1 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/BackButton.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/BackButton.kt @@ -2,6 +2,7 @@ package io.github.pylonmc.pylon.core.guide.button import io.github.pylonmc.pylon.core.content.guide.PylonGuide import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder +import io.github.pylonmc.pylon.core.util.pylonKey import net.kyori.adventure.text.Component import org.bukkit.Material import org.bukkit.entity.Player @@ -14,7 +15,7 @@ import xyz.xenondevs.invui.item.impl.AbstractItem */ open class BackButton() : AbstractItem() { - override fun getItemProvider() = ItemStackBuilder.of(Material.ENCHANTED_BOOK) + override fun getItemProvider() = ItemStackBuilder.gui(Material.ENCHANTED_BOOK, pylonKey("guide_back")) .name(Component.translatable("pylon.pyloncore.guide.button.back.name")) .lore(Component.translatable("pylon.pyloncore.guide.button.back.lore")) diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/CullingPresetButton.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/CullingPresetButton.kt index d0602fbb6..9b3b7bff8 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/CullingPresetButton.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/CullingPresetButton.kt @@ -1,10 +1,10 @@ package io.github.pylonmc.pylon.core.guide.button import io.github.pylonmc.pylon.core.resourcepack.block.BlockTextureEngine.cullingPreset -import io.github.pylonmc.pylon.core.config.PylonConfig import io.github.pylonmc.pylon.core.i18n.PylonArgument import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder import io.github.pylonmc.pylon.core.resourcepack.block.BlockTextureConfig +import io.github.pylonmc.pylon.core.util.pylonKey import net.kyori.adventure.text.Component import org.bukkit.entity.Player import org.bukkit.event.inventory.ClickType @@ -15,7 +15,7 @@ import xyz.xenondevs.invui.item.impl.AbstractItem class CullingPresetButton : AbstractItem() { override fun getItemProvider(player: Player): ItemProvider? { val preset = player.cullingPreset - return ItemStackBuilder.of(preset.material) + return ItemStackBuilder.gui(preset.material, pylonKey("guide_culling_preset_${preset.id}")) .name(Component.translatable("pylon.pyloncore.guide.button.culling-preset.${preset.id}.name")) .lore( Component.translatable("pylon.pyloncore.guide.button.culling-preset.${preset.id}.lore") diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ResearchButton.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ResearchButton.kt index 68e77b1d3..1e763e221 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ResearchButton.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ResearchButton.kt @@ -6,6 +6,8 @@ import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder import io.github.pylonmc.pylon.core.item.research.Research import io.github.pylonmc.pylon.core.item.research.Research.Companion.researchPoints import io.github.pylonmc.pylon.core.util.getAddon +import io.github.pylonmc.pylon.core.util.gui.GuiItems +import io.github.pylonmc.pylon.core.util.pylonKey import net.kyori.adventure.text.Component import org.bukkit.Material import org.bukkit.entity.Player @@ -22,6 +24,7 @@ open class ResearchButton(val research: Research) : AbstractItem() { override fun getItemProvider(player: Player): ItemProvider = try { val playerHasResearch = Research.getResearches(player).contains(research) val item = ItemStackBuilder.of(if (playerHasResearch) Material.LIME_STAINED_GLASS_PANE else research.material) + .addCustomModelDataString("${GuiItems.pylonGuiItemKeyKey}:${pylonKey("research")}:${research.key}:$playerHasResearch") .name(research.name) if (playerHasResearch) { diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleArmorTexturesButton.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleArmorTexturesButton.kt index d404f4c88..547ee5ff4 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleArmorTexturesButton.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleArmorTexturesButton.kt @@ -2,6 +2,7 @@ package io.github.pylonmc.pylon.core.guide.button import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder import io.github.pylonmc.pylon.core.resourcepack.armor.ArmorTextureEngine.hasCustomArmorTextures +import io.github.pylonmc.pylon.core.util.pylonKey import net.kyori.adventure.text.Component import org.bukkit.Material import org.bukkit.entity.Player @@ -10,9 +11,10 @@ import org.bukkit.event.inventory.InventoryClickEvent import xyz.xenondevs.invui.item.impl.AbstractItem class ToggleArmorTexturesButton : AbstractItem() { - override fun getItemProvider(player: Player) = ItemStackBuilder.of(if (player.hasCustomArmorTextures) Material.LIME_CONCRETE else Material.RED_CONCRETE) - .name(Component.translatable("pylon.pyloncore.guide.button.toggle-armor-textures.name")) - .lore(Component.translatable("pylon.pyloncore.guide.button.toggle-armor-textures.lore")) + override fun getItemProvider(player: Player) + = ItemStackBuilder.gui(if (player.hasCustomArmorTextures) Material.LIME_CONCRETE else Material.RED_CONCRETE, pylonKey("toggle_armor_textures:${player.hasCustomArmorTextures}")) + .name(Component.translatable("pylon.pyloncore.guide.button.toggle-armor-textures.name")) + .lore(Component.translatable("pylon.pyloncore.guide.button.toggle-armor-textures.lore")) override fun handleClick(clickType: ClickType, player: Player, event: InventoryClickEvent) { player.hasCustomArmorTextures = !player.hasCustomArmorTextures diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleBlockTexturesButton.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleBlockTexturesButton.kt index f15a5fcc1..b890b687d 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleBlockTexturesButton.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleBlockTexturesButton.kt @@ -3,6 +3,7 @@ package io.github.pylonmc.pylon.core.guide.button import io.github.pylonmc.pylon.core.resourcepack.block.BlockTextureEngine.hasCustomBlockTextures import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder import io.github.pylonmc.pylon.core.resourcepack.block.BlockTextureEngine +import io.github.pylonmc.pylon.core.util.pylonKey import net.kyori.adventure.text.Component import org.bukkit.Material import org.bukkit.entity.Player @@ -11,9 +12,10 @@ import org.bukkit.event.inventory.InventoryClickEvent import xyz.xenondevs.invui.item.impl.AbstractItem class ToggleBlockTexturesButton : AbstractItem() { - override fun getItemProvider(player: Player) = ItemStackBuilder.of(if (player.hasCustomBlockTextures) Material.LIME_CONCRETE else Material.RED_CONCRETE) - .name(Component.translatable("pylon.pyloncore.guide.button.toggle-block-textures.name")) - .lore(Component.translatable("pylon.pyloncore.guide.button.toggle-block-textures.lore")) + override fun getItemProvider(player: Player) + = ItemStackBuilder.gui(if (player.hasCustomBlockTextures) Material.LIME_CONCRETE else Material.RED_CONCRETE, pylonKey("toggle_block_textures:${player.hasCustomBlockTextures}")) + .name(Component.translatable("pylon.pyloncore.guide.button.toggle-block-textures.name")) + .lore(Component.translatable("pylon.pyloncore.guide.button.toggle-block-textures.lore")) override fun handleClick(clickType: ClickType, player: Player, event: InventoryClickEvent) { player.hasCustomBlockTextures = !player.hasCustomBlockTextures diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleWailaButton.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleWailaButton.kt index 3bebde6d8..fd96bf71c 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleWailaButton.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleWailaButton.kt @@ -2,6 +2,7 @@ package io.github.pylonmc.pylon.core.guide.button import io.github.pylonmc.pylon.core.block.waila.Waila.Companion.wailaEnabled import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder +import io.github.pylonmc.pylon.core.util.pylonKey import net.kyori.adventure.text.Component import org.bukkit.Material import org.bukkit.entity.Player @@ -14,9 +15,10 @@ import xyz.xenondevs.invui.item.impl.AbstractItem */ class ToggleWailaButton : AbstractItem() { - override fun getItemProvider(player: Player) = ItemStackBuilder.of(if (player.wailaEnabled) Material.LIME_CONCRETE else Material.RED_CONCRETE) - .name(Component.translatable("pylon.pyloncore.guide.button.toggle-waila.name")) - .lore(Component.translatable("pylon.pyloncore.guide.button.toggle-waila.lore")) + override fun getItemProvider(player: Player) + = ItemStackBuilder.gui(if (player.wailaEnabled) Material.LIME_CONCRETE else Material.RED_CONCRETE, pylonKey("toggle_waila:${player.wailaEnabled}")) + .name(Component.translatable("pylon.pyloncore.guide.button.toggle-waila.name")) + .lore(Component.translatable("pylon.pyloncore.guide.button.toggle-waila.lore")) override fun handleClick(clickType: ClickType, player: Player, event: InventoryClickEvent) { player.wailaEnabled = !player.wailaEnabled diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/base/SimpleDynamicGuidePage.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/base/SimpleDynamicGuidePage.kt index 79d5092bb..03b80fb55 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/base/SimpleDynamicGuidePage.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/base/SimpleDynamicGuidePage.kt @@ -5,6 +5,7 @@ import io.github.pylonmc.pylon.core.guide.button.BackButton import io.github.pylonmc.pylon.core.guide.button.PageButton import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder import io.github.pylonmc.pylon.core.util.gui.GuiItems +import io.github.pylonmc.pylon.core.util.pylonKey import net.kyori.adventure.text.Component import org.bukkit.Material import org.bukkit.NamespacedKey @@ -41,7 +42,7 @@ open class SimpleDynamicGuidePage( override fun getKey() = key - override val item = ItemStackBuilder.of(material) + override val item = ItemStackBuilder.gui(material, pylonKey("guide_page:$key")) .name(Component.translatable("pylon.${key.namespace}.guide.page.${key.key}")) /** diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/research/AddonResearchesPage.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/research/AddonResearchesPage.kt index 0594f0458..cbbd5d5b4 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/research/AddonResearchesPage.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/research/AddonResearchesPage.kt @@ -24,7 +24,7 @@ class AddonResearchesPage(val addon: PylonAddon) : SimpleDynamicGuidePage( } ) { override val item: ItemStackBuilder - get() = ItemStackBuilder.of(addon.material) + get() = ItemStackBuilder.gui(addon.material, addon.key) .name(addon.displayName) override val title: Component diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt index 9842985c1..96a67e6d3 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt @@ -7,6 +7,7 @@ import io.github.pylonmc.pylon.core.util.editData import io.github.pylonmc.pylon.core.util.editDataOrDefault import io.github.pylonmc.pylon.core.util.editDataOrSet import io.github.pylonmc.pylon.core.util.fromMiniMessage +import io.github.pylonmc.pylon.core.util.gui.GuiItems import io.papermc.paper.datacomponent.DataComponentBuilder import io.papermc.paper.datacomponent.DataComponentType import io.papermc.paper.datacomponent.DataComponentTypes @@ -344,39 +345,39 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro return of(ItemStack(material)) } + @JvmStatic + fun gui(stack: ItemStack, key: NamespacedKey): ItemStackBuilder { + return ItemStackBuilder(stack) + .addCustomModelDataString("${GuiItems.pylonGuiItemKeyKey}:$key") + } + + @JvmStatic + fun gui(material: Material, key: NamespacedKey): ItemStackBuilder { + return gui(ItemStack(material), key) + } + /** * Creates a new [ItemStack] for a [io.github.pylonmc.pylon.core.item.PylonItem] by setting * the name and lore to the default translation keys, and setting the item's Pylon ID to the * provided [key]. */ @JvmStatic - fun pylonItem(material: Material, key: NamespacedKey): ItemStackBuilder { - return ItemStackBuilder(ItemStack(material)) - .editPdc { pdc -> pdc.set(PylonItemSchema.pylonItemKeyKey, PylonSerializers.NAMESPACED_KEY, key) } - .set(DataComponentTypes.CUSTOM_MODEL_DATA, CustomModelData.customModelData().addString(key.toString())) + fun pylonItem(stack: ItemStack, key: NamespacedKey): ItemStackBuilder { + return ItemStackBuilder(stack) + .editPdc { it.set(PylonItemSchema.pylonItemKeyKey, PylonSerializers.NAMESPACED_KEY, key) } + .addCustomModelDataString("${PylonItemSchema.pylonItemKeyKey}:$key") .defaultTranslatableName(key) .defaultTranslatableLore(key) } /** - * Returns an [ItemStackBuilder] with name and lore set to the default translation keys, and with the item's ID set to [key] + * Creates a new [ItemStack] for a [io.github.pylonmc.pylon.core.item.PylonItem] by setting + * the name and lore to the default translation keys, and setting the item's Pylon ID to the + * provided [key]. */ @JvmStatic - fun pylonItem(stack: ItemStack, key: NamespacedKey): ItemStackBuilder { - return ItemStackBuilder(stack) - .editPdc { it.set(PylonItemSchema.pylonItemKeyKey, PylonSerializers.NAMESPACED_KEY, key) } - .let { - // Adds the pylon item key as the FIRST string in custom model data, but preserve any pre-existing data - val originalModelData = it.stack.getData(DataComponentTypes.CUSTOM_MODEL_DATA) - val modelData = CustomModelData.customModelData().addString(key.toString()) - if (originalModelData != null) { - modelData.addStrings(originalModelData.strings()).addColors(originalModelData.colors()) - .addFloats(originalModelData.floats()).addFlags(originalModelData.flags()) - } - it.set(DataComponentTypes.CUSTOM_MODEL_DATA, modelData) - } - .defaultTranslatableName(key) - .defaultTranslatableLore(key) + fun pylonItem(material: Material, key: NamespacedKey): ItemStackBuilder { + return pylonItem(ItemStack(material), key) } fun ItemAttributeModifiers.Builder.copy(modifiers: List?) : ItemAttributeModifiers.Builder { diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/gui/GuiItems.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/gui/GuiItems.kt index ba4f6fe3b..874ce2091 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/gui/GuiItems.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/gui/GuiItems.kt @@ -5,6 +5,7 @@ package io.github.pylonmc.pylon.core.util.gui import io.github.pylonmc.pylon.core.i18n.PylonArgument import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder import io.github.pylonmc.pylon.core.util.gui.GuiItems.background +import io.github.pylonmc.pylon.core.util.pylonKey import io.papermc.paper.datacomponent.DataComponentTypes import io.papermc.paper.datacomponent.item.TooltipDisplay import net.kyori.adventure.text.Component @@ -22,13 +23,15 @@ import xyz.xenondevs.invui.item.impl.controlitem.ScrollItem * A utility class containing items commonly used in GUIs. */ object GuiItems { + val pylonGuiItemKeyKey = pylonKey("gui_item_key") + /** * A gray glass pane with no name or lore. */ @JvmStatic @JvmOverloads fun background(name: String = ""): Item = SimpleItem( - ItemStackBuilder.of(Material.GRAY_STAINED_GLASS_PANE) + ItemStackBuilder.gui(Material.GRAY_STAINED_GLASS_PANE, pylonKey("background")) .name(name) .set(DataComponentTypes.TOOLTIP_DISPLAY, TooltipDisplay.tooltipDisplay().hideTooltip(true)) ) @@ -39,7 +42,7 @@ object GuiItems { @JvmStatic @JvmOverloads fun backgroundBlack(name: String = ""): Item = SimpleItem( - ItemStackBuilder.of(Material.BLACK_STAINED_GLASS_PANE) + ItemStackBuilder.gui(Material.BLACK_STAINED_GLASS_PANE, pylonKey("background_black")) .name(name) .set(DataComponentTypes.TOOLTIP_DISPLAY, TooltipDisplay.tooltipDisplay().hideTooltip(true)) ) @@ -109,13 +112,13 @@ object GuiItems { fun pagePrevious(): Item = PylonPageItem(false) } -private class PylonScrollItem(private val direction: Int, key: String?) : ScrollItem(direction) { +private class PylonScrollItem(private val direction: Int, private val key: String?) : ScrollItem(direction) { private val name = Component.translatable("pylon.pyloncore.gui.scroll.$key") override fun getItemProvider(gui: ScrollGui<*>): ItemProvider { val material = if (gui.canScroll(direction)) Material.GREEN_STAINED_GLASS_PANE else Material.RED_STAINED_GLASS_PANE - return ItemStackBuilder.of(material).name(name) + return ItemStackBuilder.gui(material, pylonKey("scroll_$key")).name(name) } } @@ -127,7 +130,7 @@ private class PylonPageItem(private val forward: Boolean) : PageItem(forward) { if (gui.pageAmount < 2) return background val material = if (gui.canPage) Material.GREEN_STAINED_GLASS_PANE else Material.RED_STAINED_GLASS_PANE - return ItemStackBuilder.of(material) + return ItemStackBuilder.gui(material, pylonKey("page_${if (forward) "next" else "previous"}")) .name( name.arguments( PylonArgument.of("current", gui.currentPage + 1), From d716d0b533b530ddfe0dc0a3e3879302033620e7 Mon Sep 17 00:00:00 2001 From: JustAHuman-xD Date: Sun, 28 Sep 2025 16:03:34 -0500 Subject: [PATCH 05/16] reworked pylon itemstack builder --- .../item/builder/PylonItemStackBuilder.kt | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt new file mode 100644 index 000000000..67b81649a --- /dev/null +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt @@ -0,0 +1,104 @@ +package io.github.pylonmc.pylon.core.item.builder + +import io.github.pylonmc.pylon.core.config.Config +import io.github.pylonmc.pylon.core.config.Settings +import io.github.pylonmc.pylon.core.config.adapter.ConfigAdapter +import io.papermc.paper.registry.keys.tags.BlockTypeTagKeys +import io.papermc.paper.registry.set.RegistryKeySet +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.Registry +import org.bukkit.block.BlockType +import org.bukkit.inventory.EquipmentSlotGroup +import org.bukkit.inventory.ItemStack + +@Suppress("UnstableApiUsage") +object PylonItemStackBuilder { + + @JvmStatic + fun helmet(stack: ItemStack, key: NamespacedKey) = armor(stack, key, EquipmentSlotGroup.HEAD) + + @JvmStatic + fun helmet(material: Material, key: NamespacedKey) = helmet(ItemStack(material), key) + + @JvmStatic + fun chestPlate(stack: ItemStack, key: NamespacedKey) = armor(stack, key, EquipmentSlotGroup.CHEST) + + @JvmStatic + fun chestPlate(material: Material, key: NamespacedKey) = chestPlate(ItemStack(material), key) + + @JvmStatic + fun leggings(stack: ItemStack, key: NamespacedKey) = armor(stack, key, EquipmentSlotGroup.LEGS) + + @JvmStatic + fun leggings(material: Material, key: NamespacedKey) = leggings(ItemStack(material), key) + + @JvmStatic + fun boots(stack: ItemStack, key: NamespacedKey) = armor(stack, key, EquipmentSlotGroup.FEET) + + @JvmStatic + fun boots(material: Material, key: NamespacedKey) = boots(ItemStack(material), key) + + @JvmStatic + fun armor(stack: ItemStack, key: NamespacedKey, slot: EquipmentSlotGroup) = create(stack, key) { builder, settings -> + builder.armor( + slot, + settings.getOrThrow("armor", ConfigAdapter.DOUBLE), + settings.getOrThrow("armor-toughness", ConfigAdapter.DOUBLE) + ) + } + + @JvmStatic + fun axe(stack: ItemStack, key: NamespacedKey) = tool(stack, key, Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_AXE)) + + @JvmStatic + fun axe(material: Material, key: NamespacedKey) = axe(ItemStack(material), key) + + @JvmStatic + fun pickaxe(stack: ItemStack, key: NamespacedKey) = tool(stack, key, Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_PICKAXE)) + + @JvmStatic + fun pickaxe(material: Material, key: NamespacedKey) = pickaxe(ItemStack(material), key) + + @JvmStatic + fun shovel(stack: ItemStack, key: NamespacedKey) = tool(stack, key, Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_SHOVEL)) + + @JvmStatic + fun shovel(material: Material, key: NamespacedKey) = shovel(ItemStack(material), key) + + @JvmStatic + fun hoe(stack: ItemStack, key: NamespacedKey) = tool(stack, key, Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_HOE)) + + @JvmStatic + fun hoe(material: Material, key: NamespacedKey) = hoe(ItemStack(material), key) + + @JvmStatic + fun tool(stack: ItemStack, key: NamespacedKey, blocks: RegistryKeySet) = create(stack, key) { builder, settings -> + builder.tool( + blocks, + settings.getOrThrow("mining-speed", ConfigAdapter.FLOAT), + settings.getOrThrow("mining-durability-damage", ConfigAdapter.INT) + ) + } + + @JvmStatic + fun weapon(stack: ItemStack, key: NamespacedKey, knockback: Boolean, disablesShield: Boolean) = create(stack, key) { builder, settings -> + builder.weapon( + settings.getOrThrow("attack-damage", ConfigAdapter.DOUBLE), + settings.getOrThrow("attack-speed", ConfigAdapter.DOUBLE), + settings.getOrThrow("attack-durability-damage", ConfigAdapter.INT), + if (disablesShield) settings.getOrThrow("disable-shield-seconds", ConfigAdapter.FLOAT) else 0f + ) + + if (knockback) { + builder.attackKnockback(settings.getOrThrow("attack-knockback", ConfigAdapter.DOUBLE)) + } + } + + private fun create(stack: ItemStack, key: NamespacedKey, consumer: (ItemStackBuilder, Config) -> Unit): ItemStackBuilder { + val builder = ItemStackBuilder.pylonItem(stack, key) + val settings = Settings.get(key) + consumer(builder, settings) + return builder + } +} \ No newline at end of file From 0514a67e099d4229b29c0aa5f10c08c18842c4f8 Mon Sep 17 00:00:00 2001 From: JustAHuman-xD Date: Sun, 28 Sep 2025 16:44:59 -0500 Subject: [PATCH 06/16] continue reworking that --- .../item/builder/PylonItemStackBuilder.kt | 80 +++++++++++-------- .../pylonmc/pylon/core/util/ToolType.kt | 16 ++++ 2 files changed, 61 insertions(+), 35 deletions(-) create mode 100644 pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/ToolType.kt diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt index 67b81649a..661114a4e 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt @@ -3,86 +3,88 @@ package io.github.pylonmc.pylon.core.item.builder import io.github.pylonmc.pylon.core.config.Config import io.github.pylonmc.pylon.core.config.Settings import io.github.pylonmc.pylon.core.config.adapter.ConfigAdapter -import io.papermc.paper.registry.keys.tags.BlockTypeTagKeys -import io.papermc.paper.registry.set.RegistryKeySet +import io.github.pylonmc.pylon.core.util.ToolType import org.bukkit.Material import org.bukkit.NamespacedKey -import org.bukkit.Registry -import org.bukkit.block.BlockType import org.bukkit.inventory.EquipmentSlotGroup import org.bukkit.inventory.ItemStack -@Suppress("UnstableApiUsage") object PylonItemStackBuilder { @JvmStatic - fun helmet(stack: ItemStack, key: NamespacedKey) = armor(stack, key, EquipmentSlotGroup.HEAD) + fun helmet(stack: ItemStack, key: NamespacedKey, durability: Boolean) = armor(stack, key, EquipmentSlotGroup.HEAD, durability) @JvmStatic - fun helmet(material: Material, key: NamespacedKey) = helmet(ItemStack(material), key) + fun helmet(material: Material, key: NamespacedKey, durability: Boolean) = helmet(ItemStack(material), key, durability) @JvmStatic - fun chestPlate(stack: ItemStack, key: NamespacedKey) = armor(stack, key, EquipmentSlotGroup.CHEST) + fun chestPlate(stack: ItemStack, key: NamespacedKey, durability: Boolean) = armor(stack, key, EquipmentSlotGroup.CHEST, durability) @JvmStatic - fun chestPlate(material: Material, key: NamespacedKey) = chestPlate(ItemStack(material), key) + fun chestPlate(material: Material, key: NamespacedKey, durability: Boolean) = chestPlate(ItemStack(material), key, durability) @JvmStatic - fun leggings(stack: ItemStack, key: NamespacedKey) = armor(stack, key, EquipmentSlotGroup.LEGS) + fun leggings(stack: ItemStack, key: NamespacedKey, durability: Boolean) = armor(stack, key, EquipmentSlotGroup.LEGS, durability) @JvmStatic - fun leggings(material: Material, key: NamespacedKey) = leggings(ItemStack(material), key) + fun leggings(material: Material, key: NamespacedKey, durability: Boolean) = leggings(ItemStack(material), key, durability) @JvmStatic - fun boots(stack: ItemStack, key: NamespacedKey) = armor(stack, key, EquipmentSlotGroup.FEET) + fun boots(stack: ItemStack, key: NamespacedKey, durability: Boolean) = armor(stack, key, EquipmentSlotGroup.FEET, durability) @JvmStatic - fun boots(material: Material, key: NamespacedKey) = boots(ItemStack(material), key) + fun boots(material: Material, key: NamespacedKey, durability: Boolean) = boots(ItemStack(material), key, durability) @JvmStatic - fun armor(stack: ItemStack, key: NamespacedKey, slot: EquipmentSlotGroup) = create(stack, key) { builder, settings -> + fun armor(stack: ItemStack, key: NamespacedKey, slot: EquipmentSlotGroup, durability: Boolean) = create(stack, key) { builder, settings -> builder.armor( slot, settings.getOrThrow("armor", ConfigAdapter.DOUBLE), settings.getOrThrow("armor-toughness", ConfigAdapter.DOUBLE) ) - } - - @JvmStatic - fun axe(stack: ItemStack, key: NamespacedKey) = tool(stack, key, Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_AXE)) - - @JvmStatic - fun axe(material: Material, key: NamespacedKey) = axe(ItemStack(material), key) - - @JvmStatic - fun pickaxe(stack: ItemStack, key: NamespacedKey) = tool(stack, key, Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_PICKAXE)) - @JvmStatic - fun pickaxe(material: Material, key: NamespacedKey) = pickaxe(ItemStack(material), key) + if (durability) { + builder.durability(settings.getOrThrow("durability", ConfigAdapter.INT)) + } + } @JvmStatic - fun shovel(stack: ItemStack, key: NamespacedKey) = tool(stack, key, Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_SHOVEL)) + fun armor(material: Material, key: NamespacedKey, slot: EquipmentSlotGroup, durability: Boolean) + = armor(ItemStack(material), key, slot, durability) @JvmStatic - fun shovel(material: Material, key: NamespacedKey) = shovel(ItemStack(material), key) + fun tool(stack: ItemStack, key: NamespacedKey, toolType: ToolType, durability: Boolean) = create(stack, key) { builder, settings -> + builder.tool( + toolType.getTag(), + settings.getOrThrow("mining-speed", ConfigAdapter.FLOAT), + settings.getOrThrow("mining-durability-damage", ConfigAdapter.INT) + ) - @JvmStatic - fun hoe(stack: ItemStack, key: NamespacedKey) = tool(stack, key, Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_HOE)) + if (durability) { + builder.durability(settings.getOrThrow("durability", ConfigAdapter.INT)) + } + } @JvmStatic - fun hoe(material: Material, key: NamespacedKey) = hoe(ItemStack(material), key) + fun tool(material: Material, key: NamespacedKey, toolType: ToolType, durability: Boolean) + = tool(ItemStack(material), key, toolType, durability) @JvmStatic - fun tool(stack: ItemStack, key: NamespacedKey, blocks: RegistryKeySet) = create(stack, key) { builder, settings -> - builder.tool( - blocks, + fun toolWeapon(stack: ItemStack, key: NamespacedKey, toolType: ToolType, durability: Boolean, knockback: Boolean, disablesShield: Boolean): ItemStackBuilder { + val settings = Settings.get(key) + return weapon(stack, key, durability, knockback, disablesShield).tool( + toolType.getTag(), settings.getOrThrow("mining-speed", ConfigAdapter.FLOAT), settings.getOrThrow("mining-durability-damage", ConfigAdapter.INT) ) } @JvmStatic - fun weapon(stack: ItemStack, key: NamespacedKey, knockback: Boolean, disablesShield: Boolean) = create(stack, key) { builder, settings -> + fun toolWeapon(material: Material, key: NamespacedKey, toolType: ToolType, durability: Boolean, knockback: Boolean, disablesShield: Boolean) + = toolWeapon(ItemStack(material), key, toolType, durability, knockback, disablesShield) + + @JvmStatic + fun weapon(stack: ItemStack, key: NamespacedKey, durability: Boolean, knockback: Boolean, disablesShield: Boolean) = create(stack, key) { builder, settings -> builder.weapon( settings.getOrThrow("attack-damage", ConfigAdapter.DOUBLE), settings.getOrThrow("attack-speed", ConfigAdapter.DOUBLE), @@ -93,8 +95,16 @@ object PylonItemStackBuilder { if (knockback) { builder.attackKnockback(settings.getOrThrow("attack-knockback", ConfigAdapter.DOUBLE)) } + + if (durability) { + builder.durability(settings.getOrThrow("durability", ConfigAdapter.INT)) + } } + @JvmStatic + fun weapon(material: Material, key: NamespacedKey, durability: Boolean, knockback: Boolean, disablesShield: Boolean) + = weapon(ItemStack(material), key, durability, knockback, disablesShield) + private fun create(stack: ItemStack, key: NamespacedKey, consumer: (ItemStackBuilder, Config) -> Unit): ItemStackBuilder { val builder = ItemStackBuilder.pylonItem(stack, key) val settings = Settings.get(key) diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/ToolType.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/ToolType.kt new file mode 100644 index 000000000..4a0234a77 --- /dev/null +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/ToolType.kt @@ -0,0 +1,16 @@ +package io.github.pylonmc.pylon.core.util + +import io.papermc.paper.registry.keys.tags.BlockTypeTagKeys +import io.papermc.paper.registry.tag.TagKey +import org.bukkit.Registry +import org.bukkit.block.BlockType + +@Suppress("UnstableApiUsage") +enum class ToolType(val tagKey: TagKey) { + AXE(BlockTypeTagKeys.MINEABLE_AXE), + PICKAXE(BlockTypeTagKeys.MINEABLE_PICKAXE), + SHOVEL(BlockTypeTagKeys.MINEABLE_SHOVEL), + HOE(BlockTypeTagKeys.MINEABLE_HOE); + + fun getTag() = Registry.BLOCK.getTag(tagKey) +} \ No newline at end of file From 82ab213ae61cbf791b96b1da8d1c0c87fd1a7ffc Mon Sep 17 00:00:00 2001 From: JustAHuman-xD Date: Sun, 28 Sep 2025 18:26:50 -0500 Subject: [PATCH 07/16] no context key --- .../pylonmc/pylon/core/fluid/PylonFluid.kt | 2 +- .../pylon/core/guide/button/ResearchButton.kt | 3 +-- .../guide/pages/base/SimpleDynamicGuidePage.kt | 2 +- .../pylon/core/item/builder/ItemStackBuilder.kt | 17 ++++++++++++++--- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/fluid/PylonFluid.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/fluid/PylonFluid.kt index 21a13009b..266e9e012 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/fluid/PylonFluid.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/fluid/PylonFluid.kt @@ -33,7 +33,7 @@ open class PylonFluid( val item by lazy { val builder = ItemStackBuilder.of(material) .editPdc { it.set(pylonFluidKeyKey, PylonSerializers.NAMESPACED_KEY, key) } - .addCustomModelDataString("$pylonFluidKeyKey:$key") + .addCustomModelDataString(key.toString()) .name(name) for (tag in tags) { diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ResearchButton.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ResearchButton.kt index 1e763e221..4ecf5d049 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ResearchButton.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ResearchButton.kt @@ -23,8 +23,7 @@ open class ResearchButton(val research: Research) : AbstractItem() { override fun getItemProvider(player: Player): ItemProvider = try { val playerHasResearch = Research.getResearches(player).contains(research) - val item = ItemStackBuilder.of(if (playerHasResearch) Material.LIME_STAINED_GLASS_PANE else research.material) - .addCustomModelDataString("${GuiItems.pylonGuiItemKeyKey}:${pylonKey("research")}:${research.key}:$playerHasResearch") + val item = ItemStackBuilder.gui(if (playerHasResearch) Material.LIME_STAINED_GLASS_PANE else research.material, "${pylonKey("research")}:${research.key}:$playerHasResearch") .name(research.name) if (playerHasResearch) { diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/base/SimpleDynamicGuidePage.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/base/SimpleDynamicGuidePage.kt index 03b80fb55..4a9272aeb 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/base/SimpleDynamicGuidePage.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/base/SimpleDynamicGuidePage.kt @@ -42,7 +42,7 @@ open class SimpleDynamicGuidePage( override fun getKey() = key - override val item = ItemStackBuilder.gui(material, pylonKey("guide_page:$key")) + override val item = ItemStackBuilder.gui(material, "${pylonKey("guide_page")}:$key") .name(Component.translatable("pylon.${key.namespace}.guide.page.${key.key}")) /** diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt index 96a67e6d3..3b4291646 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt @@ -346,9 +346,20 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro } @JvmStatic - fun gui(stack: ItemStack, key: NamespacedKey): ItemStackBuilder { + fun gui(stack: ItemStack, key: String): ItemStackBuilder { return ItemStackBuilder(stack) - .addCustomModelDataString("${GuiItems.pylonGuiItemKeyKey}:$key") + .editPdc { it.set(GuiItems.pylonGuiItemKeyKey, PylonSerializers.STRING, key) } + .addCustomModelDataString(key.toString()) + } + + @JvmStatic + fun gui(material: Material, key: String): ItemStackBuilder { + return gui(ItemStack(material), key) + } + + @JvmStatic + fun gui(stack: ItemStack, key: NamespacedKey): ItemStackBuilder { + return gui(stack, key.toString()) } @JvmStatic @@ -365,7 +376,7 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro fun pylonItem(stack: ItemStack, key: NamespacedKey): ItemStackBuilder { return ItemStackBuilder(stack) .editPdc { it.set(PylonItemSchema.pylonItemKeyKey, PylonSerializers.NAMESPACED_KEY, key) } - .addCustomModelDataString("${PylonItemSchema.pylonItemKeyKey}:$key") + .addCustomModelDataString(key.toString()) .defaultTranslatableName(key) .defaultTranslatableLore(key) } From 0125cbd9746261679d7896e9a4b69b96242539d3 Mon Sep 17 00:00:00 2001 From: JustAHuman-xD Date: Sun, 28 Sep 2025 18:29:51 -0500 Subject: [PATCH 08/16] implicit values --- .../pylon/core/guide/button/ToggleArmorTexturesButton.kt | 2 +- .../pylon/core/guide/button/ToggleBlockTexturesButton.kt | 2 +- .../github/pylonmc/pylon/core/guide/button/ToggleWailaButton.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleArmorTexturesButton.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleArmorTexturesButton.kt index 547ee5ff4..8c4b1e646 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleArmorTexturesButton.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleArmorTexturesButton.kt @@ -12,7 +12,7 @@ import xyz.xenondevs.invui.item.impl.AbstractItem class ToggleArmorTexturesButton : AbstractItem() { override fun getItemProvider(player: Player) - = ItemStackBuilder.gui(if (player.hasCustomArmorTextures) Material.LIME_CONCRETE else Material.RED_CONCRETE, pylonKey("toggle_armor_textures:${player.hasCustomArmorTextures}")) + = ItemStackBuilder.gui(if (player.hasCustomArmorTextures) Material.LIME_CONCRETE else Material.RED_CONCRETE, pylonKey("toggle_armor_textures")) .name(Component.translatable("pylon.pyloncore.guide.button.toggle-armor-textures.name")) .lore(Component.translatable("pylon.pyloncore.guide.button.toggle-armor-textures.lore")) diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleBlockTexturesButton.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleBlockTexturesButton.kt index b890b687d..b6a1dd480 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleBlockTexturesButton.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleBlockTexturesButton.kt @@ -13,7 +13,7 @@ import xyz.xenondevs.invui.item.impl.AbstractItem class ToggleBlockTexturesButton : AbstractItem() { override fun getItemProvider(player: Player) - = ItemStackBuilder.gui(if (player.hasCustomBlockTextures) Material.LIME_CONCRETE else Material.RED_CONCRETE, pylonKey("toggle_block_textures:${player.hasCustomBlockTextures}")) + = ItemStackBuilder.gui(if (player.hasCustomBlockTextures) Material.LIME_CONCRETE else Material.RED_CONCRETE, pylonKey("toggle_block_textures")) .name(Component.translatable("pylon.pyloncore.guide.button.toggle-block-textures.name")) .lore(Component.translatable("pylon.pyloncore.guide.button.toggle-block-textures.lore")) diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleWailaButton.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleWailaButton.kt index fd96bf71c..8bbbe50ee 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleWailaButton.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleWailaButton.kt @@ -16,7 +16,7 @@ import xyz.xenondevs.invui.item.impl.AbstractItem class ToggleWailaButton : AbstractItem() { override fun getItemProvider(player: Player) - = ItemStackBuilder.gui(if (player.wailaEnabled) Material.LIME_CONCRETE else Material.RED_CONCRETE, pylonKey("toggle_waila:${player.wailaEnabled}")) + = ItemStackBuilder.gui(if (player.wailaEnabled) Material.LIME_CONCRETE else Material.RED_CONCRETE, pylonKey("toggle_waila")) .name(Component.translatable("pylon.pyloncore.guide.button.toggle-waila.name")) .lore(Component.translatable("pylon.pyloncore.guide.button.toggle-waila.lore")) From cf178896e105b80e3488a93b1a854f07c5a3aee2 Mon Sep 17 00:00:00 2001 From: JustAHuman-xD Date: Tue, 30 Sep 2025 01:32:09 -0500 Subject: [PATCH 09/16] docs, move ItemStackBuilder.pylonItem to PylonItemStackBuilder --- .../pylonmc/pylon/core/block/PhantomBlock.kt | 2 +- .../DebugWaxedWeatheredCutCopperStairs.kt | 3 +- .../pylon/core/content/guide/PylonGuide.kt | 2 +- .../pylonmc/pylon/core/item/PylonItem.kt | 2 +- .../core/item/builder/ItemStackBuilder.kt | 36 +- .../item/builder/PylonItemStackBuilder.kt | 374 +++++++++++++++++- .../github/pylonmc/pylon/test/item/Items.java | 2 +- .../pylon/test/item/OminousBlazePower.java | 2 +- 8 files changed, 371 insertions(+), 52 deletions(-) diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PhantomBlock.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PhantomBlock.kt index 82191ab34..1c933f419 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PhantomBlock.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PhantomBlock.kt @@ -85,7 +85,7 @@ class PhantomBlock( companion object { val KEY = pylonKey("error_item") val BLOCK_KEY = pylonKey("block") - val STACK = ItemStackBuilder.pylonItem(Material.BARRIER, KEY) + val STACK = PylonItemStackBuilder.of(Material.BARRIER, KEY) .build() } } diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/debug/DebugWaxedWeatheredCutCopperStairs.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/debug/DebugWaxedWeatheredCutCopperStairs.kt index c9d7576ad..aa3a17cb7 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/debug/DebugWaxedWeatheredCutCopperStairs.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/debug/DebugWaxedWeatheredCutCopperStairs.kt @@ -10,6 +10,7 @@ import io.github.pylonmc.pylon.core.item.PylonItem import io.github.pylonmc.pylon.core.item.base.PylonBlockInteractor import io.github.pylonmc.pylon.core.item.base.PylonItemEntityInteractor import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder +import io.github.pylonmc.pylon.core.item.builder.PylonItemStackBuilder import io.github.pylonmc.pylon.core.nms.NmsAccessor import io.github.pylonmc.pylon.core.util.pylonKey import io.papermc.paper.datacomponent.DataComponentTypes @@ -99,7 +100,7 @@ internal class DebugWaxedWeatheredCutCopperStairs(stack: ItemStack) companion object { val KEY = pylonKey("debug_waxed_weathered_cut_copper_stairs") - val STACK = ItemStackBuilder.pylonItem(Material.WAXED_WEATHERED_CUT_COPPER_STAIRS, KEY) + val STACK = PylonItemStackBuilder.of(Material.WAXED_WEATHERED_CUT_COPPER_STAIRS, KEY) .set(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, true) .build() } diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/guide/PylonGuide.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/guide/PylonGuide.kt index 94ffd9073..5a7222258 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/guide/PylonGuide.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/guide/PylonGuide.kt @@ -44,7 +44,7 @@ class PylonGuide(stack: ItemStack) : PylonItem(stack), PylonInteractor { val KEY = pylonKey("guide") @JvmField - val STACK = ItemStackBuilder.pylonItem(Material.BOOK, KEY) + val STACK = PylonItemStackBuilder.of(Material.BOOK, KEY) .set(DataComponentTypes.ITEM_MODEL, Key.key("knowledge_book")) .set(DataComponentTypes.MAX_STACK_SIZE, 1) .build() diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/PylonItem.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/PylonItem.kt index 0fe23e48d..13245aa2c 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/PylonItem.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/PylonItem.kt @@ -82,7 +82,7 @@ open class PylonItem(val stack: ItemStack) : Keyed { var isNameValid = true if (name == null || name.key() != ItemStackBuilder.nameKey(schema.key)) { - PylonCore.logger.warning("Item ${schema.key}'s name is not a translation key; check your item uses ItemStackBuilder.pylonItem(...)") + PylonCore.logger.warning("Item ${schema.key}'s name is not a translation key; check your item uses PylonItemStackBuilder.of(...)") isNameValid = false } diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt index 3b4291646..23f2d9554 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt @@ -2,7 +2,7 @@ package io.github.pylonmc.pylon.core.item.builder import io.github.pylonmc.pylon.core.datatypes.PylonSerializers import io.github.pylonmc.pylon.core.i18n.PylonTranslator.Companion.translate -import io.github.pylonmc.pylon.core.item.PylonItemSchema +import io.github.pylonmc.pylon.core.item.PylonItem import io.github.pylonmc.pylon.core.util.editData import io.github.pylonmc.pylon.core.util.editDataOrDefault import io.github.pylonmc.pylon.core.util.editDataOrSet @@ -40,13 +40,13 @@ import java.util.function.Consumer import kotlin.collections.forEach /** - * Helper class for creating an [ItemStack], including utilities for creating Pylon - * items specifically. + * Helper class for creating an [ItemStack] with various properties. Refer to + * [PylonItemStackBuilder] for utilities specifically for creating [PylonItem]s. * * Implements InvUI's [ItemProvider], so can be used instead of an [ItemStack] in GUIs. * * You should use this when using anything to do with [Component.translatable] including - * [io.github.pylonmc.pylon.core.item.PylonItem]s in InvUI GUIs. Yes, this is confusing + * [PylonItem]s in InvUI GUIs. Yes, this is confusing * and annoying - it is unfortunately necessary to get around InvUI's translation system. */ @Suppress("UnstableApiUsage") @@ -109,7 +109,7 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * Sets the item's name to the default language file key (for example * `pylon.pyloncore.item.my_dumb_item.name`), based on the item [key] given. * - * Use [pylonItem] instead of this to create a stack for a [io.github.pylonmc.pylon.core.item.PylonItem]. + * Use [PylonItemStackBuilder.of] instead of this to create a stack for a [PylonItem]. */ fun defaultTranslatableName(key: NamespacedKey) = name(Component.translatable(nameKey(key))) @@ -129,7 +129,7 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * Sets the item's lore to the default language file key (for example * `pylon.pyloncore.item.my_dumb_item.lore`), based on the item [key] given. * - * Use [pylonItem] instead of this to create a stack for a [io.github.pylonmc.pylon.core.item.PylonItem]. + * Use [PylonItemStackBuilder.of] instead of this to create a stack for a [PylonItem]. */ fun defaultTranslatableLore(key: NamespacedKey) = lore(Component.translatable(loreKey(key), "")) @@ -367,30 +367,6 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro return gui(ItemStack(material), key) } - /** - * Creates a new [ItemStack] for a [io.github.pylonmc.pylon.core.item.PylonItem] by setting - * the name and lore to the default translation keys, and setting the item's Pylon ID to the - * provided [key]. - */ - @JvmStatic - fun pylonItem(stack: ItemStack, key: NamespacedKey): ItemStackBuilder { - return ItemStackBuilder(stack) - .editPdc { it.set(PylonItemSchema.pylonItemKeyKey, PylonSerializers.NAMESPACED_KEY, key) } - .addCustomModelDataString(key.toString()) - .defaultTranslatableName(key) - .defaultTranslatableLore(key) - } - - /** - * Creates a new [ItemStack] for a [io.github.pylonmc.pylon.core.item.PylonItem] by setting - * the name and lore to the default translation keys, and setting the item's Pylon ID to the - * provided [key]. - */ - @JvmStatic - fun pylonItem(material: Material, key: NamespacedKey): ItemStackBuilder { - return pylonItem(ItemStack(material), key) - } - fun ItemAttributeModifiers.Builder.copy(modifiers: List?) : ItemAttributeModifiers.Builder { modifiers?.forEach { entry -> this.addModifier(entry.attribute(), entry.modifier(), entry.group, entry.display()) diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt index 661114a4e..2c1cc130b 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt @@ -3,38 +3,228 @@ package io.github.pylonmc.pylon.core.item.builder import io.github.pylonmc.pylon.core.config.Config import io.github.pylonmc.pylon.core.config.Settings import io.github.pylonmc.pylon.core.config.adapter.ConfigAdapter +import io.github.pylonmc.pylon.core.datatypes.PylonSerializers +import io.github.pylonmc.pylon.core.item.PylonItemSchema +import io.github.pylonmc.pylon.core.item.PylonItem import io.github.pylonmc.pylon.core.util.ToolType +import io.papermc.paper.datacomponent.DataComponentTypes import org.bukkit.Material import org.bukkit.NamespacedKey +import org.bukkit.attribute.Attribute import org.bukkit.inventory.EquipmentSlotGroup import org.bukkit.inventory.ItemStack +/** + * Utility class for creating [ItemStack]s for [PylonItem]s backed by + * [ItemStackBuilder]s. + */ object PylonItemStackBuilder { + /** + * Creates a new [ItemStack] for a [PylonItem] by setting + * the name and lore to the default translation keys, and setting the item's Pylon ID to the + * provided [key]. + */ + @JvmStatic + fun of(stack: ItemStack, key: NamespacedKey): ItemStackBuilder { + return ItemStackBuilder(stack) + .editPdc { it.set(PylonItemSchema.pylonItemKeyKey, PylonSerializers.NAMESPACED_KEY, key) } + .addCustomModelDataString(key.toString()) + .defaultTranslatableName(key) + .defaultTranslatableLore(key) + } + + /** + * Creates a new [ItemStack] for a [PylonItem] by setting + * the name and lore to the default translation keys, and setting the item's Pylon ID to the + * provided [key]. + */ + @JvmStatic + fun of(material: Material, key: NamespacedKey): ItemStackBuilder { + return of(ItemStack(material), key) + } + + /** + * Creates a new [ItemStack] for a [PylonItem] by setting + * the name and lore to the default translation keys, and setting the item's Pylon ID to the + * provided [key]. + * + * The provided [consumer] is called with the created [ItemStackBuilder] and + * the [Settings][Settings.get] for the item, allowing you to further customize the item + * based on its config. + */ + @JvmStatic + fun of(stack: ItemStack, key: NamespacedKey, consumer: (ItemStackBuilder, Config) -> Any): ItemStackBuilder { + val builder = of(stack, key) + val settings = Settings.get(key) + consumer(builder, settings) + return builder + } + + /** + * Creates a new [ItemStack] for a [PylonItem] by setting + * the name and lore to the default translation keys, and setting the item's Pylon ID to the + * provided [key]. + * + * The provided [consumer] is called with the created [ItemStackBuilder] and + * the [Settings][Settings.get] for the item, allowing you to further customize the item + * based on its config. + */ + @JvmStatic + fun of(material: Material, key: NamespacedKey, consumer: (ItemStackBuilder, Config) -> Any): ItemStackBuilder { + return of(ItemStack(material), key, consumer) + } + + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [helmet][EquipmentSlotGroup.HEAD] slot. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-helmet.yml` + * ```yml + * armor: 2.0 # double + * armor-toughness: 0.5 # double + * durability: 250 # integer + * ``` + */ @JvmStatic fun helmet(stack: ItemStack, key: NamespacedKey, durability: Boolean) = armor(stack, key, EquipmentSlotGroup.HEAD, durability) + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [helmet][EquipmentSlotGroup.HEAD] slot. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-helmet.yml` + * ```yml + * armor: 2.0 # double + * armor-toughness: 0.5 # double + * durability: 250 # integer + * ``` + */ @JvmStatic fun helmet(material: Material, key: NamespacedKey, durability: Boolean) = helmet(ItemStack(material), key, durability) + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [chestplate][EquipmentSlotGroup.CHEST] slot. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-chestplate.yml` + * ```yml + * armor: 6.0 # double + * armor-toughness: 1.0 # double + * durability: 400 # integer + * ``` + */ @JvmStatic fun chestPlate(stack: ItemStack, key: NamespacedKey, durability: Boolean) = armor(stack, key, EquipmentSlotGroup.CHEST, durability) + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [chestplate][EquipmentSlotGroup.CHEST] slot. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-chestplate.yml` + * ```yml + * armor: 6.0 # double + * armor-toughness: 1.0 # double + * durability: 400 # integer + * ``` + */ @JvmStatic fun chestPlate(material: Material, key: NamespacedKey, durability: Boolean) = chestPlate(ItemStack(material), key, durability) + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [leggings][EquipmentSlotGroup.LEGS] slot. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-leggings.yml` + * ```yml + * armor: 5.0 # double + * armor-toughness: 0.5 # double + * durability: 350 # integer + * ``` + */ @JvmStatic fun leggings(stack: ItemStack, key: NamespacedKey, durability: Boolean) = armor(stack, key, EquipmentSlotGroup.LEGS, durability) + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [leggings][EquipmentSlotGroup.LEGS] slot. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-leggings.yml` + * ```yml + * armor: 5.0 # double + * armor-toughness: 0.5 # double + * durability: 350 # integer + * ``` + */ @JvmStatic fun leggings(material: Material, key: NamespacedKey, durability: Boolean) = leggings(ItemStack(material), key, durability) + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [boots][EquipmentSlotGroup.FEET] slot. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-boots.yml` + * ```yml + * armor: 2.0 # double + * armor-toughness: 0.0 # double + * durability: 300 # integer + * ``` + */ @JvmStatic fun boots(stack: ItemStack, key: NamespacedKey, durability: Boolean) = armor(stack, key, EquipmentSlotGroup.FEET, durability) + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [boots][EquipmentSlotGroup.FEET] slot. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-boots.yml` + * ```yml + * armor: 2.0 # double + * armor-toughness: 0.0 # double + * durability: 300 # integer + * ``` + */ @JvmStatic fun boots(material: Material, key: NamespacedKey, durability: Boolean) = boots(ItemStack(material), key, durability) + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the specified [slot]. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-armor.yml` + * ```yml + * armor: 2.0 # double + * armor-toughness: 0.5 # double + * durability: 250 # integer + * ``` + */ @JvmStatic fun armor(stack: ItemStack, key: NamespacedKey, slot: EquipmentSlotGroup, durability: Boolean) = create(stack, key) { builder, settings -> builder.armor( @@ -45,13 +235,43 @@ object PylonItemStackBuilder { if (durability) { builder.durability(settings.getOrThrow("durability", ConfigAdapter.INT)) + } else { + builder.unset(DataComponentTypes.DAMAGE).unset(DataComponentTypes.MAX_DAMAGE) } } + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the specified [slot]. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-armor.yml` + * ```yml + * armor: 2.0 # double + * armor-toughness: 0.5 # double + * durability: 250 # integer + * ``` + */ @JvmStatic fun armor(material: Material, key: NamespacedKey, slot: EquipmentSlotGroup, durability: Boolean) = armor(ItemStack(material), key, slot, durability) + /** + * Creates a new [ItemStack] for a [PylonItem] that can be used as a tool of the specified [toolType]. + * You must provide a value for `mining-speed` and `mining-durability-damage` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-tool.yml` + * ```yml + * mining-speed: 8.0 # double + * mining-durability-damage: 1 # integer + * durability: 500 # integer + * ``` + */ @JvmStatic fun tool(stack: ItemStack, key: NamespacedKey, toolType: ToolType, durability: Boolean) = create(stack, key) { builder, settings -> builder.tool( @@ -62,27 +282,52 @@ object PylonItemStackBuilder { if (durability) { builder.durability(settings.getOrThrow("durability", ConfigAdapter.INT)) + } else { + builder.unset(DataComponentTypes.DAMAGE).unset(DataComponentTypes.MAX_DAMAGE) } } + /** + * Creates a new [ItemStack] for a [PylonItem] that can be used as a tool of the specified [toolType]. + * You must provide a value for `mining-speed` and `mining-durability-damage` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-tool.yml` + * ```yml + * mining-speed: 8.0 # double + * mining-durability-damage: 1 # integer + * durability: 500 # integer + * ``` + */ @JvmStatic fun tool(material: Material, key: NamespacedKey, toolType: ToolType, durability: Boolean) = tool(ItemStack(material), key, toolType, durability) - @JvmStatic - fun toolWeapon(stack: ItemStack, key: NamespacedKey, toolType: ToolType, durability: Boolean, knockback: Boolean, disablesShield: Boolean): ItemStackBuilder { - val settings = Settings.get(key) - return weapon(stack, key, durability, knockback, disablesShield).tool( - toolType.getTag(), - settings.getOrThrow("mining-speed", ConfigAdapter.FLOAT), - settings.getOrThrow("mining-durability-damage", ConfigAdapter.INT) - ) - } - - @JvmStatic - fun toolWeapon(material: Material, key: NamespacedKey, toolType: ToolType, durability: Boolean, knockback: Boolean, disablesShield: Boolean) - = toolWeapon(ItemStack(material), key, toolType, durability, knockback, disablesShield) - + /** + * Creates a new [ItemStack] for a [PylonItem] that can be used as a weapon. + * You must provide a value for `attack-damage`, `attack-speed` and `attack-durability-damage` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * If `knockback` is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. + * Otherwise, removes any existing attack knockback. + * + * If `disablesShield` is true, gives the item a shield disable time defined by `disable-shield-seconds` in the [Settings][Settings.get]. + * If false, the item will not disable shields when used in attacking. + * + * `settings/example-weapon.yml` + * ```yml + * attack-damage: 6.0 # double + * attack-speed: 1.6 # double + * attack-durability-damage: 2 # integer + * attack-knockback: 0.4 # double + * disable-shield-seconds: 3.0 # float + * durability: 500 # integer + * ``` + */ @JvmStatic fun weapon(stack: ItemStack, key: NamespacedKey, durability: Boolean, knockback: Boolean, disablesShield: Boolean) = create(stack, key) { builder, settings -> builder.weapon( @@ -94,19 +339,116 @@ object PylonItemStackBuilder { if (knockback) { builder.attackKnockback(settings.getOrThrow("attack-knockback", ConfigAdapter.DOUBLE)) + } else { + builder.removeAttributeModifiers(Attribute.ATTACK_KNOCKBACK) } if (durability) { builder.durability(settings.getOrThrow("durability", ConfigAdapter.INT)) + } else { + builder.unset(DataComponentTypes.DAMAGE).unset(DataComponentTypes.MAX_DAMAGE) } } + /** + * Creates a new [ItemStack] for a [PylonItem] that can be used as a weapon. + * You must provide a value for `attack-damage`, `attack-speed` and `attack-durability-damage` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * If `knockback` is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. + * Otherwise, removes any existing attack knockback. + * + * If `disablesShield` is true, gives the item a shield disable time defined by `disable-shield-seconds` in the [Settings][Settings.get]. + * If false, the item will not disable shields when used in attacking. + * + * `settings/example-weapon.yml` + * ```yml + * attack-damage: 6.0 # double + * attack-speed: 1.6 # double + * attack-durability-damage: 2 # integer + * attack-knockback: 0.4 # double + * disable-shield-seconds: 3.0 # float + * durability: 500 # integer + * ``` + */ @JvmStatic fun weapon(material: Material, key: NamespacedKey, durability: Boolean, knockback: Boolean, disablesShield: Boolean) - = weapon(ItemStack(material), key, durability, knockback, disablesShield) + = weapon(ItemStack(material), key, durability, knockback, disablesShield) + + /** + * Creates a new [ItemStack] for a [PylonItem] that can be used as a weapon and tool of the specified [toolType]. + * You must provide a value for `attack-damage`, `attack-speed`, `attack-durability-damage`, `mining-speed` and `mining-durability-damage` + * in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * If `knockback` is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. + * Otherwise, removes any existing attack knockback. + * + * If `disablesShield` is true, gives the item a shield disable time defined by `disable-shield-seconds` in the [Settings][Settings.get]. + * If false, the item will not disable shields when used in attacking. + * + * `settings/example-tool-weapon.yml` + * ```yml + * attack-damage: 6.0 # double + * attack-speed: 1.6 # double + * attack-durability-damage: 2 # integer + * attack-knockback: 0.4 # double + * disable-shield-seconds: 1.0 # float + * + * mining-speed: 8.0 # double + * mining-durability-damage: 1 # integer + * + * durability: 500 # integer + * ``` + */ + @JvmStatic + fun toolWeapon(stack: ItemStack, key: NamespacedKey, toolType: ToolType, durability: Boolean, knockback: Boolean, disablesShield: Boolean): ItemStackBuilder { + val settings = Settings.get(key) + return weapon(stack, key, durability, knockback, disablesShield).tool( + toolType.getTag(), + settings.getOrThrow("mining-speed", ConfigAdapter.FLOAT), + settings.getOrThrow("mining-durability-damage", ConfigAdapter.INT) + ) + } + + /** + * Creates a new [ItemStack] for a [PylonItem] that can be used as a weapon and tool of the specified [toolType]. + * You must provide a value for `attack-damage`, `attack-speed`, `attack-durability-damage`, `mining-speed` and `mining-durability-damage` + * in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * If `knockback` is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. + * Otherwise, removes any existing attack knockback. + * + * If `disablesShield` is true, gives the item a shield disable time defined by `disable-shield-seconds` in the [Settings][Settings.get]. + * If false, the item will not disable shields when used in attacking. + * + * `settings/example-tool-weapon.yml` + * ```yml + * attack-damage: 6.0 # double + * attack-speed: 1.6 # double + * attack-durability-damage: 2 # integer + * attack-knockback: 0.4 # double + * disable-shield-seconds: 1.0 # float + * + * mining-speed: 8.0 # double + * mining-durability-damage: 1 # integer + * + * durability: 500 # integer + * ``` + */ + @JvmStatic + fun toolWeapon(material: Material, key: NamespacedKey, toolType: ToolType, durability: Boolean, knockback: Boolean, disablesShield: Boolean) + = toolWeapon(ItemStack(material), key, toolType, durability, knockback, disablesShield) private fun create(stack: ItemStack, key: NamespacedKey, consumer: (ItemStackBuilder, Config) -> Unit): ItemStackBuilder { - val builder = ItemStackBuilder.pylonItem(stack, key) + val builder = of(stack, key) val settings = Settings.get(key) consumer(builder, settings) return builder diff --git a/test/src/main/java/io/github/pylonmc/pylon/test/item/Items.java b/test/src/main/java/io/github/pylonmc/pylon/test/item/Items.java index 19c66061c..59c47d9e3 100644 --- a/test/src/main/java/io/github/pylonmc/pylon/test/item/Items.java +++ b/test/src/main/java/io/github/pylonmc/pylon/test/item/Items.java @@ -15,7 +15,7 @@ public final class Items { private Items() {} public static final NamespacedKey STICKY_STICK_KEY = PylonTest.key("sticky_stick"); - public static final ItemStack STICKY_STICK_STACK = ItemStackBuilder.pylonItem(Material.STICK, STICKY_STICK_KEY) + public static final ItemStack STICKY_STICK_STACK = PylonItemStackBuilder.of(Material.STICK, STICKY_STICK_KEY) .set(DataComponentTypes.ITEM_NAME, Component.text("Sticky Stick")) .build(); diff --git a/test/src/main/java/io/github/pylonmc/pylon/test/item/OminousBlazePower.java b/test/src/main/java/io/github/pylonmc/pylon/test/item/OminousBlazePower.java index 896c4ea9f..3a9a77963 100644 --- a/test/src/main/java/io/github/pylonmc/pylon/test/item/OminousBlazePower.java +++ b/test/src/main/java/io/github/pylonmc/pylon/test/item/OminousBlazePower.java @@ -14,7 +14,7 @@ public class OminousBlazePower extends PylonItem implements PylonBrewingStandFuel { public static final NamespacedKey KEY = PylonTest.key("ominous_blaze_powder"); - public static final ItemStack STACK = ItemStackBuilder.pylonItem(Material.DIAMOND_SWORD, KEY) + public static final ItemStack STACK = PylonItemStackBuilder.of(Material.DIAMOND_SWORD, KEY) .name("OMINOUS BLAZE POWDER") .lore("<#ff0000>VERY SCARY") .lore("<#222222>OH NO") From 67aa0866fb515ce3a0c5294486d66cc7c5bb3d4b Mon Sep 17 00:00:00 2001 From: JustAHuman-xD Date: Tue, 30 Sep 2025 01:36:43 -0500 Subject: [PATCH 10/16] imports --- .../kotlin/io/github/pylonmc/pylon/core/block/PhantomBlock.kt | 2 +- .../io/github/pylonmc/pylon/core/content/guide/PylonGuide.kt | 2 +- .../io/github/pylonmc/pylon/test/item/OminousBlazePower.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PhantomBlock.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PhantomBlock.kt index 1c933f419..2cf86f1e1 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PhantomBlock.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PhantomBlock.kt @@ -6,7 +6,7 @@ import io.github.pylonmc.pylon.core.block.waila.WailaConfig import io.github.pylonmc.pylon.core.datatypes.PylonSerializers import io.github.pylonmc.pylon.core.i18n.PylonArgument import io.github.pylonmc.pylon.core.item.PylonItem -import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder +import io.github.pylonmc.pylon.core.item.builder.PylonItemStackBuilder import io.github.pylonmc.pylon.core.util.pylonKey import net.kyori.adventure.bossbar.BossBar import org.bukkit.Material diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/guide/PylonGuide.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/guide/PylonGuide.kt index 5a7222258..b23286424 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/guide/PylonGuide.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/guide/PylonGuide.kt @@ -11,7 +11,7 @@ import io.github.pylonmc.pylon.core.guide.pages.item.ItemIngredientsPage import io.github.pylonmc.pylon.core.guide.pages.research.ResearchesPage import io.github.pylonmc.pylon.core.item.PylonItem import io.github.pylonmc.pylon.core.item.base.PylonInteractor -import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder +import io.github.pylonmc.pylon.core.item.builder.PylonItemStackBuilder import io.github.pylonmc.pylon.core.util.pylonKey import io.papermc.paper.datacomponent.DataComponentTypes import net.kyori.adventure.key.Key diff --git a/test/src/main/java/io/github/pylonmc/pylon/test/item/OminousBlazePower.java b/test/src/main/java/io/github/pylonmc/pylon/test/item/OminousBlazePower.java index 3a9a77963..622e104d1 100644 --- a/test/src/main/java/io/github/pylonmc/pylon/test/item/OminousBlazePower.java +++ b/test/src/main/java/io/github/pylonmc/pylon/test/item/OminousBlazePower.java @@ -2,7 +2,7 @@ import io.github.pylonmc.pylon.core.item.PylonItem; import io.github.pylonmc.pylon.core.item.base.PylonBrewingStandFuel; -import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder; +import io.github.pylonmc.pylon.core.item.builder.PylonItemStackBuilder; import io.github.pylonmc.pylon.test.PylonTest; import org.bukkit.Material; import org.bukkit.NamespacedKey; From 580a49ae6e1ed6124e6203e2e9108871bb9e60e2 Mon Sep 17 00:00:00 2001 From: JustAHuman-xD Date: Tue, 30 Sep 2025 02:03:49 -0500 Subject: [PATCH 11/16] hide from tooltip method --- .../pylon/core/item/builder/ItemStackBuilder.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt index 23f2d9554..173f66b69 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt @@ -14,6 +14,7 @@ import io.papermc.paper.datacomponent.DataComponentTypes import io.papermc.paper.datacomponent.item.CustomModelData import io.papermc.paper.datacomponent.item.ItemAttributeModifiers import io.papermc.paper.datacomponent.item.ItemLore +import io.papermc.paper.datacomponent.item.TooltipDisplay import io.papermc.paper.datacomponent.item.Tool import io.papermc.paper.datacomponent.item.UseCooldown import io.papermc.paper.datacomponent.item.Weapon @@ -125,6 +126,15 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro fun lore(vararg lore: String) = lore(*lore.map(::fromMiniMessage).toTypedArray()) + fun hideFromTooltip(componentType: DataComponentType) = apply { + val tooltipDisplay = stack.getData(DataComponentTypes.TOOLTIP_DISPLAY) + val hidden = tooltipDisplay?.hiddenComponents()?.toMutableSet() ?: mutableSetOf() + hidden.add(componentType) + stack.setData(DataComponentTypes.TOOLTIP_DISPLAY, TooltipDisplay.tooltipDisplay() + .hideTooltip(tooltipDisplay?.hideTooltip() == true) + .hiddenComponents(hidden)) + } + /** * Sets the item's lore to the default language file key (for example * `pylon.pyloncore.item.my_dumb_item.lore`), based on the item [key] given. From 01caad22f27da7c8193ec2fcb4113da2d018d8d9 Mon Sep 17 00:00:00 2001 From: JustAHuman-xD Date: Tue, 30 Sep 2025 16:26:00 -0500 Subject: [PATCH 12/16] fix tests whoops --- .../src/main/java/io/github/pylonmc/pylon/test/item/Items.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/src/main/java/io/github/pylonmc/pylon/test/item/Items.java b/test/src/main/java/io/github/pylonmc/pylon/test/item/Items.java index 59c47d9e3..1656cca64 100644 --- a/test/src/main/java/io/github/pylonmc/pylon/test/item/Items.java +++ b/test/src/main/java/io/github/pylonmc/pylon/test/item/Items.java @@ -1,7 +1,7 @@ package io.github.pylonmc.pylon.test.item; import io.github.pylonmc.pylon.core.item.PylonItem; -import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder; +import io.github.pylonmc.pylon.core.item.builder.PylonItemStackBuilder; import io.github.pylonmc.pylon.test.PylonTest; import io.papermc.paper.datacomponent.DataComponentTypes; import net.kyori.adventure.text.Component; @@ -9,7 +9,6 @@ import org.bukkit.NamespacedKey; import org.bukkit.inventory.ItemStack; - public final class Items { private Items() {} From 014478e23e3b6035bf728d747c22c1528f2d1a35 Mon Sep 17 00:00:00 2001 From: JustAHuman-xD Date: Tue, 30 Sep 2025 17:32:52 -0500 Subject: [PATCH 13/16] doc --- .../kotlin/io/github/pylonmc/pylon/core/fluid/PylonFluid.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/fluid/PylonFluid.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/fluid/PylonFluid.kt index 266e9e012..bb51a7af8 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/fluid/PylonFluid.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/fluid/PylonFluid.kt @@ -99,6 +99,10 @@ open class PylonFluid( companion object { val pylonFluidKeyKey = pylonKey("pylon_fluid_key") + /** + * Get the fluid represented by the given item stack, or null if the stack is null, empty or does not represent a fluid. + * See [item] for how to get an item stack that represents this fluid. + */ fun fromStack(stack: ItemStack?): PylonFluid? { if (stack == null || stack.isEmpty) return null val id = stack.persistentDataContainer.get(pylonFluidKeyKey, PylonSerializers.NAMESPACED_KEY) ?: return null From d7e518c20ee2ed2d643cf9ab3509d713711ea067 Mon Sep 17 00:00:00 2001 From: JustAHuman-xD Date: Sat, 4 Oct 2025 16:09:35 -0500 Subject: [PATCH 14/16] goodbye PylonItemStackBuilder again --- .../pylonmc/pylon/core/block/PhantomBlock.kt | 4 +- .../DebugWaxedWeatheredCutCopperStairs.kt | 3 +- .../pylon/core/content/guide/PylonGuide.kt | 4 +- .../core/item/builder/ItemStackBuilder.kt | 465 +++++++++++++++++- .../item/builder/PylonItemStackBuilder.kt | 456 ----------------- .../github/pylonmc/pylon/test/item/Items.java | 4 +- .../pylon/test/item/OminousBlazePower.java | 4 +- 7 files changed, 469 insertions(+), 471 deletions(-) delete mode 100644 pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PhantomBlock.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PhantomBlock.kt index 2cf86f1e1..71f0030d3 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PhantomBlock.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PhantomBlock.kt @@ -6,7 +6,7 @@ import io.github.pylonmc.pylon.core.block.waila.WailaConfig import io.github.pylonmc.pylon.core.datatypes.PylonSerializers import io.github.pylonmc.pylon.core.i18n.PylonArgument import io.github.pylonmc.pylon.core.item.PylonItem -import io.github.pylonmc.pylon.core.item.builder.PylonItemStackBuilder +import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder import io.github.pylonmc.pylon.core.util.pylonKey import net.kyori.adventure.bossbar.BossBar import org.bukkit.Material @@ -85,7 +85,7 @@ class PhantomBlock( companion object { val KEY = pylonKey("error_item") val BLOCK_KEY = pylonKey("block") - val STACK = PylonItemStackBuilder.of(Material.BARRIER, KEY) + val STACK = ItemStackBuilder.pylon(Material.BARRIER, KEY) .build() } } diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/debug/DebugWaxedWeatheredCutCopperStairs.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/debug/DebugWaxedWeatheredCutCopperStairs.kt index aa3a17cb7..49f744b9e 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/debug/DebugWaxedWeatheredCutCopperStairs.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/debug/DebugWaxedWeatheredCutCopperStairs.kt @@ -10,7 +10,6 @@ import io.github.pylonmc.pylon.core.item.PylonItem import io.github.pylonmc.pylon.core.item.base.PylonBlockInteractor import io.github.pylonmc.pylon.core.item.base.PylonItemEntityInteractor import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder -import io.github.pylonmc.pylon.core.item.builder.PylonItemStackBuilder import io.github.pylonmc.pylon.core.nms.NmsAccessor import io.github.pylonmc.pylon.core.util.pylonKey import io.papermc.paper.datacomponent.DataComponentTypes @@ -100,7 +99,7 @@ internal class DebugWaxedWeatheredCutCopperStairs(stack: ItemStack) companion object { val KEY = pylonKey("debug_waxed_weathered_cut_copper_stairs") - val STACK = PylonItemStackBuilder.of(Material.WAXED_WEATHERED_CUT_COPPER_STAIRS, KEY) + val STACK = ItemStackBuilder.pylon(Material.WAXED_WEATHERED_CUT_COPPER_STAIRS, KEY) .set(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, true) .build() } diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/guide/PylonGuide.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/guide/PylonGuide.kt index b23286424..4dfda8e41 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/guide/PylonGuide.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/guide/PylonGuide.kt @@ -11,7 +11,7 @@ import io.github.pylonmc.pylon.core.guide.pages.item.ItemIngredientsPage import io.github.pylonmc.pylon.core.guide.pages.research.ResearchesPage import io.github.pylonmc.pylon.core.item.PylonItem import io.github.pylonmc.pylon.core.item.base.PylonInteractor -import io.github.pylonmc.pylon.core.item.builder.PylonItemStackBuilder +import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder import io.github.pylonmc.pylon.core.util.pylonKey import io.papermc.paper.datacomponent.DataComponentTypes import net.kyori.adventure.key.Key @@ -44,7 +44,7 @@ class PylonGuide(stack: ItemStack) : PylonItem(stack), PylonInteractor { val KEY = pylonKey("guide") @JvmField - val STACK = PylonItemStackBuilder.of(Material.BOOK, KEY) + val STACK = ItemStackBuilder.pylon(Material.BOOK, KEY) .set(DataComponentTypes.ITEM_MODEL, Key.key("knowledge_book")) .set(DataComponentTypes.MAX_STACK_SIZE, 1) .build() diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt index 173f66b69..b1138b5a5 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt @@ -1,8 +1,13 @@ package io.github.pylonmc.pylon.core.item.builder +import io.github.pylonmc.pylon.core.config.Config +import io.github.pylonmc.pylon.core.config.Settings +import io.github.pylonmc.pylon.core.config.adapter.ConfigAdapter import io.github.pylonmc.pylon.core.datatypes.PylonSerializers import io.github.pylonmc.pylon.core.i18n.PylonTranslator.Companion.translate import io.github.pylonmc.pylon.core.item.PylonItem +import io.github.pylonmc.pylon.core.item.PylonItemSchema +import io.github.pylonmc.pylon.core.util.ToolType import io.github.pylonmc.pylon.core.util.editData import io.github.pylonmc.pylon.core.util.editDataOrDefault import io.github.pylonmc.pylon.core.util.editDataOrSet @@ -41,8 +46,8 @@ import java.util.function.Consumer import kotlin.collections.forEach /** - * Helper class for creating an [ItemStack] with various properties. Refer to - * [PylonItemStackBuilder] for utilities specifically for creating [PylonItem]s. + * Helper class for creating an [ItemStack] with various properties. Includes + * methods for creating [PylonItem] stacks, and gui items. * * Implements InvUI's [ItemProvider], so can be used instead of an [ItemStack] in GUIs. * @@ -110,7 +115,7 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * Sets the item's name to the default language file key (for example * `pylon.pyloncore.item.my_dumb_item.name`), based on the item [key] given. * - * Use [PylonItemStackBuilder.of] instead of this to create a stack for a [PylonItem]. + * Use [pylon] instead of this to create a stack for a [PylonItem]. */ fun defaultTranslatableName(key: NamespacedKey) = name(Component.translatable(nameKey(key))) @@ -139,7 +144,7 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * Sets the item's lore to the default language file key (for example * `pylon.pyloncore.item.my_dumb_item.lore`), based on the item [key] given. * - * Use [PylonItemStackBuilder.of] instead of this to create a stack for a [PylonItem]. + * Use [pylon] instead of this to create a stack for a [PylonItem]. */ fun defaultTranslatableLore(key: NamespacedKey) = lore(Component.translatable(loreKey(key), "")) @@ -220,7 +225,7 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro armorToughness: Double ) = armor(EquipmentSlotGroup.HEAD, armor, armorToughness) - fun chestPlate( + fun chestplate( armor: Double, armorToughness: Double ) = armor(EquipmentSlotGroup.CHEST, armor, armorToughness) @@ -355,6 +360,10 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro return of(ItemStack(material)) } + /** + * Creates a new [ItemStack] for a GUI item, sets its pdc key and adds + * a custom model data string for resource packs. + */ @JvmStatic fun gui(stack: ItemStack, key: String): ItemStackBuilder { return ItemStackBuilder(stack) @@ -362,26 +371,472 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro .addCustomModelDataString(key.toString()) } + /** + * Creates a new [ItemStack] for a GUI item, sets its pdc key and adds + * a custom model data string for resource packs. + */ @JvmStatic fun gui(material: Material, key: String): ItemStackBuilder { return gui(ItemStack(material), key) } + /** + * Creates a new [ItemStack] for a GUI item, sets its pdc key and adds + * a custom model data string for resource packs. + */ @JvmStatic fun gui(stack: ItemStack, key: NamespacedKey): ItemStackBuilder { return gui(stack, key.toString()) } + /** + * Creates a new [ItemStack] for a GUI item, sets its pdc key and adds + * a custom model data string for resource packs. + */ @JvmStatic fun gui(material: Material, key: NamespacedKey): ItemStackBuilder { return gui(ItemStack(material), key) } + /** + * Creates a new [ItemStack] for a [PylonItem] by setting + * the name and lore to the default translation keys, and setting the item's Pylon ID to the + * provided [key]. + */ + @JvmStatic + fun pylon(stack: ItemStack, key: NamespacedKey): ItemStackBuilder { + return ItemStackBuilder(stack) + .editPdc { it.set(PylonItemSchema.pylonItemKeyKey, PylonSerializers.NAMESPACED_KEY, key) } + .addCustomModelDataString(key.toString()) + .defaultTranslatableName(key) + .defaultTranslatableLore(key) + } + + /** + * Creates a new [ItemStack] for a [PylonItem] by setting + * the name and lore to the default translation keys, and setting the item's Pylon ID to the + * provided [key]. + */ + @JvmStatic + fun pylon(material: Material, key: NamespacedKey): ItemStackBuilder { + return pylon(ItemStack(material), key) + } + + /** + * Creates a new [ItemStack] for a [PylonItem] by setting + * the name and lore to the default translation keys, and setting the item's Pylon ID to the + * provided [key]. + * + * The provided [consumer] is called with the created [ItemStackBuilder] and + * the [Settings][Settings.get] for the item, allowing you to further customize the item + * based on its config. + */ + @JvmStatic + fun pylon(stack: ItemStack, key: NamespacedKey, consumer: (ItemStackBuilder, Config) -> Any): ItemStackBuilder { + val builder = pylon(stack, key) + val settings = Settings.get(key) + consumer(builder, settings) + return builder + } + + /** + * Creates a new [ItemStack] for a [PylonItem] by setting + * the name and lore to the default translation keys, and setting the item's Pylon ID to the + * provided [key]. + * + * The provided [consumer] is called with the created [ItemStackBuilder] and + * the [Settings][Settings.get] for the item, allowing you to further customize the item + * based on its config. + */ + @JvmStatic + fun pylon(material: Material, key: NamespacedKey, consumer: (ItemStackBuilder, Config) -> Any): ItemStackBuilder { + return pylon(ItemStack(material), key, consumer) + } + + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [helmet][EquipmentSlotGroup.HEAD] slot. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-helmet.yml` + * ```yml + * armor: 2.0 # double + * armor-toughness: 0.5 # double + * durability: 250 # integer + * ``` + */ + @JvmStatic + fun pylonHelmet(stack: ItemStack, key: NamespacedKey, durability: Boolean) = pylonArmor(stack, key, EquipmentSlotGroup.HEAD, durability) + + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [helmet][EquipmentSlotGroup.HEAD] slot. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-helmet.yml` + * ```yml + * armor: 2.0 # double + * armor-toughness: 0.5 # double + * durability: 250 # integer + * ``` + */ + @JvmStatic + fun pylonHelmet(material: Material, key: NamespacedKey, durability: Boolean) = pylonHelmet(ItemStack(material), key, durability) + + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [chestplate][EquipmentSlotGroup.CHEST] slot. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-chestplate.yml` + * ```yml + * armor: 6.0 # double + * armor-toughness: 1.0 # double + * durability: 400 # integer + * ``` + */ + @JvmStatic + fun pylonChestplate(stack: ItemStack, key: NamespacedKey, durability: Boolean) = pylonArmor(stack, key, EquipmentSlotGroup.CHEST, durability) + + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [chestplate][EquipmentSlotGroup.CHEST] slot. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-chestplate.yml` + * ```yml + * armor: 6.0 # double + * armor-toughness: 1.0 # double + * durability: 400 # integer + * ``` + */ + @JvmStatic + fun pylonChestplate(material: Material, key: NamespacedKey, durability: Boolean) = pylonChestplate(ItemStack(material), key, durability) + + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [leggings][EquipmentSlotGroup.LEGS] slot. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-leggings.yml` + * ```yml + * armor: 5.0 # double + * armor-toughness: 0.5 # double + * durability: 350 # integer + * ``` + */ + @JvmStatic + fun pylonLeggings(stack: ItemStack, key: NamespacedKey, durability: Boolean) = pylonArmor(stack, key, EquipmentSlotGroup.LEGS, durability) + + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [leggings][EquipmentSlotGroup.LEGS] slot. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-leggings.yml` + * ```yml + * armor: 5.0 # double + * armor-toughness: 0.5 # double + * durability: 350 # integer + * ``` + */ + @JvmStatic + fun pylonLeggings(material: Material, key: NamespacedKey, durability: Boolean) = pylonLeggings(ItemStack(material), key, durability) + + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [boots][EquipmentSlotGroup.FEET] slot. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-boots.yml` + * ```yml + * armor: 2.0 # double + * armor-toughness: 0.0 # double + * durability: 300 # integer + * ``` + */ + @JvmStatic + fun pylonBoots(stack: ItemStack, key: NamespacedKey, durability: Boolean) = pylonArmor(stack, key, EquipmentSlotGroup.FEET, durability) + + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [boots][EquipmentSlotGroup.FEET] slot. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-boots.yml` + * ```yml + * armor: 2.0 # double + * armor-toughness: 0.0 # double + * durability: 300 # integer + * ``` + */ + @JvmStatic + fun pylonBoots(material: Material, key: NamespacedKey, durability: Boolean) = pylonBoots(ItemStack(material), key, durability) + + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the specified [slot]. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-armor.yml` + * ```yml + * armor: 2.0 # double + * armor-toughness: 0.5 # double + * durability: 250 # integer + * ``` + */ + @JvmStatic + fun pylonArmor(stack: ItemStack, key: NamespacedKey, slot: EquipmentSlotGroup, durability: Boolean) = create(stack, key) { builder, settings -> + builder.armor( + slot, + settings.getOrThrow("armor", ConfigAdapter.DOUBLE), + settings.getOrThrow("armor-toughness", ConfigAdapter.DOUBLE) + ) + + if (durability) { + builder.durability(settings.getOrThrow("durability", ConfigAdapter.INT)) + } else { + builder.unset(DataComponentTypes.DAMAGE).unset(DataComponentTypes.MAX_DAMAGE) + } + } + + /** + * Creates a new [ItemStack] for a [PylonItem] that can be worn in the specified [slot]. + * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-armor.yml` + * ```yml + * armor: 2.0 # double + * armor-toughness: 0.5 # double + * durability: 250 # integer + * ``` + */ + @JvmStatic + fun pylonArmor(material: Material, key: NamespacedKey, slot: EquipmentSlotGroup, durability: Boolean) + = pylonArmor(ItemStack(material), key, slot, durability) + + /** + * Creates a new [ItemStack] for a [PylonItem] that can be used as a tool of the specified [toolType]. + * You must provide a value for `mining-speed` and `mining-durability-damage` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-tool.yml` + * ```yml + * mining-speed: 8.0 # double + * mining-durability-damage: 1 # integer + * durability: 500 # integer + * ``` + */ + @JvmStatic + fun pylonTool(stack: ItemStack, key: NamespacedKey, toolType: ToolType, durability: Boolean) = create(stack, key) { builder, settings -> + builder.tool( + toolType.getTag(), + settings.getOrThrow("mining-speed", ConfigAdapter.FLOAT), + settings.getOrThrow("mining-durability-damage", ConfigAdapter.INT) + ) + + if (durability) { + builder.durability(settings.getOrThrow("durability", ConfigAdapter.INT)) + } else { + builder.unset(DataComponentTypes.DAMAGE).unset(DataComponentTypes.MAX_DAMAGE) + } + } + + /** + * Creates a new [ItemStack] for a [PylonItem] that can be used as a tool of the specified [toolType]. + * You must provide a value for `mining-speed` and `mining-durability-damage` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * `settings/example-tool.yml` + * ```yml + * mining-speed: 8.0 # double + * mining-durability-damage: 1 # integer + * durability: 500 # integer + * ``` + */ + @JvmStatic + fun pylonTool(material: Material, key: NamespacedKey, toolType: ToolType, durability: Boolean) + = pylonTool(ItemStack(material), key, toolType, durability) + + /** + * Creates a new [ItemStack] for a [PylonItem] that can be used as a weapon. + * You must provide a value for `attack-damage`, `attack-speed` and `attack-durability-damage` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * If `knockback` is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. + * Otherwise, removes any existing attack knockback. + * + * If `disablesShield` is true, gives the item a shield disable time defined by `disable-shield-seconds` in the [Settings][Settings.get]. + * If false, the item will not disable shields when used in attacking. + * + * `settings/example-weapon.yml` + * ```yml + * attack-damage: 6.0 # double + * attack-speed: 1.6 # double + * attack-durability-damage: 2 # integer + * attack-knockback: 0.4 # double + * disable-shield-seconds: 3.0 # float + * durability: 500 # integer + * ``` + */ + @JvmStatic + fun pylonWeapon(stack: ItemStack, key: NamespacedKey, durability: Boolean, knockback: Boolean, disablesShield: Boolean) = create(stack, key) { builder, settings -> + builder.weapon( + settings.getOrThrow("attack-damage", ConfigAdapter.DOUBLE), + settings.getOrThrow("attack-speed", ConfigAdapter.DOUBLE), + settings.getOrThrow("attack-durability-damage", ConfigAdapter.INT), + if (disablesShield) settings.getOrThrow("disable-shield-seconds", ConfigAdapter.FLOAT) else 0f + ) + + if (knockback) { + builder.attackKnockback(settings.getOrThrow("attack-knockback", ConfigAdapter.DOUBLE)) + } else { + builder.removeAttributeModifiers(Attribute.ATTACK_KNOCKBACK) + } + + if (durability) { + builder.durability(settings.getOrThrow("durability", ConfigAdapter.INT)) + } else { + builder.unset(DataComponentTypes.DAMAGE).unset(DataComponentTypes.MAX_DAMAGE) + } + } + + /** + * Creates a new [ItemStack] for a [PylonItem] that can be used as a weapon. + * You must provide a value for `attack-damage`, `attack-speed` and `attack-durability-damage` in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * If `knockback` is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. + * Otherwise, removes any existing attack knockback. + * + * If `disablesShield` is true, gives the item a shield disable time defined by `disable-shield-seconds` in the [Settings][Settings.get]. + * If false, the item will not disable shields when used in attacking. + * + * `settings/example-weapon.yml` + * ```yml + * attack-damage: 6.0 # double + * attack-speed: 1.6 # double + * attack-durability-damage: 2 # integer + * attack-knockback: 0.4 # double + * disable-shield-seconds: 3.0 # float + * durability: 500 # integer + * ``` + */ + @JvmStatic + fun pylonWeapon(material: Material, key: NamespacedKey, durability: Boolean, knockback: Boolean, disablesShield: Boolean) + = pylonWeapon(ItemStack(material), key, durability, knockback, disablesShield) + + /** + * Creates a new [ItemStack] for a [PylonItem] that can be used as a weapon and tool of the specified [toolType]. + * You must provide a value for `attack-damage`, `attack-speed`, `attack-durability-damage`, `mining-speed` and `mining-durability-damage` + * in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * If `knockback` is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. + * Otherwise, removes any existing attack knockback. + * + * If `disablesShield` is true, gives the item a shield disable time defined by `disable-shield-seconds` in the [Settings][Settings.get]. + * If false, the item will not disable shields when used in attacking. + * + * `settings/example-tool-weapon.yml` + * ```yml + * attack-damage: 6.0 # double + * attack-speed: 1.6 # double + * attack-durability-damage: 2 # integer + * attack-knockback: 0.4 # double + * disable-shield-seconds: 1.0 # float + * + * mining-speed: 8.0 # double + * mining-durability-damage: 1 # integer + * + * durability: 500 # integer + * ``` + */ + @JvmStatic + fun pylonToolWeapon(stack: ItemStack, key: NamespacedKey, toolType: ToolType, durability: Boolean, knockback: Boolean, disablesShield: Boolean): ItemStackBuilder { + val settings = Settings.get(key) + return pylonWeapon(stack, key, durability, knockback, disablesShield).tool( + toolType.getTag(), + settings.getOrThrow("mining-speed", ConfigAdapter.FLOAT), + settings.getOrThrow("mining-durability-damage", ConfigAdapter.INT) + ) + } + + /** + * Creates a new [ItemStack] for a [PylonItem] that can be used as a weapon and tool of the specified [toolType]. + * You must provide a value for `attack-damage`, `attack-speed`, `attack-durability-damage`, `mining-speed` and `mining-durability-damage` + * in the [Settings][Settings.get] for the item. + * + * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * Otherwise, removes any existing durability. + * + * If `knockback` is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. + * Otherwise, removes any existing attack knockback. + * + * If `disablesShield` is true, gives the item a shield disable time defined by `disable-shield-seconds` in the [Settings][Settings.get]. + * If false, the item will not disable shields when used in attacking. + * + * `settings/example-tool-weapon.yml` + * ```yml + * attack-damage: 6.0 # double + * attack-speed: 1.6 # double + * attack-durability-damage: 2 # integer + * attack-knockback: 0.4 # double + * disable-shield-seconds: 1.0 # float + * + * mining-speed: 8.0 # double + * mining-durability-damage: 1 # integer + * + * durability: 500 # integer + * ``` + */ + @JvmStatic + fun pylonToolWeapon(material: Material, key: NamespacedKey, toolType: ToolType, durability: Boolean, knockback: Boolean, disablesShield: Boolean) + = pylonToolWeapon(ItemStack(material), key, toolType, durability, knockback, disablesShield) + fun ItemAttributeModifiers.Builder.copy(modifiers: List?) : ItemAttributeModifiers.Builder { modifiers?.forEach { entry -> this.addModifier(entry.attribute(), entry.modifier(), entry.group, entry.display()) } return this } + + private fun create(stack: ItemStack, key: NamespacedKey, consumer: (ItemStackBuilder, Config) -> Unit): ItemStackBuilder { + val builder = pylon(stack, key) + val settings = Settings.get(key) + consumer(builder, settings) + return builder + } } } diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt deleted file mode 100644 index 2c1cc130b..000000000 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/PylonItemStackBuilder.kt +++ /dev/null @@ -1,456 +0,0 @@ -package io.github.pylonmc.pylon.core.item.builder - -import io.github.pylonmc.pylon.core.config.Config -import io.github.pylonmc.pylon.core.config.Settings -import io.github.pylonmc.pylon.core.config.adapter.ConfigAdapter -import io.github.pylonmc.pylon.core.datatypes.PylonSerializers -import io.github.pylonmc.pylon.core.item.PylonItemSchema -import io.github.pylonmc.pylon.core.item.PylonItem -import io.github.pylonmc.pylon.core.util.ToolType -import io.papermc.paper.datacomponent.DataComponentTypes -import org.bukkit.Material -import org.bukkit.NamespacedKey -import org.bukkit.attribute.Attribute -import org.bukkit.inventory.EquipmentSlotGroup -import org.bukkit.inventory.ItemStack - -/** - * Utility class for creating [ItemStack]s for [PylonItem]s backed by - * [ItemStackBuilder]s. - */ -object PylonItemStackBuilder { - - /** - * Creates a new [ItemStack] for a [PylonItem] by setting - * the name and lore to the default translation keys, and setting the item's Pylon ID to the - * provided [key]. - */ - @JvmStatic - fun of(stack: ItemStack, key: NamespacedKey): ItemStackBuilder { - return ItemStackBuilder(stack) - .editPdc { it.set(PylonItemSchema.pylonItemKeyKey, PylonSerializers.NAMESPACED_KEY, key) } - .addCustomModelDataString(key.toString()) - .defaultTranslatableName(key) - .defaultTranslatableLore(key) - } - - /** - * Creates a new [ItemStack] for a [PylonItem] by setting - * the name and lore to the default translation keys, and setting the item's Pylon ID to the - * provided [key]. - */ - @JvmStatic - fun of(material: Material, key: NamespacedKey): ItemStackBuilder { - return of(ItemStack(material), key) - } - - /** - * Creates a new [ItemStack] for a [PylonItem] by setting - * the name and lore to the default translation keys, and setting the item's Pylon ID to the - * provided [key]. - * - * The provided [consumer] is called with the created [ItemStackBuilder] and - * the [Settings][Settings.get] for the item, allowing you to further customize the item - * based on its config. - */ - @JvmStatic - fun of(stack: ItemStack, key: NamespacedKey, consumer: (ItemStackBuilder, Config) -> Any): ItemStackBuilder { - val builder = of(stack, key) - val settings = Settings.get(key) - consumer(builder, settings) - return builder - } - - /** - * Creates a new [ItemStack] for a [PylonItem] by setting - * the name and lore to the default translation keys, and setting the item's Pylon ID to the - * provided [key]. - * - * The provided [consumer] is called with the created [ItemStackBuilder] and - * the [Settings][Settings.get] for the item, allowing you to further customize the item - * based on its config. - */ - @JvmStatic - fun of(material: Material, key: NamespacedKey, consumer: (ItemStackBuilder, Config) -> Any): ItemStackBuilder { - return of(ItemStack(material), key, consumer) - } - - /** - * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [helmet][EquipmentSlotGroup.HEAD] slot. - * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. - * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. - * Otherwise, removes any existing durability. - * - * `settings/example-helmet.yml` - * ```yml - * armor: 2.0 # double - * armor-toughness: 0.5 # double - * durability: 250 # integer - * ``` - */ - @JvmStatic - fun helmet(stack: ItemStack, key: NamespacedKey, durability: Boolean) = armor(stack, key, EquipmentSlotGroup.HEAD, durability) - - /** - * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [helmet][EquipmentSlotGroup.HEAD] slot. - * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. - * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. - * Otherwise, removes any existing durability. - * - * `settings/example-helmet.yml` - * ```yml - * armor: 2.0 # double - * armor-toughness: 0.5 # double - * durability: 250 # integer - * ``` - */ - @JvmStatic - fun helmet(material: Material, key: NamespacedKey, durability: Boolean) = helmet(ItemStack(material), key, durability) - - /** - * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [chestplate][EquipmentSlotGroup.CHEST] slot. - * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. - * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. - * Otherwise, removes any existing durability. - * - * `settings/example-chestplate.yml` - * ```yml - * armor: 6.0 # double - * armor-toughness: 1.0 # double - * durability: 400 # integer - * ``` - */ - @JvmStatic - fun chestPlate(stack: ItemStack, key: NamespacedKey, durability: Boolean) = armor(stack, key, EquipmentSlotGroup.CHEST, durability) - - /** - * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [chestplate][EquipmentSlotGroup.CHEST] slot. - * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. - * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. - * Otherwise, removes any existing durability. - * - * `settings/example-chestplate.yml` - * ```yml - * armor: 6.0 # double - * armor-toughness: 1.0 # double - * durability: 400 # integer - * ``` - */ - @JvmStatic - fun chestPlate(material: Material, key: NamespacedKey, durability: Boolean) = chestPlate(ItemStack(material), key, durability) - - /** - * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [leggings][EquipmentSlotGroup.LEGS] slot. - * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. - * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. - * Otherwise, removes any existing durability. - * - * `settings/example-leggings.yml` - * ```yml - * armor: 5.0 # double - * armor-toughness: 0.5 # double - * durability: 350 # integer - * ``` - */ - @JvmStatic - fun leggings(stack: ItemStack, key: NamespacedKey, durability: Boolean) = armor(stack, key, EquipmentSlotGroup.LEGS, durability) - - /** - * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [leggings][EquipmentSlotGroup.LEGS] slot. - * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. - * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. - * Otherwise, removes any existing durability. - * - * `settings/example-leggings.yml` - * ```yml - * armor: 5.0 # double - * armor-toughness: 0.5 # double - * durability: 350 # integer - * ``` - */ - @JvmStatic - fun leggings(material: Material, key: NamespacedKey, durability: Boolean) = leggings(ItemStack(material), key, durability) - - /** - * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [boots][EquipmentSlotGroup.FEET] slot. - * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. - * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. - * Otherwise, removes any existing durability. - * - * `settings/example-boots.yml` - * ```yml - * armor: 2.0 # double - * armor-toughness: 0.0 # double - * durability: 300 # integer - * ``` - */ - @JvmStatic - fun boots(stack: ItemStack, key: NamespacedKey, durability: Boolean) = armor(stack, key, EquipmentSlotGroup.FEET, durability) - - /** - * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [boots][EquipmentSlotGroup.FEET] slot. - * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. - * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. - * Otherwise, removes any existing durability. - * - * `settings/example-boots.yml` - * ```yml - * armor: 2.0 # double - * armor-toughness: 0.0 # double - * durability: 300 # integer - * ``` - */ - @JvmStatic - fun boots(material: Material, key: NamespacedKey, durability: Boolean) = boots(ItemStack(material), key, durability) - - /** - * Creates a new [ItemStack] for a [PylonItem] that can be worn in the specified [slot]. - * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. - * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. - * Otherwise, removes any existing durability. - * - * `settings/example-armor.yml` - * ```yml - * armor: 2.0 # double - * armor-toughness: 0.5 # double - * durability: 250 # integer - * ``` - */ - @JvmStatic - fun armor(stack: ItemStack, key: NamespacedKey, slot: EquipmentSlotGroup, durability: Boolean) = create(stack, key) { builder, settings -> - builder.armor( - slot, - settings.getOrThrow("armor", ConfigAdapter.DOUBLE), - settings.getOrThrow("armor-toughness", ConfigAdapter.DOUBLE) - ) - - if (durability) { - builder.durability(settings.getOrThrow("durability", ConfigAdapter.INT)) - } else { - builder.unset(DataComponentTypes.DAMAGE).unset(DataComponentTypes.MAX_DAMAGE) - } - } - - /** - * Creates a new [ItemStack] for a [PylonItem] that can be worn in the specified [slot]. - * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. - * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. - * Otherwise, removes any existing durability. - * - * `settings/example-armor.yml` - * ```yml - * armor: 2.0 # double - * armor-toughness: 0.5 # double - * durability: 250 # integer - * ``` - */ - @JvmStatic - fun armor(material: Material, key: NamespacedKey, slot: EquipmentSlotGroup, durability: Boolean) - = armor(ItemStack(material), key, slot, durability) - - /** - * Creates a new [ItemStack] for a [PylonItem] that can be used as a tool of the specified [toolType]. - * You must provide a value for `mining-speed` and `mining-durability-damage` in the [Settings][Settings.get] for the item. - * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. - * Otherwise, removes any existing durability. - * - * `settings/example-tool.yml` - * ```yml - * mining-speed: 8.0 # double - * mining-durability-damage: 1 # integer - * durability: 500 # integer - * ``` - */ - @JvmStatic - fun tool(stack: ItemStack, key: NamespacedKey, toolType: ToolType, durability: Boolean) = create(stack, key) { builder, settings -> - builder.tool( - toolType.getTag(), - settings.getOrThrow("mining-speed", ConfigAdapter.FLOAT), - settings.getOrThrow("mining-durability-damage", ConfigAdapter.INT) - ) - - if (durability) { - builder.durability(settings.getOrThrow("durability", ConfigAdapter.INT)) - } else { - builder.unset(DataComponentTypes.DAMAGE).unset(DataComponentTypes.MAX_DAMAGE) - } - } - - /** - * Creates a new [ItemStack] for a [PylonItem] that can be used as a tool of the specified [toolType]. - * You must provide a value for `mining-speed` and `mining-durability-damage` in the [Settings][Settings.get] for the item. - * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. - * Otherwise, removes any existing durability. - * - * `settings/example-tool.yml` - * ```yml - * mining-speed: 8.0 # double - * mining-durability-damage: 1 # integer - * durability: 500 # integer - * ``` - */ - @JvmStatic - fun tool(material: Material, key: NamespacedKey, toolType: ToolType, durability: Boolean) - = tool(ItemStack(material), key, toolType, durability) - - /** - * Creates a new [ItemStack] for a [PylonItem] that can be used as a weapon. - * You must provide a value for `attack-damage`, `attack-speed` and `attack-durability-damage` in the [Settings][Settings.get] for the item. - * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. - * Otherwise, removes any existing durability. - * - * If `knockback` is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. - * Otherwise, removes any existing attack knockback. - * - * If `disablesShield` is true, gives the item a shield disable time defined by `disable-shield-seconds` in the [Settings][Settings.get]. - * If false, the item will not disable shields when used in attacking. - * - * `settings/example-weapon.yml` - * ```yml - * attack-damage: 6.0 # double - * attack-speed: 1.6 # double - * attack-durability-damage: 2 # integer - * attack-knockback: 0.4 # double - * disable-shield-seconds: 3.0 # float - * durability: 500 # integer - * ``` - */ - @JvmStatic - fun weapon(stack: ItemStack, key: NamespacedKey, durability: Boolean, knockback: Boolean, disablesShield: Boolean) = create(stack, key) { builder, settings -> - builder.weapon( - settings.getOrThrow("attack-damage", ConfigAdapter.DOUBLE), - settings.getOrThrow("attack-speed", ConfigAdapter.DOUBLE), - settings.getOrThrow("attack-durability-damage", ConfigAdapter.INT), - if (disablesShield) settings.getOrThrow("disable-shield-seconds", ConfigAdapter.FLOAT) else 0f - ) - - if (knockback) { - builder.attackKnockback(settings.getOrThrow("attack-knockback", ConfigAdapter.DOUBLE)) - } else { - builder.removeAttributeModifiers(Attribute.ATTACK_KNOCKBACK) - } - - if (durability) { - builder.durability(settings.getOrThrow("durability", ConfigAdapter.INT)) - } else { - builder.unset(DataComponentTypes.DAMAGE).unset(DataComponentTypes.MAX_DAMAGE) - } - } - - /** - * Creates a new [ItemStack] for a [PylonItem] that can be used as a weapon. - * You must provide a value for `attack-damage`, `attack-speed` and `attack-durability-damage` in the [Settings][Settings.get] for the item. - * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. - * Otherwise, removes any existing durability. - * - * If `knockback` is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. - * Otherwise, removes any existing attack knockback. - * - * If `disablesShield` is true, gives the item a shield disable time defined by `disable-shield-seconds` in the [Settings][Settings.get]. - * If false, the item will not disable shields when used in attacking. - * - * `settings/example-weapon.yml` - * ```yml - * attack-damage: 6.0 # double - * attack-speed: 1.6 # double - * attack-durability-damage: 2 # integer - * attack-knockback: 0.4 # double - * disable-shield-seconds: 3.0 # float - * durability: 500 # integer - * ``` - */ - @JvmStatic - fun weapon(material: Material, key: NamespacedKey, durability: Boolean, knockback: Boolean, disablesShield: Boolean) - = weapon(ItemStack(material), key, durability, knockback, disablesShield) - - /** - * Creates a new [ItemStack] for a [PylonItem] that can be used as a weapon and tool of the specified [toolType]. - * You must provide a value for `attack-damage`, `attack-speed`, `attack-durability-damage`, `mining-speed` and `mining-durability-damage` - * in the [Settings][Settings.get] for the item. - * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. - * Otherwise, removes any existing durability. - * - * If `knockback` is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. - * Otherwise, removes any existing attack knockback. - * - * If `disablesShield` is true, gives the item a shield disable time defined by `disable-shield-seconds` in the [Settings][Settings.get]. - * If false, the item will not disable shields when used in attacking. - * - * `settings/example-tool-weapon.yml` - * ```yml - * attack-damage: 6.0 # double - * attack-speed: 1.6 # double - * attack-durability-damage: 2 # integer - * attack-knockback: 0.4 # double - * disable-shield-seconds: 1.0 # float - * - * mining-speed: 8.0 # double - * mining-durability-damage: 1 # integer - * - * durability: 500 # integer - * ``` - */ - @JvmStatic - fun toolWeapon(stack: ItemStack, key: NamespacedKey, toolType: ToolType, durability: Boolean, knockback: Boolean, disablesShield: Boolean): ItemStackBuilder { - val settings = Settings.get(key) - return weapon(stack, key, durability, knockback, disablesShield).tool( - toolType.getTag(), - settings.getOrThrow("mining-speed", ConfigAdapter.FLOAT), - settings.getOrThrow("mining-durability-damage", ConfigAdapter.INT) - ) - } - - /** - * Creates a new [ItemStack] for a [PylonItem] that can be used as a weapon and tool of the specified [toolType]. - * You must provide a value for `attack-damage`, `attack-speed`, `attack-durability-damage`, `mining-speed` and `mining-durability-damage` - * in the [Settings][Settings.get] for the item. - * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. - * Otherwise, removes any existing durability. - * - * If `knockback` is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. - * Otherwise, removes any existing attack knockback. - * - * If `disablesShield` is true, gives the item a shield disable time defined by `disable-shield-seconds` in the [Settings][Settings.get]. - * If false, the item will not disable shields when used in attacking. - * - * `settings/example-tool-weapon.yml` - * ```yml - * attack-damage: 6.0 # double - * attack-speed: 1.6 # double - * attack-durability-damage: 2 # integer - * attack-knockback: 0.4 # double - * disable-shield-seconds: 1.0 # float - * - * mining-speed: 8.0 # double - * mining-durability-damage: 1 # integer - * - * durability: 500 # integer - * ``` - */ - @JvmStatic - fun toolWeapon(material: Material, key: NamespacedKey, toolType: ToolType, durability: Boolean, knockback: Boolean, disablesShield: Boolean) - = toolWeapon(ItemStack(material), key, toolType, durability, knockback, disablesShield) - - private fun create(stack: ItemStack, key: NamespacedKey, consumer: (ItemStackBuilder, Config) -> Unit): ItemStackBuilder { - val builder = of(stack, key) - val settings = Settings.get(key) - consumer(builder, settings) - return builder - } -} \ No newline at end of file diff --git a/test/src/main/java/io/github/pylonmc/pylon/test/item/Items.java b/test/src/main/java/io/github/pylonmc/pylon/test/item/Items.java index 1656cca64..da367c859 100644 --- a/test/src/main/java/io/github/pylonmc/pylon/test/item/Items.java +++ b/test/src/main/java/io/github/pylonmc/pylon/test/item/Items.java @@ -1,7 +1,7 @@ package io.github.pylonmc.pylon.test.item; import io.github.pylonmc.pylon.core.item.PylonItem; -import io.github.pylonmc.pylon.core.item.builder.PylonItemStackBuilder; +import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder; import io.github.pylonmc.pylon.test.PylonTest; import io.papermc.paper.datacomponent.DataComponentTypes; import net.kyori.adventure.text.Component; @@ -14,7 +14,7 @@ public final class Items { private Items() {} public static final NamespacedKey STICKY_STICK_KEY = PylonTest.key("sticky_stick"); - public static final ItemStack STICKY_STICK_STACK = PylonItemStackBuilder.of(Material.STICK, STICKY_STICK_KEY) + public static final ItemStack STICKY_STICK_STACK = ItemStackBuilder.pylon(Material.STICK, STICKY_STICK_KEY) .set(DataComponentTypes.ITEM_NAME, Component.text("Sticky Stick")) .build(); diff --git a/test/src/main/java/io/github/pylonmc/pylon/test/item/OminousBlazePower.java b/test/src/main/java/io/github/pylonmc/pylon/test/item/OminousBlazePower.java index 622e104d1..0fef400fb 100644 --- a/test/src/main/java/io/github/pylonmc/pylon/test/item/OminousBlazePower.java +++ b/test/src/main/java/io/github/pylonmc/pylon/test/item/OminousBlazePower.java @@ -2,7 +2,7 @@ import io.github.pylonmc.pylon.core.item.PylonItem; import io.github.pylonmc.pylon.core.item.base.PylonBrewingStandFuel; -import io.github.pylonmc.pylon.core.item.builder.PylonItemStackBuilder; +import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder; import io.github.pylonmc.pylon.test.PylonTest; import org.bukkit.Material; import org.bukkit.NamespacedKey; @@ -14,7 +14,7 @@ public class OminousBlazePower extends PylonItem implements PylonBrewingStandFuel { public static final NamespacedKey KEY = PylonTest.key("ominous_blaze_powder"); - public static final ItemStack STACK = PylonItemStackBuilder.of(Material.DIAMOND_SWORD, KEY) + public static final ItemStack STACK = ItemStackBuilder.pylon(Material.DIAMOND_SWORD, KEY) .name("OMINOUS BLAZE POWDER") .lore("<#ff0000>VERY SCARY") .lore("<#222222>OH NO") From 7ca5bfe4ef45ba4da227a8da50e13c0ae38e11a3 Mon Sep 17 00:00:00 2001 From: JustAHuman-xD Date: Sat, 11 Oct 2025 21:47:34 -0500 Subject: [PATCH 15/16] ohm feedback --- .../core/item/builder/ItemStackBuilder.kt | 124 +++++++++--------- .../pylonmc/pylon/core/util/PylonUtils.kt | 7 + .../pylonmc/pylon/core/util/ToolType.kt | 16 --- 3 files changed, 68 insertions(+), 79 deletions(-) delete mode 100644 pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/ToolType.kt diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt index 643c003a4..04f882f5f 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt @@ -7,12 +7,12 @@ import io.github.pylonmc.pylon.core.datatypes.PylonSerializers import io.github.pylonmc.pylon.core.i18n.PylonTranslator.Companion.translate import io.github.pylonmc.pylon.core.item.PylonItem import io.github.pylonmc.pylon.core.item.PylonItemSchema -import io.github.pylonmc.pylon.core.util.ToolType import io.github.pylonmc.pylon.core.util.editData import io.github.pylonmc.pylon.core.util.editDataOrDefault import io.github.pylonmc.pylon.core.util.editDataOrSet import io.github.pylonmc.pylon.core.util.fromMiniMessage import io.github.pylonmc.pylon.core.util.gui.GuiItems +import io.github.pylonmc.pylon.core.util.pickaxeMineable import io.papermc.paper.datacomponent.DataComponentBuilder import io.papermc.paper.datacomponent.DataComponentType import io.papermc.paper.datacomponent.DataComponentTypes @@ -304,7 +304,12 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro addAttributeModifier(Attribute.ATTACK_KNOCKBACK, AttributeModifier(baseAttackKnockback, knockback, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlotGroup.MAINHAND)) } - fun durability(durability: Int) = set(DataComponentTypes.MAX_DAMAGE, durability) + fun durability(durability: Int) = durability(durability, 0) + + fun durability(durability: Int, damage: Int) = apply { + set(DataComponentTypes.MAX_DAMAGE, durability) + set(DataComponentTypes.DAMAGE, damage) + } fun useCooldown(cooldownTicks: Int, cooldownGroup: Key?) = set(DataComponentTypes.USE_COOLDOWN, UseCooldown.useCooldown(cooldownTicks / 20.0f).cooldownGroup(cooldownGroup)) @@ -459,7 +464,7 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [helmet][EquipmentSlotGroup.HEAD] slot. * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * If [hasDurability] is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. * Otherwise, removes any existing durability. * * `settings/example-helmet.yml` @@ -470,13 +475,13 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * ``` */ @JvmStatic - fun pylonHelmet(stack: ItemStack, key: NamespacedKey, durability: Boolean) = pylonArmor(stack, key, EquipmentSlotGroup.HEAD, durability) + fun pylonHelmet(stack: ItemStack, key: NamespacedKey, hasDurability: Boolean) = pylonArmor(stack, key, EquipmentSlotGroup.HEAD, hasDurability) /** * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [helmet][EquipmentSlotGroup.HEAD] slot. * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * If [hasDurability] is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. * Otherwise, removes any existing durability. * * `settings/example-helmet.yml` @@ -487,13 +492,13 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * ``` */ @JvmStatic - fun pylonHelmet(material: Material, key: NamespacedKey, durability: Boolean) = pylonHelmet(ItemStack(material), key, durability) + fun pylonHelmet(material: Material, key: NamespacedKey, hasDurability: Boolean) = pylonHelmet(ItemStack(material), key, hasDurability) /** * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [chestplate][EquipmentSlotGroup.CHEST] slot. * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * If [hasDurability] is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. * Otherwise, removes any existing durability. * * `settings/example-chestplate.yml` @@ -504,13 +509,13 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * ``` */ @JvmStatic - fun pylonChestplate(stack: ItemStack, key: NamespacedKey, durability: Boolean) = pylonArmor(stack, key, EquipmentSlotGroup.CHEST, durability) + fun pylonChestplate(stack: ItemStack, key: NamespacedKey, hasDurability: Boolean) = pylonArmor(stack, key, EquipmentSlotGroup.CHEST, hasDurability) /** * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [chestplate][EquipmentSlotGroup.CHEST] slot. * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * If [hasDurability] is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. * Otherwise, removes any existing durability. * * `settings/example-chestplate.yml` @@ -521,13 +526,13 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * ``` */ @JvmStatic - fun pylonChestplate(material: Material, key: NamespacedKey, durability: Boolean) = pylonChestplate(ItemStack(material), key, durability) + fun pylonChestplate(material: Material, key: NamespacedKey, hasDurability: Boolean) = pylonChestplate(ItemStack(material), key, hasDurability) /** * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [leggings][EquipmentSlotGroup.LEGS] slot. * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * If [hasDurability] is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. * Otherwise, removes any existing durability. * * `settings/example-leggings.yml` @@ -538,13 +543,13 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * ``` */ @JvmStatic - fun pylonLeggings(stack: ItemStack, key: NamespacedKey, durability: Boolean) = pylonArmor(stack, key, EquipmentSlotGroup.LEGS, durability) + fun pylonLeggings(stack: ItemStack, key: NamespacedKey, hasDurability: Boolean) = pylonArmor(stack, key, EquipmentSlotGroup.LEGS, hasDurability) /** * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [leggings][EquipmentSlotGroup.LEGS] slot. * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * If [hasDurability] is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. * Otherwise, removes any existing durability. * * `settings/example-leggings.yml` @@ -555,13 +560,13 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * ``` */ @JvmStatic - fun pylonLeggings(material: Material, key: NamespacedKey, durability: Boolean) = pylonLeggings(ItemStack(material), key, durability) + fun pylonLeggings(material: Material, key: NamespacedKey, hasDurability: Boolean) = pylonLeggings(ItemStack(material), key, hasDurability) /** * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [boots][EquipmentSlotGroup.FEET] slot. * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * If [hasDurability] is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. * Otherwise, removes any existing durability. * * `settings/example-boots.yml` @@ -572,13 +577,13 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * ``` */ @JvmStatic - fun pylonBoots(stack: ItemStack, key: NamespacedKey, durability: Boolean) = pylonArmor(stack, key, EquipmentSlotGroup.FEET, durability) + fun pylonBoots(stack: ItemStack, key: NamespacedKey, hasDurability: Boolean) = pylonArmor(stack, key, EquipmentSlotGroup.FEET, hasDurability) /** * Creates a new [ItemStack] for a [PylonItem] that can be worn in the [boots][EquipmentSlotGroup.FEET] slot. * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * If [hasDurability] is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. * Otherwise, removes any existing durability. * * `settings/example-boots.yml` @@ -589,13 +594,13 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * ``` */ @JvmStatic - fun pylonBoots(material: Material, key: NamespacedKey, durability: Boolean) = pylonBoots(ItemStack(material), key, durability) + fun pylonBoots(material: Material, key: NamespacedKey, hasDurability: Boolean) = pylonBoots(ItemStack(material), key, hasDurability) /** * Creates a new [ItemStack] for a [PylonItem] that can be worn in the specified [slot]. * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * If [hasDurability] is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. * Otherwise, removes any existing durability. * * `settings/example-armor.yml` @@ -606,14 +611,14 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * ``` */ @JvmStatic - fun pylonArmor(stack: ItemStack, key: NamespacedKey, slot: EquipmentSlotGroup, durability: Boolean) = create(stack, key) { builder, settings -> + fun pylonArmor(stack: ItemStack, key: NamespacedKey, slot: EquipmentSlotGroup, hasDurability: Boolean) = pylon(stack, key) { builder, settings -> builder.armor( slot, settings.getOrThrow("armor", ConfigAdapter.DOUBLE), settings.getOrThrow("armor-toughness", ConfigAdapter.DOUBLE) ) - if (durability) { + if (hasDurability) { builder.durability(settings.getOrThrow("durability", ConfigAdapter.INT)) } else { builder.unset(DataComponentTypes.DAMAGE).unset(DataComponentTypes.MAX_DAMAGE) @@ -624,7 +629,7 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * Creates a new [ItemStack] for a [PylonItem] that can be worn in the specified [slot]. * You must provide a value for `armor` and `armor-toughness` in the [Settings][Settings.get] for the item. * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * If [hasDurability] is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. * Otherwise, removes any existing durability. * * `settings/example-armor.yml` @@ -635,14 +640,14 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * ``` */ @JvmStatic - fun pylonArmor(material: Material, key: NamespacedKey, slot: EquipmentSlotGroup, durability: Boolean) - = pylonArmor(ItemStack(material), key, slot, durability) + fun pylonArmor(material: Material, key: NamespacedKey, slot: EquipmentSlotGroup, hasDurability: Boolean) + = pylonArmor(ItemStack(material), key, slot, hasDurability) /** - * Creates a new [ItemStack] for a [PylonItem] that can be used as a tool of the specified [toolType]. - * You must provide a value for `mining-speed` and `mining-durability-damage` in the [Settings][Settings.get] for the item. + * Creates a new [ItemStack] for a [PylonItem] that can be used as a tool for the [mineable] blocks. (See [pickaxeMineable] + * and related for basic tools) You must provide a value for `mining-speed` and `mining-durability-damage` in the [Settings][Settings.get] for the item. * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * If [hasDurability] is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. * Otherwise, removes any existing durability. * * `settings/example-tool.yml` @@ -653,14 +658,14 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * ``` */ @JvmStatic - fun pylonTool(stack: ItemStack, key: NamespacedKey, toolType: ToolType, durability: Boolean) = create(stack, key) { builder, settings -> + fun pylonTool(stack: ItemStack, key: NamespacedKey, mineable: RegistryKeySet, hasDurability: Boolean) = pylon(stack, key) { builder, settings -> builder.tool( - toolType.getTag(), + mineable, settings.getOrThrow("mining-speed", ConfigAdapter.FLOAT), settings.getOrThrow("mining-durability-damage", ConfigAdapter.INT) ) - if (durability) { + if (hasDurability) { builder.durability(settings.getOrThrow("durability", ConfigAdapter.INT)) } else { builder.unset(DataComponentTypes.DAMAGE).unset(DataComponentTypes.MAX_DAMAGE) @@ -668,10 +673,10 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro } /** - * Creates a new [ItemStack] for a [PylonItem] that can be used as a tool of the specified [toolType]. - * You must provide a value for `mining-speed` and `mining-durability-damage` in the [Settings][Settings.get] for the item. + * Creates a new [ItemStack] for a [PylonItem] that can be used as a tool for the [mineable] blocks. (See [pickaxeMineable] + * and related for basic tools) You must provide a value for `mining-speed` and `mining-durability-damage` in the [Settings][Settings.get] for the item. * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * If [hasDurability] is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. * Otherwise, removes any existing durability. * * `settings/example-tool.yml` @@ -682,17 +687,17 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * ``` */ @JvmStatic - fun pylonTool(material: Material, key: NamespacedKey, toolType: ToolType, durability: Boolean) - = pylonTool(ItemStack(material), key, toolType, durability) + fun pylonTool(material: Material, key: NamespacedKey, mineable: RegistryKeySet, hasDurability: Boolean) + = pylonTool(ItemStack(material), key, mineable, hasDurability) /** * Creates a new [ItemStack] for a [PylonItem] that can be used as a weapon. * You must provide a value for `attack-damage`, `attack-speed` and `attack-durability-damage` in the [Settings][Settings.get] for the item. * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * If [hasDurability] is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. * Otherwise, removes any existing durability. * - * If `knockback` is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. + * If [hasKnockback] is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. * Otherwise, removes any existing attack knockback. * * If `disablesShield` is true, gives the item a shield disable time defined by `disable-shield-seconds` in the [Settings][Settings.get]. @@ -709,7 +714,7 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * ``` */ @JvmStatic - fun pylonWeapon(stack: ItemStack, key: NamespacedKey, durability: Boolean, knockback: Boolean, disablesShield: Boolean) = create(stack, key) { builder, settings -> + fun pylonWeapon(stack: ItemStack, key: NamespacedKey, hasDurability: Boolean, hasKnockback: Boolean, disablesShield: Boolean) = pylon(stack, key) { builder, settings -> builder.weapon( settings.getOrThrow("attack-damage", ConfigAdapter.DOUBLE), settings.getOrThrow("attack-speed", ConfigAdapter.DOUBLE), @@ -717,13 +722,13 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro if (disablesShield) settings.getOrThrow("disable-shield-seconds", ConfigAdapter.FLOAT) else 0f ) - if (knockback) { + if (hasKnockback) { builder.attackKnockback(settings.getOrThrow("attack-knockback", ConfigAdapter.DOUBLE)) } else { builder.removeAttributeModifiers(Attribute.ATTACK_KNOCKBACK) } - if (durability) { + if (hasDurability) { builder.durability(settings.getOrThrow("durability", ConfigAdapter.INT)) } else { builder.unset(DataComponentTypes.DAMAGE).unset(DataComponentTypes.MAX_DAMAGE) @@ -734,10 +739,10 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * Creates a new [ItemStack] for a [PylonItem] that can be used as a weapon. * You must provide a value for `attack-damage`, `attack-speed` and `attack-durability-damage` in the [Settings][Settings.get] for the item. * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * If [hasDurability] is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. * Otherwise, removes any existing durability. * - * If `knockback` is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. + * If [hasKnockback] is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. * Otherwise, removes any existing attack knockback. * * If `disablesShield` is true, gives the item a shield disable time defined by `disable-shield-seconds` in the [Settings][Settings.get]. @@ -754,21 +759,21 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * ``` */ @JvmStatic - fun pylonWeapon(material: Material, key: NamespacedKey, durability: Boolean, knockback: Boolean, disablesShield: Boolean) - = pylonWeapon(ItemStack(material), key, durability, knockback, disablesShield) + fun pylonWeapon(material: Material, key: NamespacedKey, hasDurability: Boolean, hasKnockback: Boolean, disablesShield: Boolean) + = pylonWeapon(ItemStack(material), key, hasDurability, hasKnockback, disablesShield) /** - * Creates a new [ItemStack] for a [PylonItem] that can be used as a weapon and tool of the specified [toolType]. + * Creates a new [ItemStack] for a [PylonItem] that can be used as a weapon and tool for the [mineable] blocks. (See [pickaxeMineable] and related for basic tools) * You must provide a value for `attack-damage`, `attack-speed`, `attack-durability-damage`, `mining-speed` and `mining-durability-damage` * in the [Settings][Settings.get] for the item. * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * If [hasDurability] is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. * Otherwise, removes any existing durability. * - * If `knockback` is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. + * If [hasDurability] is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. * Otherwise, removes any existing attack knockback. * - * If `disablesShield` is true, gives the item a shield disable time defined by `disable-shield-seconds` in the [Settings][Settings.get]. + * If [hasKnockback] is true, gives the item a shield disable time defined by `disable-shield-seconds` in the [Settings][Settings.get]. * If false, the item will not disable shields when used in attacking. * * `settings/example-tool-weapon.yml` @@ -786,24 +791,24 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * ``` */ @JvmStatic - fun pylonToolWeapon(stack: ItemStack, key: NamespacedKey, toolType: ToolType, durability: Boolean, knockback: Boolean, disablesShield: Boolean): ItemStackBuilder { + fun pylonToolWeapon(stack: ItemStack, key: NamespacedKey, mineable: RegistryKeySet, hasDurability: Boolean, hasKnockback: Boolean, disablesShield: Boolean): ItemStackBuilder { val settings = Settings.get(key) - return pylonWeapon(stack, key, durability, knockback, disablesShield).tool( - toolType.getTag(), + return pylonWeapon(stack, key, hasDurability, hasKnockback, disablesShield).tool( + mineable, settings.getOrThrow("mining-speed", ConfigAdapter.FLOAT), settings.getOrThrow("mining-durability-damage", ConfigAdapter.INT) ) } /** - * Creates a new [ItemStack] for a [PylonItem] that can be used as a weapon and tool of the specified [toolType]. + * Creates a new [ItemStack] for a [PylonItem] that can be used as a weapon and tool for the [mineable] blocks. (See [pickaxeMineable] and related for basic tools) * You must provide a value for `attack-damage`, `attack-speed`, `attack-durability-damage`, `mining-speed` and `mining-durability-damage` * in the [Settings][Settings.get] for the item. * - * If `durability` is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. + * If [hasDurability] is true, gives the item a max durability defined by `durability` in the [Settings][Settings.get]. * Otherwise, removes any existing durability. * - * If `knockback` is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. + * If [hasKnockback] is true, gives the item knockback defined by `attack-knockback` in the [Settings][Settings.get]. * Otherwise, removes any existing attack knockback. * * If `disablesShield` is true, gives the item a shield disable time defined by `disable-shield-seconds` in the [Settings][Settings.get]. @@ -824,8 +829,8 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro * ``` */ @JvmStatic - fun pylonToolWeapon(material: Material, key: NamespacedKey, toolType: ToolType, durability: Boolean, knockback: Boolean, disablesShield: Boolean) - = pylonToolWeapon(ItemStack(material), key, toolType, durability, knockback, disablesShield) + fun pylonToolWeapon(material: Material, key: NamespacedKey, mineable: RegistryKeySet, hasDurability: Boolean, hasKnockback: Boolean, disablesShield: Boolean) + = pylonToolWeapon(ItemStack(material), key, mineable, hasDurability, hasKnockback, disablesShield) fun ItemAttributeModifiers.Builder.copy(modifiers: List?) : ItemAttributeModifiers.Builder { modifiers?.forEach { entry -> @@ -833,12 +838,5 @@ open class ItemStackBuilder internal constructor(val stack: ItemStack) : ItemPro } return this } - - private fun create(stack: ItemStack, key: NamespacedKey, consumer: (ItemStackBuilder, Config) -> Unit): ItemStackBuilder { - val builder = pylon(stack, key) - val settings = Settings.get(key) - consumer(builder, settings) - return builder - } } } diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/PylonUtils.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/PylonUtils.kt index 389d2a323..5ef784533 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/PylonUtils.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/PylonUtils.kt @@ -10,6 +10,7 @@ import io.github.pylonmc.pylon.core.entity.display.transform.TransformUtil.yawTo import io.github.pylonmc.pylon.core.item.PylonItem import io.github.pylonmc.pylon.core.registry.PylonRegistry import io.papermc.paper.datacomponent.DataComponentType +import io.papermc.paper.registry.keys.tags.BlockTypeTagKeys import net.kyori.adventure.text.Component import net.kyori.adventure.text.TranslatableComponent import net.kyori.adventure.text.TranslationArgumentLike @@ -17,6 +18,7 @@ import net.kyori.adventure.text.minimessage.MiniMessage import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer import net.kyori.adventure.translation.GlobalTranslator import org.bukkit.NamespacedKey +import org.bukkit.Registry import org.bukkit.block.Block import org.bukkit.block.BlockFace import org.bukkit.configuration.file.YamlConfiguration @@ -418,3 +420,8 @@ fun ItemStack.vanillaDisplayName(): Component val Component.plainText: String get() = PlainTextComponentSerializer.plainText().serialize(this) + +fun pickaxeMineable() = Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_PICKAXE) +fun axeMineable() = Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_AXE) +fun shovelMineable() = Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_SHOVEL) +fun hoeMineable() = Registry.BLOCK.getTag(BlockTypeTagKeys.MINEABLE_HOE) \ No newline at end of file diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/ToolType.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/ToolType.kt deleted file mode 100644 index 4a0234a77..000000000 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/util/ToolType.kt +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.pylonmc.pylon.core.util - -import io.papermc.paper.registry.keys.tags.BlockTypeTagKeys -import io.papermc.paper.registry.tag.TagKey -import org.bukkit.Registry -import org.bukkit.block.BlockType - -@Suppress("UnstableApiUsage") -enum class ToolType(val tagKey: TagKey) { - AXE(BlockTypeTagKeys.MINEABLE_AXE), - PICKAXE(BlockTypeTagKeys.MINEABLE_PICKAXE), - SHOVEL(BlockTypeTagKeys.MINEABLE_SHOVEL), - HOE(BlockTypeTagKeys.MINEABLE_HOE); - - fun getTag() = Registry.BLOCK.getTag(tagKey) -} \ No newline at end of file From 2078fcd47b00f262fc7e3fe1cb09a3fe55e38b01 Mon Sep 17 00:00:00 2001 From: JustAHuman-xD Date: Sat, 11 Oct 2025 21:50:33 -0500 Subject: [PATCH 16/16] Update ItemStackBuilder.kt --- .../github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt index b996d7b07..77631e74c 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/builder/ItemStackBuilder.kt @@ -4,10 +4,8 @@ import io.github.pylonmc.pylon.core.config.Config import io.github.pylonmc.pylon.core.config.Settings import io.github.pylonmc.pylon.core.config.adapter.ConfigAdapter import io.github.pylonmc.pylon.core.datatypes.PylonSerializers -import io.github.pylonmc.pylon.core.i18n.PylonTranslator.Companion.translate import io.github.pylonmc.pylon.core.item.PylonItem import io.github.pylonmc.pylon.core.item.PylonItemSchema -import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder.Companion.pylonItem import io.github.pylonmc.pylon.core.util.editData import io.github.pylonmc.pylon.core.util.editDataOrDefault import io.github.pylonmc.pylon.core.util.editDataOrSet @@ -31,7 +29,6 @@ import net.kyori.adventure.key.Key import net.kyori.adventure.text.Component import net.kyori.adventure.text.ComponentLike import net.kyori.adventure.util.TriState -import org.apache.commons.lang3.LocaleUtils import org.bukkit.Color import org.bukkit.Material import org.bukkit.NamespacedKey