初心者modderの備忘録

マイクラのmodを作りたくて、初めて見たのですが難しくて忘れそうなので自分用の備忘録も兼ねてブログにしようと思います

9日目 Tile Entityへのインベントリの追加

先日作成したTileEntityにインベントリを追加しようと思います。
インベントリ自体はTileEntityクラスのみ編集することで追加できます。
今回はそれに加えてインベントリにアクセスするための機能をブロッククラスに追加していきます。


インベントリを実装するためのLazyOptionalをTileEntityクラスに追加し、それに合わせてコードを追加していきます。

public class TestTileEntity extends TileEntity
{
	private int count = 0;
	private LazyOptional<ItemStackHandler> itemHandler = LazyOptional.of(() -> new ItemStackHandler(1));

	public TestTileEntity()
	{
		super(ModTileEntities.TEST_TILE);
	}

	@Override
	public CompoundNBT write(CompoundNBT compound)
	{
		compound.putInt("T_count", count);
		itemHandler.ifPresent(h ->
		{
			compound.put("Items", h.serializeNBT() );
		});
		System.out.println("write");
		return super.write(compound);
	}

	@Override
	public void read(CompoundNBT compound)
	{
		System.out.println("read");
		super.read(compound);
		count = compound.getInt("T_count");
		itemHandler.ifPresent(h ->
		{
			if(compound.contains("Items"))
			{
				h.deserializeNBT(compound.getCompound("Items"));
			}
		});
	}

	public void increment()
	{
		count++;
	}

	public int getCount()
	{
		return count;
	}

	@Override
	public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side)
	{
		if(cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY)
		{
			return itemHandler.cast();
		}
		return super.getCapability(cap, side);
	}
}

4行目でインベントリを持たせるためのItemStackHandlerを作成しています。今回は1スロットのみ追加しています。ItemStackHandlerのコンストラクターの引数でスロット数を指定できます。
ItemHandlerを他のクラスに渡すためのgetCapabilityメソッドがLazyOptionalなので、宣言はItemStackHandler型のLazyOptionalで行ないます。

続いて、ItemStackHandlerを保存するために、NBTcompoundのwrtie (15行目~) およびread (29行目~) に書き加えます。
ItemStackHandlerを丸ごとシリアライズしてNBTに書き込み、読み込むときはNBTからItemStackHandlerにデシリアライズします。
ただし読み込みに関して、初回読み込み時にはNBTがnullになってしまうのでnullチェックを入れています。

最後に、このクラスのItemStackHandlerを他のクラスに渡すためのgetCapabilityメソッドをオーバーライドします (49行目~)。
引数のcapabilityがITEM_HANDLER_CAPABILITYである場合には、このクラスのLazyOptionalをIItemHandlerにキャストして渡し、それ以外の場合ではスーパークラスの同メソッドを呼び出します。

これでタイルエンティティへインベントリが追加されました。



続いて、Blockクラスの編集を行ないます。

public class TestTileBlock extends Block
{
	public TestTileBlock(Properties properties, String name)
	{
		super(properties);
		setRegistryName(TestMod.MODID, name);
	}

	@Override
	public boolean hasTileEntity(BlockState state)
	{
		return true;
	}

	@Override
	public TileEntity createTileEntity(BlockState state, IBlockReader world)
	{
		return ModTileEntities.TEST_TILE.create();
	}

	@Override
	public ActionResultType func_225533_a_(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult result)
	{
		TileEntity te = world.getTileEntity(pos);
		if(te instanceof TestTileEntity && !world.isRemote)
		{
			((TestTileEntity) te).increment();

			te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).<ItemStackHandler>cast()
			.ifPresent(h ->
			{
				h.setStackInSlot(0, new ItemStack(Items.DIAMOND, ((TestTileEntity) te).getCount()));
			}
					);

			System.out.println(((TestTileEntity) te).getCount() +", "+pos);
		}
		return ActionResultType.SUCCESS;
	}

	@SuppressWarnings("deprecation")
	@Override
	public void onReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean isMoving)
	{
		if(world.getTileEntity(pos) instanceof TestTileEntity && !world.isRemote)
		{
			TestTileEntity te = (TestTileEntity) world.getTileEntity(pos);

			te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).ifPresent(h ->
			{
				world.addEntity(new ItemEntity(world, pos.getX(), pos.getY(), pos.getZ(), h.getStackInSlot(0)));
			});
		}
		super.onReplaced(state, world, pos, newState, isMoving);
	}
}

まず、ブロックを右クリックしたときの挙動として、29~34行目の処理を追加しました。
getCapabilityでIItemHandler型としてTestTileEntityのItemStackHandlerを受け取り、それをItemStackHandlerにキャストします (IItemHandlerにはsetStackInSlotメソッドが存在しないため)。
そして、setStackInSlotを使ってcountの数だけダイアモンドをスロットにセットします。



次に、このブロックが壊されたときの挙動として、49~52行目の処理を追加しました。
先程と同様に、getCapabilityでIItemHandlerを受け取ります。
getStackInSlotはIItenHandlerにも実装されているのでキャストなしで使います。
インベントリ内のアイテムスタックをアイテムエンティティとして生成し、コンソールにアイテムと個数を表示するようにしました。

これで実際に動かしてみます。
12回クリックした後、一度セーブします。

f:id:json_fileman:20200513233208p:plain


その後、再びログインしブロックを破壊します。

f:id:json_fileman:20200513233543p:plain


すると、12個のダイヤモンドがドロップし、コンソールにもそのように表示されています。
ちゃんとインベントリの内容が保存されています。


次回は、このインベントリをもつタイルエンティティにGUIを実装したいと思います。