LootTable(戦利品表)はざっくり言うと、mobからのドロップ条件や確率などやダンジョンなどの宝箱の中身の内容などがjsonファイルに記述してあってそれをjavaのソースコードで紐づけを行っているものです。
このForgeのHPに載っている内容のコードを利用することによってMobのドロップ品を
追加したり宝箱の中身を追加したり・・・変更したりできるようになります。
せっかく追加した自分のmodのアイテムたちをmobからドロップさせたり、
宝箱の中身に加えることが出来たらとっても素敵だと思いませんか?
というか私がどうしても自作アイテムを宝箱の中身に加えたいから頑張ったのです(`・ω・´)
さて・・・当然ForgeのHPは英語で書かれています。しかも私的にはざっくりとしか書かれてなくて苦労しました(;´∀`) 私がグーグル先生(翻訳の方)とざっくり訳したものもメモ替わりに
書いておきます。訳してもわからない部分も多々ありますが、LootTableに追加することに
関しては出来るようになりましたので良しとしておきます(`・ω・´)
他の事柄に対してはもしこれを読んだ方がわかった!となられましたら是非教えていただきたいと願っております<(_ _)>
『ルートテーブルの登録』
Minecrafをロードしてルートテーブルを認識させるには次にように呼び出す。
LootTableList.register(new ResourceLocation("modid", "loot_table_name"));
これは下記のjsonファイルを読み込む。
/assets/modid/loot_tables/loot_table_name.json
※modのLootTableのLootPoolには、Table内でそのPoolを識別するための独自の
nameタグを含める必要がある。
一般的なnameタグの名前の付け方はentryに含まれるアイテムの種類で
Poolに名前を付けることです。
同じnameタグで複数のLootentryを指定する場合(例えば同じアイテムであるが機能が異なる)は、Pool内で各entryごとに識別するための独自のentryNameタグを与える必要がある。
衝突しないnameタグの場合はentryNameは自動的にnameの値に設定される。
これらの追加要件はロード時にLootTableEventwo使用してTableの
変更を容易にするためにForgeによって課せられる。
『カスタムオブジェクトの登録』
バニラに加えて独自の戦利品条件、戦利品機能、エンティティプロパティを登録することもできます。 エンティティプロパティはminecraft:entity_properties戦利品条件の使用専用であり、略奪に関与するエンティティ(略奪エンティティまたはキラー)が特定のプロパティを持っているかどうかをテストするために使用されます。 バニラの唯一のプロパティはminecraft:on_fireです。 LootConditionManager.registerCondition、 LootFunctionManager.registerFunction()、または EntityPropertyManager.registerProperty()をそれぞれ呼び出すことにより、3つすべてが同様に登録されます。 メソッドは、オブジェクトのIDをResourceLocationとして受け取るSerializerインスタンスと、コードで動作を実装するクラス(それぞれLootCondition、LootFunction、およびEntityProperty)を取ります。 JSONシリアライザーとデシリアライザーを登録するため、条件、関数、またはプロパティを使用するときに追加のフィールドが必要になる場合があります。 例については、net.minecraft.world.storage.loot。{条件、関数、プロパティ}の標準的な実装を参照してください。 次に、条件、関数、またはプロパティを使用するには、シリアライザコンストラクターに渡したレジストリ名を指定するだけです。
戦利品エントリの例:
『バニラのLootTableの修正』
独自のルートテーブル、条件、関数、エンティティプロパティを指定できるだけでなく、ロード時に他のものを変更することもできます。
※userはバニラによってゲーム(とmod)の独自のテーブルを上書きするために、独自のルートテーブルをワールドセーブディレクトリに配置することが 許可されている。これらは構成ファイルと見なされるため、設計上以下に説明する方法、設計によるものでないと変更できない。
実行時にテーブルを変更するためのエントリポイントは、LootTableLoadEventでロードされるテーブルごとに1回発生します。 ここから名前でプールをクエリして削除したり、LootPoolのインスタンスを追加したりできます。 これがmodの戦利品プールに名前を付ける必要がある理由です。バニラの戦利品プールには名前が付いていないため、バニラのテーブルをどのように変更するのか疑問に思われるかもしれません。Forgeはバニラテーブル内のすべてのプールの名前を生成することにより、これを解決します。多くのテーブルにはプールが1つしかないため、最初のプールはmainという名前です。 後続のプールは、2番目のプールにはpool1、3番目にはpool2というように、位置によって名前が付けられます。プールを削除しても、他のプールの名前は変更されません。 各LootPool内でプールのロールおよびボーナスロール属性(テーブルがこのプールを呼び出す回数)を変更したり、名前でエントリをクエリおよび削除したり、LootEntryのインスタンスを追加したりすることもできます。 プールの場合と同様に、エントリには、取得と削除のために独自の名前が必要です。Forgeはこれを解決するために、非表示のentryNameフィールドをすべての戦利品エントリに追加します。エントリのnameフィールドがプール内で独自の場合、entryNameは自動的に名前に設定されます。それ以外の場合、名前はmoddedエントリーで指定する必要があり、vanillaエントリーに対して自動的に生成されます。リピートごとに、番号が増加します。 たとえば、バニラプールにそれぞれ「minecraft:stick」という名前の3つのエントリがある場合、生成される3つのentryNameタグは、minecraft:stick、minecraft:stick#0、およびminecraft:stick#1になります。同様に、エントリを削除しても、他のエントリの名前はシフトされません。
※テーブルのLootTableLoadEventの実行中に、テーブルに必要なすべての変更を行う必要があります。その後の変更は安全性チェックによって許可されないか、安全性チェックがバイパスされると未定義の動作を引き起こします。
『ダンジョン戦利品の追加』
バニラのルートテーブルを変更する例としてダンジョンにランダムに置いてある中身を追加。
まず、変更するテーブルのイベントをリッスンします。
@SubscribeEvent
public void lootLoad(LootTableLoadEvent evt) {
if (evt.getName().toString().equals("minecraft:chests/simple_dungeon")) {
// evt.getTable()で何かを行う
}
}
この場合simple_dungeonのチェストに生成の可能性を追加しているが既存のプールのエントリの重みに干渉したくない。 ルートエントリは完全に異なるテーブルから再帰的に描画できるため、最も柔軟でシンプルなソリューションは独自のルートテーブルJSONを参照する単一のルートエントリを持つ別のプールを追加することです。
例えばあなたのmodに /assets/mymod/loot_tables/inject/simple_dungeon.json
があるとします。
※このテーブルをLootTableList.register()で登録する必要があります!
LootEntry entry = new LootEntryTable(new ResourceLocation("mymod:inject/simple_dungeon"),weight,quality,conditions,entryname); //プール内の唯一のエントリであるため、重みは関係ありません。 他のパラメータは必要に応じて設定します。
LootPool pool = new LootPool(new LootEntry[]{entry},conditions,rolls,bonusrolls, name); //他のパラメータは必要に応じて設定します。
evt.getTable().addPool(pool);
その後、戦利品エントリとプールが作成されて追加され、ダンジョンチェスト用の新しい戦利品プールが作成されます。 確率は60%であり、ネザースターは40%です。 もちろん、追加するルートを事前に決定できない場合は、 上記の呼び出しと同様に、イベントハンドラーにLootPoolsとLootEntryの実装を自由に作成して追加できます。
『Mobドロップの変更』
EntityLivingのサブクラスは、死亡時にルートテーブルからの描画を自動的にサポートします。 これは、getLootTableメソッドをオーバーライドして、ResourceLocationを目的のテーブルに返すことによって行われます。これは、Mobのデフォルトテーブルとして機能します。 エンティティのdeathLootTableフィールドを設定することにより、1つのエンティティのモブと他のモッドの両方のモブのテーブルをオーバーライドできます。
『コード内のルートの生成』
場合によっては、独自のコードからルートテーブルからItemStacksを生成することができます。 最初に、戦利品テーブル自体を取得します(worldへのアクセスが必要です)。
LootTable table = this.world.getLootTableManager().getLootTableFromLocation(new ResourceLocation("mymod:my_table")); //assets/mymod/loot_tables/my_table.jsonに解決されます
次に、提供されたLootContextBuilderを使用してLootContextを作成します。LootContextBuilderは殺し屋、略奪者の運、最後の一撃など、
略奪のコンテキストに関する情報を保持します。
LootContext ctx = new LootContext.Builder(world)
.withLuck(...) // 運を調整, 通常はEntityPlayer.getLuck()
.withLootedEntity(...) // ルートエンティティを設定
.withPlayer(...) // プレイヤーをキラーに設定
.withDamageSource(...) // プレイヤー以外の死因要因
.build();
最後に、ItemStackのコレクションを取得するには:
List stacks = table.generateLootForPools(world.rand, ctx);
または、在庫を埋めるには:
table.fillInventory(iinventory, world.rand, ctx);
現在のところ、これはIInventoryでのみ機能します。
上記の内容は理解できましたでしょうか。(私にはよくわからないことだらけでした)
私が出来るようになったのは2つだけです。
1つめはLootTableの登録とそのjsonファイルの作成。
2つめはバニラのLootTableに自分のmodのLootTableを追加する。
~LootTableの登録~
assets/modidにloot_tablsという名前のフォルダを作成します。
そのloot_tablsというフォルダの中にLootTableのjsonファイルを作成します。
分類ごとにさらにフォルダに分けてもOKです。
jsonファイルの内容などはバニラを参考にするのですが、ForgeのHPにも
書いてあるように、バニラとの相違点があるので丸っとコピペするだけでは
ダメなのです。あくまでも参考程度にします。
asesets/moonrosemod/loot_tables/chests/test.json
{
"pools":
[
{
"name":"test",
"rolls": 2,
"entries":
[
{
"type": "item",
"name": "moonrosemod:rubyrod",
"weight": 50,
"functions":
[
{
"function": "enchant_with_levels",
"levels": 30,
"treasure": true
}
]
},
{
"type": "item",
"name": "moonrosemod:rubysword",
"weight": 40,
"functions":
[
{
"function": "enchant_randomly"
}
]
}
]
},
{
"name":"test1",
"rolls": 3,
"entries":
[
{
"type": "item",
"name": "minecraft:golden_apple",
"weight": 40,
"functions":
[
{
"function": "set_count",
"count":
{
"min": 1,
"max": 2
},
"function":"set_data",
"data": 1
}
]
}
]
},
{
"name":"test2",
"rolls": 3,
"entries":
[
{
"entryName":"aa",
"type": "item",
"name": "moonrosemod:platinumapple",
"weight": 40,
"functions":
[
{
"function": "set_count",
"count":
{
"min": 1,
"max": 2
},
"function":"set_data",
"data": 1
}
]
},
{
"type": "item",
"name": "moonrosemod:platinumapple",
"weight": 40,
"functions":
[
{
"function": "set_count",
"count":
{
"min": 1,
"max": 2
}
}
]
},
{
"type": "item",
"name": "minecraft:golden_apple",
"weight": 40,
"functions":
[
{
"function": "set_count",
"count":
{
"min": 1,
"max": 2
}
}
]
}
]
}
]
}
少しでも参考になるようにと詰め込みました(´・ω・`)
金のリンゴは同じアイテム名でメタ値で普通の金のリンゴとエンチャントEffectが
ついた金のリンゴと区別しているアイテムですが
バニラでは同じentry内に書かれていますがForgeのHPにも書いてあるように
modのLootTableでは同じentry内に入れるとそれだけでこのjsonファイルが
反映されなくなるので同じアイテム名の物はentryNameタグをつける必要があります。
それか別のentryにいれるか。
jsonファイルが作成出来たら
@EventHandler public void preInt(FMLPreInitializationEvent event)
{
LootTableList.register
(new ResourceLocation("moonrosemod:chests/test"));
}
LootTableの登録をします。modid:jsonファイル名
分類フォルダに入れてなければmoonrosemod:testになります。
ちゃんと反映されています。確率があるので全部のアイテムが入るわけではないので
何回か出してみて確認します。しかし・・・LootTableの登録をしただけなので
何にも入ってないし世界のどこにも生成されていませんw
なのでLootTableを呼び出すコマンドを使います。
/setblock ~-1 ~0 ~ minecraft:chest 0 replace {LootTable:"moonrosemod:chests/test"}
それかアイテムを使ったときになんかするコードを書いてそのアイテムを使うか。
@Override
public EnumActionResult onItemUse(EntityPlayer player, World worldIn, BlockPos pos, EnumHand hand,
EnumFacing facing, float hitX, float hitY, float hitZ)
{
String test = "moonrosemod:chests/test";
String test1 = "minecraft:chests/spawn_bonus_chest";
worldIn.setBlockState(pos.offset(facing), Blocks.CHEST.getDefaultState());
if (worldIn.getTileEntity(pos.offset(facing)) instanceof TileEntityChest) {
((TileEntityChest) worldIn.getTileEntity(pos.offset(facing))).setLootTable(
new ResourceLocation(test), player.getRNG().nextLong());
}
return super.onItemUse(player, worldIn, pos, hand, facing, hitX, hitY, hitZ);
}
このメソッドを自分のmodのアイテムクラスとか武器クラスにいれる。
私はrubyrodのクラスにItemSwordクラスを継承させてこのメソッドを入れています。
なので杖を地面に対して使うたびにチェストが出てきます。
~バニラのLootTableの内容にmodのLootTableの内容を追加する~
さて・・・いよいよ本題の宝箱やmobドロップに追加したいと思います。
追加したいアイテムなどをまたjsonファイルにLootTableを書いて作成します。
asesets/moonrosemod/loot_tables/inject/test2.json
{
"pools": [
{
"name": "main",
"rolls": 2,
"entries": [
{
"type": "item",
"name": "moonrosemod:silverapple",
"weight": 40
},
{
"type": "item",
"name": "moonrosemod:ruby",
"weight": 50,
"functions":
[
{
"function": "set_count",
"count":
{
"min": 1,
"max": 2
}
}
]
},
{
"type": "empty",
"weight": 40
}
]
}
]
}
そしてこれを登録します。
真ん中のやつですね。登録方法は先ほどやったので省略します。
それからバニラのLootTableにmodのLootTableを追加するイベントメソッドの
コードを記述します。
@EventHandler
public void init(FMLInitializationEvent event)
{
MinecraftForge.EVENT_BUS.register(this);
}
//既存の宝箱(スポーンボーナスチェスト)のルートテーブルに追加するイベントメソッド
@SubscribeEvent
public void lootLoad(LootTableLoadEvent evt)
{
if (evt.getName().toString().equals("minecraft:chests/spawn_bonus_chest"))
{
LootEntry entry = new LootEntryTable
(new ResourceLocation("moonrosemod:inject/test2"), 10, 3, new LootCondition[0], "moonrosemod_inject_test2_entry");
LootPool pool = new LootPool(new LootEntry[] {entry},new LootCondition[0] ,
new RandomValueRange(3), new RandomValueRange(1, 3), "moonrosemod_inject_test2_pool");
evt.getTable().addPool(pool);
}
}
私はmodidを設定したクラスに直に書いていますので上記の記述でいいですが
他のクラスに書く場合は気を付けてください。
if~の最後のかっこの中に
"minecraft:chests/spawn_bonus_chest"はスポーンボーナスチェスト
"minecraft:chests/abandoned_mineshaft "は廃坑道
"minecraft:chests/desert_pyramid "は砂漠の寺院
というように追加したいバニラのLootTable名を入れます。
LootEntryTable(ResourceLocation tableIn, int weightIn, int qualityIn, LootCondition[] conditionsIn, String entryName)と引数が設定されているのでそれぞれ入れます。
tebleInの部分にはLootTableの名前をmoonrosemod:inject/test2とかMODID,inject/test2とかでもなんでもいいのでmodid+:LootTableのファイル名をいれる。
ウェイトとクオリティはそれぞれの数値をいれます。(あまりよくわからないから適当な数値が
入ってますw)比重と優劣でいいのかしら?他のentryがない場合は関係ないみたいなことが
ForgeのHPに書いてありますね。LootCondtion[]については大体こういう引数については
上記の写真みたいにいれるようですよ。他のメソッドでもちらほら見かけます。
最後の名前はわかりやすければなんでもいいです。他と被らなければ。
Poolの方の名前と一緒でも別でもいいみたいです。
LootPool(LootEntry[] lootEntriesIn, LootCondition[] poolConditionsIn, RandomValueRange rollsIn, RandomValueRange bonusRollsIn, String name)
一番目の引数に先ほどentryという変数に代入したnew LootEntryTable(new ResourceLocation("moonrosemod:inject/test2"), 10, 3, new LootCondition[0], "moonrosemod_inject_test2_entry");を入れます。数値のところもよくわからないけど
ロールを回す数とボーナスロールの数で最後に名前を入れますentryと同じでもいいですし
別のでもいいです。数値とか確率については実際に試してみながら調整します(;´Д`)
で・・・
最後に evt.getTable().addPool(pool);でpoolという変数に代入しているもの・・・
つまりは"moonrosemod:inject/test2"のLootTableの内容を追加しているのです。
スポーンボーナスチェストの中身を確かめるには新規ワールドを作成して実際に
スポーンボーナスチェストを確かめるか(確率を高くしてあるので確かめられるとは思う)
スポーンボーナスチェストをコマンドで呼び出します。
/setblock ~-1 ~0 ~ minecraft:chest 0 replace {LootTable:"minecraft:chests/spawn_bonus_chest"}
先ほどのアイテムを使用したときに何かする的なのが付いたアイテムを使って
出してもいいです。
自作のアイテムがチェストに入っているのは嬉しいものですね。
同じようにmobのLootTableにもmodのLootTableを追加できます。
if (evt.getName().toString().equals("minecraft:entities/zombie"))というように
変えればいいだけなのです。
このクラスにリストがあるのでバニラのLootTableの名前はこれを見ます。
mobにドロップさせたいアイテムなどをjsonファイルで書いていきます。
asesets/moonrosemod/loot_tables/entites/drop.json
{
"pools": [
{
"name": "drop",
"rolls": 1,
"entries": [
{
"type": "item",
"name": "moonrosemod:silver_ore",
"weight": 1,
"functions": [
{
"function": "set_count",
"count": {
"min": 1,
"max": 3
}
},
{
"function": "furnace_smelt",
"conditions": [
{
"condition": "entity_properties",
"entity": "this",
"properties": {
"on_fire": true
}
}
]
},
{
"function": "looting_enchant",
"count": {
"min": 0,
"max": 1
}
}
]
}
]
},
{
"conditions": [
{
"condition": "killed_by_player"
},
{
"condition": "random_chance_with_looting",
"chance": 0.05,
"looting_multiplier": 0.01
}
],
"name": "drop2",
"rolls": 1,
"entries": [
{
"type": "item",
"name": "minecraft:emerald",
"weight": 1,
"functions": [
{
"function": "set_count",
"count": {
"min": 1,
"max": 3
}
},
{
"function": "looting_enchant",
"count": {
"min": 0,
"max": 1
}
}
]
}
]
},
{
"name":"drop3",
"rolls": 1,
"entries": [
{
"type": "item",
"name": "minecraft:tipped_arrow",
"weight": 1,
"functions": [
{
"function": "set_count",
"count": {
"min": 0,
"max": 1
}
},
{
"function": "looting_enchant",
"count": {
"min": 0,
"max": 1
},
"limit": 1
},
{
"function": "set_nbt",
"tag": "{Potion:\"minecraft:slowness\"}"
}
]
}
],
"conditions": [
{
"condition": "killed_by_player"
}
]
},
{
"name":"drop4",
"rolls": 1,
"entries": [
{
"type": "loot_table",
"name": "moonrosemod:inject/test2",
"weight": 1
}
]
},
{
"name": "drop5",
"rolls": 1,
"entries": [
{
"type": "item",
"name": "minecraft:golden_apple",
"weight": 1,
"functions": [
{
"function": "set_count",
"count": {
"min": 1,
"max": 3
}
},
{
"function":"set_data",
"data": 1
},
{
"function": "looting_enchant",
"count": {
"min": 0,
"max": 1
}
}
]
}
],
"conditions": [
{
"condition": "entity_properties",
"entity": "this",
"properties": {
"on_fire": true
}
}
]
}
]
}
少しでも参考になるようにいっぱい詰め込みました!
しかし・・・functionはこうやってつなぐんですね・・・。
chests/testのところでは{}で囲わなくてもつなげられたからこれをみてびっくり
してます(;´Д`)まああれでもちゃんと反映されているのだしいいとしておきます。
{
"function": "looting_enchant",
"count": {
"min": 0,
"max": 1
}はバニラの方で入っているからいれてみたけれど
追加分に関してはいちいちいれなくても大丈夫です。
また登録します。3番目のです。
さっきのメソッドのメソッド名をちょっと変えてwあとはバニラのLootTableの名称を
変えただけのものです('ω')
ゾンビはエッグを使って呼び放題。
普通に倒したとき。
on fireで倒したとき。
playerkillerを指定していないものに関しては他の要因で死んだときもドロップします。
ゾンビのドロップがずいぶん豪華に!!(;´∀`)
とっても長くなりましたが終了いたします。
『カスタムオブジェクトの登録』はよくわからないので既存のものを利用するばかりです(;´∀`)
『Mobドロップの変更』はmobを新規作成するときに使用することしかわかっていません。
『コード内のルートの生成』もさっぱりよくわからない。














