Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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<ElementTag, Key> {

// <--[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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.denizenscript.denizen.paper.datacomponents;

import com.denizenscript.denizen.objects.EntityTag;
import com.denizenscript.denizen.utilities.Utilities;
import com.denizenscript.denizen.utilities.entity.DenizenEntityType;
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<MapTag, Equippable> {

// <--[property]
// @object ItemTag
// @name equippable
// @input MapTag
// @description
// Controls the properties of an item's equippable <@link language Item Components>.
// The map includes keys:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to make it clear which keys are optional (and what their default is) - the meta sounds like you might need every single key when actually all you need is slot

// - "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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to be more clear about referring to the right click armor swapping feature

// - "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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is true? iirc it's the sound played when shearing this item off of an entity that's wearing it

// @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) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These keys not always being in tag output isn't documented (can probably go in a @tag section?)

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 allowedEntities = new ListTag(value.allowedEntities().values(), key -> new EntityTag(DenizenEntityType.getByName(key.key().value())));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use the Registry API to get the type from the key directly - Registry.ENTITY_TYPE.getOrThrow(key)

map.putObject("allowed_entities", allowedEntities);
}
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.getElement("slot");
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<TypedKey<EntityType>> keys = new ArrayList<>(entityList.size());
for (String entry : entityList) {
keys.add(TypedKey.create(RegistryKey.ENTITY_TYPE, Utilities.parseNamespacedKey(entry)));
}
Comment on lines +97 to +99

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this tested with actual EntityTag objects in the list? I believe that would break because of the e@ in the strings
This should loop over the list as EntityTags (ListTag#filter), get the entity type, and then create the TypedKey from EntityType#key.

builder.allowedEntities(RegistrySet.keySet(RegistryKey.ENTITY_TYPE, keys));
}
return builder.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.denizenscript.denizen.paper.datacomponents;

import com.denizenscript.denizencore.objects.Mechanism;
import com.denizenscript.denizencore.objects.core.DurationTag;
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<MapTag, SwingAnimation> {

// <--[property]
// @object ItemTag
// @name swing_animation
// @input ElementTag
// @description
// Controls an item's swing animation <@link language Item Components>.
// The map includes keys:
// - "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", a DurationTag 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("type", new ElementTag(value.type()));
result.putObject("duration", new DurationTag((long) value.duration()));
return result;
}

@Override
public SwingAnimation fromDenizen(MapTag value, Mechanism mechanism) {
SwingAnimation.Builder builder = SwingAnimation.swingAnimation();
setIfValid(builder::type, value, "type", ElementTag.class,
null,
element -> element.asEnum(SwingAnimation.Animation.class),
"swing animation type", mechanism);
setIfValid(builder::duration, value, "duration", DurationTag.class, null, DurationTag::getTicksAsInt, "number", mechanism);
return builder.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.denizenscript.denizen.paper.datacomponents;

import com.denizenscript.denizencore.objects.Mechanism;
import com.denizenscript.denizencore.objects.core.DurationTag;
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<MapTag, Weapon> {

// <--[property]
// @object ItemTag
// @name weapon
// @input MapTag
// @description
// Controls an item's weapon <@link language Item Components>.
// The map includes keys:
// - "disable_blocking_duration", a DurationTag representing the duration that a shield will be disabled for after blocking an attack from this item.
// - "durability_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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure about the or break a block? I believe that's controlled by the tool component

// @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 DurationTag(value.disableBlockingForSeconds()));
weaponData.putObject("durability_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", DurationTag.class, null, duration -> (float) duration.getSeconds(), "number", mechanism);
setIfValid(builder::itemDamagePerAttack, value, "durability_per_attack", ElementTag.class, ElementTag::isInt, ElementTag::asInt, "number", mechanism);
return builder.build();
}
}