# 创建你属于自己的属性

## 技能是如何运转的?

### 属性是什么?

技能是一个自定义的物品属性。同一属性在不同的物品上可以有不同的变化。例如，耐久属性允许您为一个物品定义一个自定义的耐久，但并不是所有拥有耐久机制的物品都有相同的耐久

{% hint style="info" %}
耐久机制的工作原理是将物品的耐久存储在它的metadatas中，并使用耐久度条来显示它的自定义耐久度，而不是它的真正耐久度
{% endhint %}

### 那么如果每个项目的属性都不一样，我是不是每次都要重写一个不同的耐久呢？

当然，为此Oraxen允许你为每个属性关联一个ItemModifier列表。ItemModifier是Function，它基本上是一小段代码，包含了当一个物品由服务器通过其配置生成时要对其进行的修改。例如对于耐久机制，我使用了一个 itemModifier，它可以将用户选择的耐久存储在物品的 metadatas 中

```java
item -> item.setCustomTag(NAMESPACED_KEY,
                        PersistentDataType.INTEGER, section.getInt("value"))
```

## 让我们创建我们的第一个属性

{% hint style="info" %}
在本教程中，我将以耐久机制为例（因为它非常简单易懂），但你可以按照本教程来创建你想要的机制
{% endhint %}

### 第一步：创建我们的属性类

从创建一个继承自Mechanical的类开始，如果使用intelliJ，则应该得到如下所示：

```java
class DurabilityMechanic extends Mechanic {

    public DurabilityMechanic(MechanicFactory mechanicFactory, 
                    ConfigurationSection section,
                    Function<ItemBuilder, ItemBuilder>... modifiers) {
        super(mechanicFactory, section, modifiers);
    }

}
```

Mechanic构造函数需要三个参数:

```
- 创建属性的实例
- 用于配置属性的部分
- 项目修饰
```

我希望我的属性文件的每个变化都有不同的耐久，所以我将阅读属性文件的配置并存储值

#### 属性配置部分的外观：

```java
class DurabilityMechanic extends Mechanic {

    private int itemDurability;

    public DurabilityMechanic(MechanicFactory mechanicFactory, 
                              ConfigurationSection section) {
        /* We give:
        - an instance of the Factory which created the mechanic
        - the section used to configure the mechanic
        - the item modifier(s)
         */
        super(mechanicFactory, section, item ->
                item.setCustomTag(NAMESPACED_KEY,
                        PersistentDataType.INTEGER, section.getInt("value")));
        this.itemDurability = section.getInt("value");
    }

    public int getItemMaxDurability() {
        return itemDurability;
    }
}
```

所以现在我们有了一个DurabilityMechanic类，它能够适应任何项目，它将调用我们的DurabilityModifier类来告诉Oraxen在创建它们之前要做哪些修改（这里我们只是在条目中添加一个包含所需新耐久的数据）

### 第二步：创建属性功能类

和之前一样，使用你的ide功能自动创建一个扩展MechanicFactory的类

```java
class DurabilityMechanicFactory extends MechanicFactory {

    public DurabilityMechanicFactory(ConfigurationSection section) {
        super(section);
    }

    @Override
    public Mechanic parse(ConfigurationSection itemMechanicConfiguration) {
        return null;
    }
}
```

我们重写解析方法来创建一个新的属性（通过之前创建的DurabilityMechanic类）。我们还想告诉Oraxen这个属性已经成功实现了，可以使用addToImplemented方法加载，所以我们的类现在看起来是这样的

```java
public class DurabilityMechanicFactory extends MechanicFactory {

    public DurabilityMechanicFactory(ConfigurationSection section) {
        super(section);
    }

    @Override
    public Mechanic parse(ConfigurationSection itemMechanicConfiguration) {
        Mechanic mechanic = new DurabilityMechanic(this, itemMechanicConfiguration);
        addToImplemented(mechanic);
        return mechanic;
    }

}
```

### 第三步：添加我们的功能(事件)

在我的例子中，我只需要使用一个事件来玩耐久，我会创建一个DurabilityMechanicsManager类来实现Listener，以获得一个干净整洁的代码，但我可以直接在DurabilityMechanicFactory中完成。我告诉Bukkit，当建成后，哪个类管理事件

```java
public class DurabilityMechanicFactory extends MechanicFactory {

    public DurabilityMechanicFactory(ConfigurationSection section) {
        super(section);
        MechanicsManager.registerListeners(OraxenPlugin.get(),
                new DurabilityMechanicsManager(this));
    }

    @Override
    public Mechanic parse(ConfigurationSection itemMechanicConfiguration) {
        Mechanic mechanic = new DurabilityMechanic(this, itemMechanicConfiguration);
        addToImplemented(mechanic);
        return mechanic;
    }

}
```

为了根据插件管理的真实耐久计算物品上显示的耐久，我使用了一些简单的数学方法：

bukkitDamage等于bukkitMaxDurability减 realMaxDurability除realDurability乘bukkitMaxDurability&#x20;

所以这就是我的DurabilityMechanicsManager类:

```java
class DurabilityMechanicsManager implements Listener {

    private DurabilityMechanicFactory factory;

    public DurabilityMechanicsManager(DurabilityMechanicFactory factory) {
        this.factory = factory;
    }

    @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
    private void onItemDamaged(PlayerItemDamageEvent event) {
        ItemStack item = event.getItem();
        String itemID = OraxenItems.getIdByItem(item);
        if (factory.isNotImplementedIn(itemID))
            return;

        DurabilityMechanic durabilityMechanic = 
                (DurabilityMechanic) factory.getMechanic(itemID);

        ItemMeta itemMeta = item.getItemMeta();
        PersistentDataContainer persistentDataContainer = 
                itemMeta.getPersistentDataContainer();
        int realDurabilityLeft = persistentDataContainer
                .get(DurabilityMechanic.NAMESPACED_KEY, PersistentDataType.INTEGER) 
                        - event.getDamage();

        if (realDurabilityLeft > 0) {
            double realMaxDurability = 
                    //because int rounded values suck
                    durabilityMechanic.getItemMaxDurability();
            persistentDataContainer.set(DurabilityMechanic.NAMESPACED_KEY,
                    PersistentDataType.INTEGER, realDurabilityLeft);
            ((Damageable) itemMeta).setDamage((int) (item.getType()
                    .getMaxDurability() - realDurabilityLeft 
                    / realMaxDurability * item.getType().getMaxDurability()));
            item.setItemMeta(itemMeta);
        } else {
            item.setAmount(0);
        }

    }

}
```

### 最后一步：注册你的属性

只需在加载插件时调用这一行（例如在你的onEnable方法中）

```java
MechanicsManager.registerMechanicFactory("durability", 
                                DurabilityMechanicFactory.class);
```

## 结论

为了正确创建新的属性，建议将其代码分为三部分：

* 一个扩展MechanicFactory
* 一个属性，它扩展了属性
* 在MechanicsManager类中加入您自己的功能（也可以选择）

{% hint style="info" %}
由于有了ItemModifier，您可以使用您的机制修改项目：

```java
item -> item.setCustomTag(NAMESPACED_KEY,
                        PersistentDataType.INTEGER, section.getInt("value"))
```

也可以使用类似的方法修改材质包：

```java
ResourcePack.addModifiers(packFolder -> {/* your modifications
```

{% endhint %}

最后注册你的技能！

总结一下教程，这里是耐久机制的完整源码
