マイクラをヴァンサバライクにするプラグインの開発

## はじめに
アドベントカレンダー6日目！！　担当は3年の片平です。
昨日は、桃山さんのLINEでのカレンダー共有についての記事でした。
共有カレンダーをLINEで通知してくれる便利な機能で是非触ってみたいです！

アドベントカレンダーは初めてなので、至らないところがあったらすみません。
今日は僕の好きなMinecraft関連の記事です！

Minecraftのマルチサーバーで使えるプラグインを作成したので、それの解説となります！
昔は全くできなかったことが宮治研に入ってできるようになって、成長を感じています。

## 作るもの
マイクラをヴァンサバライク（Vampire Survivorsというゲーム風）なゲームに変えるプラグインを作成しました。
機能としてはマイクラの進捗（実績のようなもの）を獲得するたびにプレイヤーのレベルがあがり、能力値を選んで上昇させることができるものです。

実際に作ったのは夏休みで、追加したい機能は他にもあるのですがとりあえずレベルアップとステ振りの機能だけ実装しました。

言語はJavaでIntelliJ IDEAを用いて開発しました。

## セットアップ
まず初めにIntelliJ IDEA を起動したらプラグインでMinecraftと検索して一番上のMinecraft Developmentをインストールします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617892/cdbd298e-896b-ba73-c7c2-ee84d3345efb.png)

次にプロジェクトを新規作成を押して
ジェネレーターからMinecraftを選択、画像のように設定してください
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617892/1887eae0-174c-bbe8-247a-c05c1de3e1e8.png)

最初に生成されるTest.javaがこうなっていれば成功です
```java:Test.java
    package com.example.test;
    
    import org.bukkit.plugin.java.JavaPlugin;
    
    public final class Test extends JavaPlugin {
    
        @Override
        public void onEnable() {
            // Plugin startup logic
    
        }
    
        @Override
        public void onDisable() {
            // Plugin shutdown logic
        }
    }
```
## コード
メインのTest.javaの記述は省いて中身だけ書きます
まだまだ整理できてなくて、ひとファイルにまとめてしまっていますがご了承ください
また、デバッグ用のコードや未完成の処理もあるかもしれません🙇‍♂️🙇‍♂️

```java:Listeners.java
public class Listeners implements Listener {

    //マップ生成
    public HashMap<Player, Integer> mapDamage = new HashMap<>();
    public HashMap<Player, Integer> mapHp = new HashMap<>();
    public HashMap<Player, Integer> mapDefence = new HashMap<>();
    public HashMap<Player, Integer> mapLuck = new HashMap<>();
    Inventory inventory = Bukkit.createInventory(null, 9, "LEVEL UP");

    //マップ作成
    @EventHandler
    public void onJoin(PlayerJoinEvent e) {
        mapDamage.put(e.getPlayer(), 0);
        mapHp.put(e.getPlayer(), 0);
        mapDefence.put(e.getPlayer(), 0);
        mapLuck.put(e.getPlayer(), 0);
    }

    //実績解除時のレベルアップ
    @EventHandler
    public void onGetAdvancement(PlayerAdvancementDoneEvent e) {
        Player p = e.getPlayer();
        NamespacedKey advancementKey = e.getAdvancement().getKey();
        Advancement advancement = Bukkit.getAdvancement(advancementKey);

        if (advancement != null) {
            // 実績の表示名を取得
            String displayName = advancement.getDisplay().getTitle();
            Bukkit.broadcastMessage(p.getName() + "さんが" + displayName + "をクリアしてレベルアップしました。");
            inventory.setItem(0, new ViewItems().BuffHeart());
            inventory.setItem(2, new ViewItems().BuffAttack());
            inventory.setItem(4, new ViewItems().BuffDefence());
            inventory.setItem(6, new ViewItems().BuffSpeed());

        } else {
            Bukkit.broadcastMessage(p.getName() + "さんが不明な実績をクリアしてレベルアップしました。");
        }
        p.openInventory(inventory);
        p.playSound(p, Sound.ENTITY_PLAYER_LEVELUP, 1.0F, 1.0F);
    }

    //ステータス割り振り
    @EventHandler
    public void onInventoryClick(InventoryClickEvent e) {
        Player p = (Player) e.getWhoClicked();
        Inventory clickedInventory = e.getClickedInventory();
        ItemStack clickedItem = e.getCurrentItem();

        if (clickedInventory != null && clickedItem != null) {
            if (p.getOpenInventory().getTopInventory().equals(inventory)) {
                if (clickedInventory.equals(inventory)) {
                    if (clickedItem.isSimilar(new ViewItems().BuffHeart())) {
                        // クリックされたアイテムが ViewItemsMain() で設定されたアイテムと一致する場合
                        e.setCancelled(true); // クリックをキャンセル
                        p.updateInventory();
                        p.closeInventory();
                        // ここでアイテムが一致した場合の処理を記述
                        p.sendMessage("HPが増加しました");
                        p.playSound(p, Sound.ENTITY_ITEM_PICKUP, 1.0F, 1.0F);
                        int PlayerHpValue = mapHp.get(p);
                        mapHp.put(p, PlayerHpValue + 2);
                        p.setHealthScale(p.getHealthScale() + 2);
                    } else if (clickedItem.isSimilar(new ViewItems().BuffAttack())) {
                        e.setCancelled(true); // クリックをキャンセル
                        p.updateInventory();
                        p.closeInventory();

                        p.sendMessage("攻撃力が増加しました。");
                        p.playSound(p, Sound.ENTITY_ITEM_PICKUP, 1.0F, 1.0F);
                        int PlayerDamageValue = mapDamage.get(p);
                        mapDamage.put(p, PlayerDamageValue + 1);

                    } else if (clickedItem.isSimilar(new ViewItems().BuffDefence())) {
                        e.setCancelled(true); // クリックをキャンセル
                        p.updateInventory();
                        p.closeInventory();

                        p.sendMessage("防御力が増加しました。");
                        p.playSound(p, Sound.ENTITY_ITEM_PICKUP, 1.0F, 1.0F);
                        int PlayerDefenceValue = mapDefence.get(p);
                        mapDamage.put(p, PlayerDefenceValue + 1);

                    } else if (clickedItem.isSimilar(new ViewItems().BuffSpeed())) {
                        e.setCancelled(true); // クリックをキャンセル
                        p.updateInventory();
                        p.closeInventory();

                        p.sendMessage("運が増加しました。");
                        p.playSound(p, Sound.ENTITY_ITEM_PICKUP, 1.0F, 1.0F);
                        int PlayerLuckValue = mapLuck.get(p);
                        mapLuck.put(p, PlayerLuckValue + 10);


                    } else {
                        // クリックされたアイテムが一致しない場合
                        e.setCancelled(true); // クリックをキャンセル
                        p.sendMessage("このアイテムはクリックできません！");
                    }
                } else {
                    e.setCancelled(true);
                    p.updateInventory();
                    p.sendMessage("インベントリの操作はできません");
                }
            }
        }
    }

    //ダメージアップ処理
    @EventHandler
    public void onEntityDamageByEntity(EntityDamageByEntityEvent e) {
        EntityType attacker = e.getDamager().getType();
        double damage = e.getDamage();
        if (attacker == EntityType.PLAYER) {
            Player p = (Player) e.getDamager();
            e.setDamage(damage + mapDamage.get(p));
            p.sendMessage("あなたは" + damage + mapDamage.get(p) + "のダメージを与えた。");
        }
    }

    //防御処理
    @EventHandler
    public void onEntityDamage(EntityDamageEvent e) {
        EntityType entityType = e.getEntityType();
        double damage = e.getDamage();
        if (entityType == EntityType.PLAYER) {
            Player p = (Player) e.getEntity();
            if (damage - mapDefence.get(p) < 0) {
                e.setDamage(0);
                p.sendMessage("あなたは" + damage + mapDamage.get(p) + "のダメージをうけた。（無敵）");
            } else {
                e.setDamage(damage - mapDefence.get(p));
                p.sendMessage("あなたは" + damage + mapDamage.get(p) + "のダメージをうけた。");
            }
        }
    }

    //幸運処理
    @EventHandler
    public void onBlockDropItem(BlockDropItemEvent e ){
        Player p = e.getPlayer();

        if( e.isCancelled()) {
            return;
        }

        List<Item> drops = e.getItems();
        Random random = new Random();

        for (Item drop : drops) {
            if (random.nextDouble() * 100 < mapLuck.get(p)) {
                ItemStack itemStack = drop.getItemStack(); // ItemからItemStackを取得
                p.getInventory().addItem(itemStack); // プレイヤーのインベントリにアイテムを追加
                p.updateInventory(); // インベントリを更新
                p.sendMessage(itemStack.getType().toString() + "が増えました。");
            }
        }

    }
}
```

## 完成形
これらをビルドしてプラグインサーバーを立てれば完成します
このように実績解除すると4つのうちから報酬を選んぶことができます

![2023-12-06_04.02.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617892/1ed224c6-7bfa-d639-def9-140285815015.png)

一応それぞれ効果としては
体力増加や攻撃力増加、防御力増加、移動速度上昇（調整中）となります

以上でプラグインの開発は終わりです！！

## 終わりに
まだまだわからないことだらけですが、試行錯誤しながら実際に形になると喜びが大きいです！！
僕はMinecraftが大好きなので同じ方いたら是非仲良くしてください！