注目キーワード

ScriptAPIでエンチャントの取得・付与を行う関数集 – ItemStackのgetComponent関数の使い方【マイクラ統合版】

はじめに

マインクラフトにおいては、以下のコマンドでエンチャントの付与ができることが広く知られています:

enchant @s sharpness 1
Copied!

これは手に持っているアイテムに「鋭さ1」のエンチャントを付与する例であり、このサイトの読者なら特に解説は不要でしょう。

さて、実はデフォルトのマインクラフト(コマンドシステム)においてエンチャントを扱うシステムはこれくらいのものです。エンチャントの付与・取得を行う方法は極めて乏しいと言わざるを得ず、上記のコマンドもエンチャントの付与は「手に持っているアイテム」に限定しています。柔軟にエンチャントの管理を行おうと思ったら、力不足であることは否めません。

Character
コマンドを実行するrunCommand関数についてもいえることですが。

そこで登場するのが、ScriptAPIなのです。

ScriptAPIとは?

ScriptAPIとは、マインクラフトのアドオンシステムの一つであり、Javascriptというプログラミング言語を用いてゲームシステムを柔軟に制御するシステムのことです。詳細については以下の記事で解説しています:

ScriptAPIの環境構築については上記の記事をご覧ください。本ページでは割愛します。

ScriptAPIとエンチャント

さて、本ページではScriptAPIを用いてエンチャントを制御(付与及び取得)する方法について、ソースコードを示しながら解説していきます。なお、以下で突拍子もなく変数「server」が登場しますが、これは、以下のimport文:

import * as server from '@minecraft/server';
Copied!

を前提としています。予めご了承ください。

エンチャントを取得

アドオンを制作していると、手に持っているアイテムのエンチャントを取得し、それに応じた処理を行いたくなることがあります。例えば・・・

Character
幸運のエンチャントがついたときにカスタム鉱石を壊したときに、追加で鉱石をドロップしたい!

といった具合。この場合、手に持っているアイテムに「幸運のエンチャントがついているかどうか」を判定する必要に迫られます。より一般的に言い換えると、手に持っているアイテムのエンチャントをリスト形式などで取得できればいいわけですね。以下は、アイテムスタック型(ItemStack Class)を引数としてエンチャントのリストを返す関数です:

function getEnchantments(itemStack){
    const enchantableComponent = itemStack.getComponent("minecraft:enchantable");
    let enchantments = enchantableComponent.getEnchantments();
    return enchantments;
}
Copied!

この関数は、エンチャントのidとレベルで構成されるリストをそのまま返します。即ち、以下のようになります:

[{"level":3,"type":{"id":"unbreaking"}}]
Copied!

あとはこれを解析し、指定のエンチャントの有無を調べたり、エンチャントのレベルを検知することができます。

使用例

以下は、「lq:test」というスクリプトイベントを定義し、今手に持っているアイテムに付与されているエンチャントのリストを表示するものです。

server.system.afterEvents.scriptEventReceive.subscribe(ev => {
    if (ev.id === "lq:test"){
        let item = ev.sourceEntity.getComponent("minecraft:inventory").container.getItem(ev.sourceEntity.selectedSlotIndex);
        ev.sourceEntity.sendMessage(JSON.stringify(getEnchantments(item)));
    }
});
Copied!

関数の使用時には、このようにアイテムスタック型の変数を引数に取る必要があります。

エンチャントレベルを取得

上記とは別に、指定のエンチャントのレベルだけを取得したくなることもあります。上記の例でいえば、幸運のエンチャントレベルに応じてドロップする鉱石の量を変えるといったことですね。それを実装する関数が以下です:

function get_enchantment_level(item_stack, enchantment_id){
  const enchantable = item_stack.getComponent("minecraft:enchantable");
  if (enchantable == undefined){return 0;}
  const enchantments = enchantable.getEnchantments();
  for (let i = 0; i < enchantments.length; i++){
    if (enchantments[i].type.id == enchantment_id){
      return enchantments[i].level;
    }
  }
  return 0;
}
Copied!

これは、アイテムスタックとエンチャントのidを引数とし、そのアイテムについているエンチャントのレベルを数値で返します。また、当該アイテムに指定のエンチャントがついていない場合は0を返します。この性質をif文で用いることで、実質的にエンチャントの有無を簡単に調べる関数として重宝すると思います。

使用例

以下はその性質を用いて、プレイヤーが手に持っているアイテムにエンチャントがついているかどうかをif文内で検知し、それに応じて条件分岐を行う例です:

server.system.afterEvents.scriptEventReceive.subscribe(ev => {
    if (ev.id === "lq:test"){
        let item = ev.sourceEntity.getComponent("minecraft:inventory").container.getItem(ev.sourceEntity.selectedSlotIndex);
        if( get_enchantment_level(item, "sharpness")){
            ev.sourceEntity.sendMessage("ダメージ増加がついています");
        }else{
            ev.sourceEntity.sendMessage("ダメージ増加がついていません");
        }
    }
});
Copied!
Character
idの指定で「minecraft:」は不要です

エンチャントがついたアイテムを付与

以下は、プレイヤーとアイテムid、エンチャントのリストを引数にとって、エンチャントがついたツールをプレイヤーに付与する関数です:

function give_enchanted_item(player, itemName, enchantments) {
    const enchantedItem = new server.ItemStack(itemName, 1);
    let enchantableComponent = enchantedItem.getComponent("minecraft:enchantable");
    enchantableComponent.addEnchantments(enchantments);
    player.getComponent("minecraft:inventory").container.setItem(player.selectedSlotIndex, enchantedItem);
}
Copied!

なお、引数のenchantmentsは注意が必要で、typeとlevelプロパティを含むObject型のlistを引数とする必要があります。具体的な例は以下:

使用例

server.system.afterEvents.scriptEventReceive.subscribe(ev => {
    if (ev.id === "lq:test"){
        give_enchanted_item(ev.sourceEntity, "minecraft:diamond_sword",[{type: new server.EnchantmentType("mending"), level:1}]);
    }
});
Copied!

これは修繕(mending)のレベル1のエンチャントがついたダイアモンド剣をプレイヤーに与えるスクリプトイベントを定義する例です。実際にこれを実行すると、修繕1のついたダイア剣が入手できることが分かると思います。

既存のアイテムにエンチャントを付与

先に示した通り、既存のコマンドシステムは「手持ちのアイテムにエンチャントを付与」することしかサポートしていません。特に、インベントリ内に全てのエンチャント可能なアイテムにエンチャントを付与しようとすると大きな困難が伴います。

しかし、ScriptAPIを用いるとこうした問題も解決します。以下は、プレイヤーのインベントリ内にあるエンチャント可能な全てのアイテムに修繕1を付与する例です(ただし、既に別のエンチャントがあるアイテムに対しては何もしません):

server.system.afterEvents.scriptEventReceive.subscribe(ev => {
    if (ev.id === "lq:test"){
        const player = ev.sourceEntity;
        for (let i = 0; i < 36; i ++){
            let item = player.getComponent("minecraft:inventory").container.getItem(i);
            if(item == undefined){continue;}
            let enchantableComponent = item.getComponent("minecraft:enchantable");
            if (enchantableComponent) {
                if (enchantableComponent.getEnchantments().length > 0) {continue;}
                enchantableComponent.addEnchantments([{type: new server.EnchantmentType("mending"), level:1}]);
                player.getComponent("minecraft:inventory").container.setItem(i, item);
            }
        }
    }
});
Copied!

これをアレンジ・応用することで、エンチャントするアイテムをフィルタリングしたり、付与するエンチャントを変更したりすることができると思います。

Character
結構需要があるかも?

カスタムエンチャントを制作

最後に、カスタムエンチャントを定義する例を示して終わります。以下は、「電撃の力」というLoreを定義し、このLoreがついた剣で敵を攻撃したときに追加で雷を召喚するスクリプトの例です:

server.world.afterEvents.entityHitEntity.subscribe(ev => {
    let player = ev.damagingEntity;
    if(player.typeId == "minecraft:player"){
        let weapon = player.getComponent("minecraft:inventory").container.getItem(player.selectedSlotIndex);
        if(weapon == undefined){return;}
        if(weapon.getLore()[0] == "電撃の力"){
            ev.hitEntity.runCommand("summon lightning_bolt ~ ~ ~");
            ev.damagingEntity.runCommand("say 電撃の力で攻撃を与えた!");
        }
    }
})
Copied!

なお、このあたりの詳しい事情は以下の記事で詳細に解説しています:

まとめ

上記のように、一般的にgetComponent関数のgetEnchantment関数によってエンチャントの取得が可能になり、またaddEnchantment関数によってエンチャントの付与が可能になります。実用的にはこれらを組み合わせて自分好みのスクリプトを作っていくことになるかと思います。

EnchantTypeの表現方法が少し特殊なのが気になるところです。特にlevelとtypeプロパティが必須なので注意しましょう。意外とネット上の情報が少ないので、本記事の内容がお役に立てれば幸いです。

最新情報をチェックしよう!