Minecraft Modding 応用① 1.14.4【Item.Properties】

#はじめに
最近知り合いの方が1.14.4で開発してる&正直1.7.10のModdingに飽きてきたので1.14.4を初めて触ってみました。
予想ですが、バニラのコード見てても開発側ですら仕様変更に追いつけてない気がします。
いろいろ変わりすぎて終始「🤔🤔🤔」って感じでした。笑

基礎の解説は知り合いの方がくっそわかりやすく書いてくれてますのでそちらをご覧下さい。
こちら: [Minecraft 1.14.4 Forge Modの作成 その1](https://qiita.com/Hiroya_W/items/f38089724d9358d1668d) 私は応用の方を対抗して書きたいと思います！

2019/12/28追記:
私も環境構築の記事を書いたので是非読んでください。 [こちら](https://qiita.com/RiSE4/items/f751569a51b46d263e0e)

#開発環境 
- Windows 10
- Minecraft 1.14.4
- Minecraft Forge 1.14.4-28.1.79
- IntelliJ IDEA 2019.2.4
- Gradle 4.9
- Forge Gradle 3.+

#注意
この解説シリーズはある程度自力でModdingができる方向けです。
基礎的な部分はあまり解説しないので、先駆者の方の解説なり動画なりをご覧下さい。
1.14.4は初めて触るので、手探り状態で書いています。もっといいコーディングのやり方もあるはずです。
また、クラスの数が多いのでコード内に直接解説することが多いと思います。悪しからず。


#作っていきましょう！
##コンセプトについて
今回は**複合ツール**を作ってみようと思います。私が個人用に作った仕様を引き継いで、

- ***ツルハシ、シャベル、オノとして機能する*** (主目標1 理解できれば割と簡単)
- ***NBTTagを利用して破壊速度を変更できるモードの実装*** (主目標2 コード長くてばりめんどい)
- ***自動で耐久が回復する*** (ついで めっちゃ簡単)

をやりたいと思います。私はクワは武器としてアホみたいなスペックで用意したい派なので、クワの機能は追加しません。
が、すぐ実装可能なのでコンセプトを達成できた後にちょこっと解説しようと思います。

そして、今回のポイントとして、**Item.Properties**と**NBTTagCompound**があります。
この記事ではItem.Propertiesをメインに解説していきます。

##ひな型やヘルパーなどの実装
```java:メインのクラス
package rise.sunrisecraft.common;

import rise.sunrisecraft.lib.ToolMaterial;

@Mod("sunrisecraft")
public class SunriseCraft {

    public static final String MOD_ID = "sunrisecraft";

    static ToolMaterial TOOL_RISEMOND = new ToolMaterial(4, 4800, 17F, 10F, 42);
}

```
本来はコンストラクタでFMLCommonSetupEventなどを呼び出す必要があったりするのですが、
アイテムの追加程度であればなくても動きます。

```java:ToolMaterialを追加するためのヘルパークラス
package rise.sunrisecraft.lib;

public class ToolMaterial implements IItemTier {

    private float attackDamage, efficiency;
    private int maxUses, harvestLevel, enchantability;
    private ItemStack repairStack;

    public ToolMaterial(int harvestLevel, int maxUses, float efficiency, float attackDamage, int enchantability)
    {
        this.harvestLevel = harvestLevel;
        this.maxUses = maxUses;
        this.efficiency = efficiency;
        this.attackDamage = attackDamage;
        this.enchantability = enchantability;
    }

    /**
     * Set item for repair.
     * @param itemStack Repair item (ItemStack)
     * @return ToolMaterial
     */
    public ToolMaterial setRepairItem(ItemStack itemStack)
    {
        this.repairStack = itemStack;
        return this;
    }

    @Override
    public int getMaxUses() { return this.maxUses; }

    @Override
    public float getEfficiency() { return this.efficiency; }

    @Override
    public float getAttackDamage() { return this.attackDamage; }

    @Override
    public int getHarvestLevel() { return this.harvestLevel; }

    @Override
    public int getEnchantability() { return this.enchantability; }

    @Override
    public Ingredient getRepairMaterial() { return Ingredient.fromStacks(this.repairStack); }
}
```
1.7.10時代に使われていた**ToolMaterial**は**IItemTier**に変更されました。
その度に個別クラスを継承するのは面倒くさかったので、ヘルパークラスとして上記のものを書きました。
1.7.10では*EnumHelper.addToolMaterial(...)*を使っていましたが、このクラスはインスタンスを作るだけです。
今回のツールではIItemTierを継承したクラスを取得する必要があるので作りました。

```java:アイテムの登録とかを自動でやってくれるクラス
package rise.sunrisecraft.common;

@Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD)
public class RegisterHandler {

    @SubscribeEvent
    public static void registerItems(RegistryEvent.Register<Item> event)
    {
        event.getRegistry().registerAll(ItemRegistry.ITEMS.toArray(new Item[0]));
    }
}
```
**@Mod.EventBusSubscriber**は自動的にイベントとして認識してくれるため、
メインクラスにイベント呼び出し用のメソッドを書かなくていいのはこのためです。
**registerItems()**では、ItemRegistryの*ITEMS*内のコンテンツを全て自動的にメタ値0でアイテムとして登録してくれます。
本来メタデータとして0~15を適切に呼び出す必要がありましたが、1.14.4では使用頻度がほぼないのでこれで問題ありません。
自作でメタ値をもたせたい場合はPropertyEnumなどを作ってこのメソッド内で適切に処理をする必要があります。

```java:アイテムを初期化するクラス
package rise.sunrisecraft.common;

public class ItemRegistry {

    public static final List<Item> ITEMS = new ArrayList<>();

    public static final Item RISEMOND_HAMMER = new ItemHammer("risemond_hammer", SunriseCraft.TOOL_RISEMOND);

}
```
初期化だけすれば自動でリストに登録するようにしてあるので、未使用の警告が出ても無視して構いません。
それでは本題のツールのクラスに入っていきましょう！

##ツールのクラスを作っていく
```java:ツールのクラス
package rise.sunrisecraft.item;

import rise.sunrisecraft.common.ItemRegistry;
import rise.sunrisecraft.common.SunriseCraft;
import rise.sunrisecraft.lib.ItemProperties;
import rise.sunrisecraft.lib.ToolMaterial;

public class ItemHammer extends ToolItem {

    public ItemHammer(String name, ToolMaterial material)
    {
        super(material.getAttackDamage(), 9F, material, new HashSet<Block>(),
                new ItemProperties()
                .setItemGroup(SunriseCraft.GROUP_SUNRISE)
                .setHarvestLevel(ToolType.PICKAXE, material.getHarvestLevel())
                .setHarvestLevel(ToolType.SHOVEL, material.getHarvestLevel())
                .setHarvestLevel(ToolType.AXE, material.getHarvestLevel())
        );
        this.setRegistryName(new ResourceLocation(SunriseCraft.MOD_ID, name));
        ItemRegistry.ITEMS.add(this);
    }
}
```
今まではツール類は**ItemTool**を継承していましたが、**ToolItem**に変更されました。
そして、アイテムのインスタンスを作る際、必ず**Item.Properties**が必要になりました。
![37364e705eb3479080ef19f7ad64e3d7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463558/22c8ed0a-6fd0-0cbd-8bef-556d476d5c13.png)

###Item.Propertiesの使い道
- 最大スタックサイズの設定
- クリエイティブタブの指定
- 食料としての機能の実装

といったように、Itemクラス内にあったアイテムに対して細かい設定を追加するメソッドがなくなってしまいました。
それらを一括で管理できるのがItem.Propertiesというわけです。上記以外にも、様々な物がこれに置き換えられています。

そして、今回は1.7.10時代で言う**setHarvestLevel()**の機能に相当する**addToolType()**を使い、ツルハシ、シャベル、オノの機能を追加しました。
ただし、今までは**String**形式で入力していたところを、**ToolType**を参照する形に変更されました。
※2番目の引数の*ToolMaterial*は、先程作ったヘルパークラスです。


###そんなもん使って無くない？
これも私がつくりやすくするために、1.7.10時代のメソッドと同じか似せた名前で呼び出しができるヘルパークラスを作りました。

```java:ItemProperties.java

package rise.sunrisecraft.lib;

public class ItemProperties extends Item.Properties {

    /**
     * Set max stack size.
     * @param stackSize Max stack size
     * @return Properties
     */
    public ItemProperties setMaxStackSize(int stackSize)
    {
        return (ItemProperties) this.maxStackSize(stackSize);
    }

    /**
     * Set max damage value
     * @param maxDamage Max damage value
     * @return Properties
     */
    public ItemProperties setMaxDamage(int maxDamage)
    {
        return (ItemProperties) this.defaultMaxDamage(maxDamage);
    }

    /**
     * Set container item (In crafting: return to crafting grid)
     * @param containerItem Container item
     * @return Properties
     */
    public ItemProperties setContainerItem(Item containerItem)
    {
        return (ItemProperties) this.containerItem(containerItem);
    }

    /**
     * Set item group
     * @param group Item group (Creative tab)
     * @return Properties
     */
    public ItemProperties setItemGroup(ItemGroup group)
    {
        return (ItemProperties) this.group(group);
    }

    /**
     * Set item rarity
     * @param rarity Rarity
     * @return Properties
     */
    public ItemProperties setRarity(Rarity rarity)
    {
        return (ItemProperties) this.rarity(rarity);
    }

    /**
     * Set harvest type and level. (Like "pickaxe", "shovel", "axe" ... and other type from any mods.)
     * @param toolType Tool type
     * @param level Harvest level
     * @return Properties
     */
    public ItemProperties setHarvestLevel(ToolType toolType, int level)
    {
        return (ItemProperties) this.addToolType(toolType, level);
    }
}
```
コードに書いてる説明の通りの働きをします。基本的に名前を変えただけなので挙動は元のクラスと同じです。

#終わりに
今回はItem.Propertiesの解説を行いました。Item.Propertiesはアイテムの実装になくてはならない存在なので、
一度元のクラスに目を通すことをおすすめします。複合ツールとしてはこれで申し分ないですが、
次回はNBTTagCompoundを使用して、アイテムにモードチェンジなどの機能を組み込んでみたいと思います。

お楽しみに！

次回の記事はこちら:[Minecraft Modding 応用② 1.14.4【CompoundNBT】](https://qiita.com/RiSE4/items/c334039b3bbeddf48d16)