diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/BreakSoundAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/BreakSoundAdapter.java new file mode 100644 index 0000000000..0c7c878676 --- /dev/null +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/BreakSoundAdapter.java @@ -0,0 +1,35 @@ +package com.denizenscript.denizen.paper.datacomponents; + +import com.denizenscript.denizen.utilities.Utilities; +import com.denizenscript.denizencore.objects.Mechanism; +import com.denizenscript.denizencore.objects.core.ElementTag; +import io.papermc.paper.datacomponent.DataComponentTypes; +import net.kyori.adventure.key.Key; + +public class BreakSoundAdapter extends DataComponentAdapter.Valued { + + // <--[property] + // @object ItemTag + // @name break_sound + // @input ElementTag + // @description + // Controls an item's break sound <@link language Item Components> in namespaced key format. + // The default namespace is "minecraft", so for example an input of "block.anvil.land" becomes "minecraft:block.anvil.land". + // @mechanism + // Provide no input to reset the item to its default value. + // --> + + public BreakSoundAdapter() { + super(ElementTag.class, DataComponentTypes.BREAK_SOUND, "break_sound"); + } + + @Override + public ElementTag toDenizen(Key value) { + return new ElementTag(value.asMinimalString(), true); + } + + @Override + public Key fromDenizen(ElementTag value, Mechanism mechanism) { + return Utilities.parseNamespacedKey(value.asString()); + } +} diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/ComponentAdaptersRegistry.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/ComponentAdaptersRegistry.java index 2029b783e1..7276989c57 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/ComponentAdaptersRegistry.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/ComponentAdaptersRegistry.java @@ -9,5 +9,9 @@ public static void register() { DataComponentAdapter.register(new MaxDurabilityAdapter()); DataComponentAdapter.register(new MaxStackSizeAdapter()); DataComponentAdapter.register(new RarityAdapter()); + DataComponentAdapter.register(new WeaponAdapter()); + DataComponentAdapter.register(new BreakSoundAdapter()); + DataComponentAdapter.register(new SwingAnimationAdapter()); + DataComponentAdapter.register(new EquippableAdapter()); } } diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/EquippableAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/EquippableAdapter.java new file mode 100644 index 0000000000..a476f16ebb --- /dev/null +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/EquippableAdapter.java @@ -0,0 +1,103 @@ +package com.denizenscript.denizen.paper.datacomponents; + +import com.denizenscript.denizen.utilities.Utilities; +import com.denizenscript.denizencore.objects.Mechanism; +import com.denizenscript.denizencore.objects.core.ElementTag; +import com.denizenscript.denizencore.objects.core.ListTag; +import com.denizenscript.denizencore.objects.core.MapTag; +import io.papermc.paper.datacomponent.DataComponentTypes; +import io.papermc.paper.datacomponent.item.Equippable; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.TypedKey; +import io.papermc.paper.registry.set.RegistrySet; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.EquipmentSlot; + +import java.util.ArrayList; +import java.util.List; + +public class EquippableAdapter extends DataComponentAdapter.Valued { + + // <--[property] + // @object ItemTag + // @name equippable + // @input MapTag + // @description + // Controls the properties of an item's equippable <@link language Item Components>. + // The map includes keys: + // - "slot", an ElementTag representing the equipment slot. Valid values: HAND, OFF_HAND, FEET, LEGS, CHEST, HEAD, BODY. + // - "equip_sound", an ElementTag representing the sound played when equipping this item in namespaced key format. + // - "asset_id", an ElementTag representing the asset id for this item in namespaced key format. + // - "camera_overlay", an ElementTag representing the camera overlay to use when the item is equipped in namespaced key format. + // - "allowed_entities", a ListTag(EntityTag) representing entity types that can equip this item. If not set, all entities are allowed to wear this item. + // - "dispensable", a ElementTag(Boolean) controlling whether the item can be dispensed. + // - "swappable", a ElementTag(Boolean) controlling whether the item can be swapped. + // - "damage_on_hurt", a ElementTag(Boolean) controlling whether the item takes damage when the wearer is hurt. + // - "equip_on_interact", a ElementTag(Boolean) controlling whether the item is equipped on entity interaction. + // - "can_be_sheared", a ElementTag(Boolean) controlling whether the item can be sheared off an entity. + // - "shear_sound", an ElementTag representing the sound played when shearing using this item in namespaced key format. + // @mechanism + // Provide no input to reset the item to its default value. + // --> + + public EquippableAdapter() { + super(MapTag.class, DataComponentTypes.EQUIPPABLE, "equippable"); + } + + @Override + public MapTag toDenizen(Equippable value) { + MapTag map = new MapTag(); + map.putObject("slot", new ElementTag(value.slot())); + map.putObject("equip_sound", new ElementTag(value.equipSound().asMinimalString(), true)); + if (value.assetId() != null) { + map.putObject("asset_id", new ElementTag(value.assetId().asMinimalString(), true)); + } + if (value.cameraOverlay() != null) { + map.putObject("camera_overlay", new ElementTag(value.cameraOverlay().asMinimalString(), true)); + } + if (value.allowedEntities() != null) { + ListTag entities = new ListTag(); + value.allowedEntities().forEach(key -> entities.addObject(new ElementTag(key.key().asMinimalString(), true))); + map.putObject("allowed_entities", entities); + } + map.putObject("dispensable", new ElementTag(value.dispensable())); + map.putObject("swappable", new ElementTag(value.swappable())); + map.putObject("damage_on_hurt", new ElementTag(value.damageOnHurt())); + map.putObject("equip_on_interact", new ElementTag(value.equipOnInteract())); + map.putObject("can_be_sheared", new ElementTag(value.canBeSheared())); + map.putObject("shear_sound", new ElementTag(value.shearSound().asMinimalString(), true)); + return map; + } + + @Override + public Equippable fromDenizen(MapTag value, Mechanism mechanism) { + ElementTag slot = value.getObjectAs("slot", ElementTag.class, mechanism.context); + if (slot == null) { + mechanism.echoError("Equippable map must have a 'slot' key."); + return null; + } + if (!slot.matchesEnum(EquipmentSlot.class)) { + mechanism.echoError("Invalid 'slot' specified for equippable: must be a valid EquipmentSlot."); + return null; + } + Equippable.Builder builder = Equippable.equippable(slot.asEnum(EquipmentSlot.class)); + setIfValid(builder::equipSound, value, "equip_sound", ElementTag.class, null, element -> Utilities.parseNamespacedKey(element.asString()), "namespaced key", mechanism); + setIfValid(builder::assetId, value, "asset_id", ElementTag.class, null, element -> Utilities.parseNamespacedKey(element.asString()), "namespaced key", mechanism); + setIfValid(builder::cameraOverlay, value, "camera_overlay", ElementTag.class, null, element -> Utilities.parseNamespacedKey(element.asString()), "namespaced key", mechanism); + setIfValid(builder::dispensable, value, "dispensable", ElementTag.class, ElementTag::isBoolean, ElementTag::asBoolean, "boolean", mechanism); + setIfValid(builder::swappable, value, "swappable", ElementTag.class, ElementTag::isBoolean, ElementTag::asBoolean, "boolean", mechanism); + setIfValid(builder::damageOnHurt, value, "damage_on_hurt", ElementTag.class, ElementTag::isBoolean, ElementTag::asBoolean, "boolean", mechanism); + setIfValid(builder::equipOnInteract, value, "equip_on_interact", ElementTag.class, ElementTag::isBoolean, ElementTag::asBoolean, "boolean", mechanism); + setIfValid(builder::canBeSheared, value, "can_be_sheared", ElementTag.class, ElementTag::isBoolean, ElementTag::asBoolean, "boolean", mechanism); + setIfValid(builder::shearSound, value, "shear_sound", ElementTag.class, null, element -> Utilities.parseNamespacedKey(element.asString()), "namespaced key", mechanism); + ListTag entityList = value.getObjectAs("allowed_entities", ListTag.class, mechanism.context); + if (entityList != null) { + List> keys = new ArrayList<>(); + for (String entry : entityList) { + keys.add(TypedKey.create(RegistryKey.ENTITY_TYPE, Utilities.parseNamespacedKey(entry))); + } + builder.allowedEntities(RegistrySet.keySet(RegistryKey.ENTITY_TYPE, keys)); + } + return builder.build(); + } +} diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/SwingAnimationAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/SwingAnimationAdapter.java new file mode 100644 index 0000000000..b5648f15ec --- /dev/null +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/SwingAnimationAdapter.java @@ -0,0 +1,46 @@ +package com.denizenscript.denizen.paper.datacomponents; + +import com.denizenscript.denizencore.objects.Mechanism; +import com.denizenscript.denizencore.objects.core.ElementTag; +import com.denizenscript.denizencore.objects.core.MapTag; +import io.papermc.paper.datacomponent.DataComponentTypes; +import io.papermc.paper.datacomponent.item.SwingAnimation; + +public class SwingAnimationAdapter extends DataComponentAdapter.Valued { + + // <--[property] + // @object ItemTag + // @name swing_animation + // @input ElementTag + // @description + // Controls an item's swing animation <@link language Item Components>. + // The map includes keys: + // - "animation_type", an ElementTag representing the animation type. Valid animation types can be found at <@link url https://jd.papermc.io/paper/io/papermc/paper/datacomponent/item/SwingAnimation.Animation.html> + // - "duration", an ElementTag(Number) representing the duration of the animation. + // @mechanism + // Provide no input to reset the item to its default value. + // --> + + public SwingAnimationAdapter() { + super(MapTag.class, DataComponentTypes.SWING_ANIMATION, "swing_animation"); + } + + @Override + public MapTag toDenizen(SwingAnimation value) { + MapTag result = new MapTag(); + result.putObject("animation_type", new ElementTag(value.type())); + result.putObject("duration", new ElementTag(value.duration())); + return result; + } + + @Override + public SwingAnimation fromDenizen(MapTag value, Mechanism mechanism) { + SwingAnimation.Builder builder = SwingAnimation.swingAnimation(); + setIfValid(builder::type, value, "animation_type", ElementTag.class, + element -> element.matchesEnum(SwingAnimation.Animation.class), + element -> element.asEnum(SwingAnimation.Animation.class), + "animation type", mechanism); + setIfValid(builder::duration, value, "duration", ElementTag.class, ElementTag::isInt, ElementTag::asInt, "number", mechanism); + return builder.build(); + } +} diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/WeaponAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/WeaponAdapter.java new file mode 100644 index 0000000000..42d2e522ed --- /dev/null +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/WeaponAdapter.java @@ -0,0 +1,43 @@ +package com.denizenscript.denizen.paper.datacomponents; + +import com.denizenscript.denizencore.objects.Mechanism; +import com.denizenscript.denizencore.objects.core.ElementTag; +import com.denizenscript.denizencore.objects.core.MapTag; +import io.papermc.paper.datacomponent.DataComponentTypes; +import io.papermc.paper.datacomponent.item.Weapon; + +public class WeaponAdapter extends DataComponentAdapter.Valued { + + // <--[property] + // @object ItemTag + // @name weapon + // @input MapTag + // @description + // Controls an item's weapon <@link language Item Components>. + // The map includes keys: + // - "disable_blocking_duration", an ElementTag(Decimal) representing the number of seconds that a shield will be disabled for after blocking an attack from this item. + // - "item_damage_per_attack", an ElementTag(Number) representing the amount of durability damage this item will take when used to attack an entity or break a block. + // @mechanism + // Provide no input to reset the item to its default value. + // --> + + public WeaponAdapter() { + super(MapTag.class, DataComponentTypes.WEAPON, "weapon"); + } + + @Override + public MapTag toDenizen(Weapon value) { + MapTag weaponData = new MapTag(); + weaponData.putObject("disable_blocking_duration", new ElementTag(value.disableBlockingForSeconds())); + weaponData.putObject("item_damage_per_attack", new ElementTag(value.itemDamagePerAttack())); + return weaponData; + } + + @Override + public Weapon fromDenizen(MapTag value, Mechanism mechanism) { + Weapon.Builder builder = Weapon.weapon(); + setIfValid(builder::disableBlockingForSeconds, value, "disable_blocking_duration", ElementTag.class, ElementTag::isFloat, ElementTag::asFloat, "decimal number", mechanism); + setIfValid(builder::itemDamagePerAttack, value, "item_damage_per_attack", ElementTag.class, ElementTag::isInt, ElementTag::asInt, "number", mechanism); + return builder.build(); + } +}