1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ChatGPTにMinecraftのMODを作ってもらう(後編)

Last updated at Posted at 2022-12-08

ChatGPTにMODを作ってもらおうと思います。前編はChatGPTに任せきりにしてめちゃめちゃだったので、後編では人力で手直ししていこうと思います。結論から申し上げますと動くようにはなりましたが工業要素はゼロです。

IronGeneratorTileEntityの実装

ChatGPTはIronGeneratorTileEntityクラスをIronGeneratorクラスの内部クラスとして実装してくれました。そのままでも支障は特にありません。
Forgeを導入した開発環境ではデコンパイルし難読化解除した(確か)Minecraftのコードを読むことができます。Minecraftの内部実装ではTileEntityの子クラスや孫クラスは内部クラスとして実装されてはいませんでした。何となく統一した方が良いような気がしたので、内部クラスとして実装するのはやめることにします。

IronGeneratorTileEntity.java
public class IronGeneratorTileEntity extends TileEntityLockable implements ISidedInventory, ITickable {
    private int fuelAmount = 0;
    private int electricityAmount = 0;
    private int maxFuelAmount = 1000;
    private int maxElectricityAmount = 10000;
    private String ironGeneratorCustomName;
    private ItemStack[] ironGeneratorItemStacks = new ItemStack[3];
    private int ironGeneratorBurnTime;
    private int currentItemBurnTime;
    private int cookTime;
    private int totalCookTime;

    private final int[] SLOTS_TOP = new int[] {0};
    private final int[] SLOTS_BOTTOM = new int[] {2, 1};
    private final int[] SLOTS_SIDES = new int[] {1};

    public void update() {
        if (!worldObj.isRemote) {
            if (fuelAmount > 0) {
                fuelAmount--;
                electricityAmount += 100;
                if (electricityAmount > maxElectricityAmount) {
                    electricityAmount = maxElectricityAmount;
                }
            }
        }
    }

    public int getFuelAmount() {
        return fuelAmount;
    }

    public int getElectricityAmount() {
        return electricityAmount;
    }

    public int getMaxFuelAmount() {
        return maxFuelAmount;
    }

    public int getMaxElectricityAmount() {
        return maxElectricityAmount;
    }

    public boolean isBurning() {
        return fuelAmount > 0;
    }

    public boolean isFull() {
        return electricityAmount >= maxElectricityAmount;
    }

    public void setFuelAmount(int fuelAmount) {
        this.fuelAmount = fuelAmount;
    }

    public void setElectricityAmount(int electricityAmount) {
        this.electricityAmount = electricityAmount;
    }

    public Container createContainer(InventoryPlayer playerInventoty, EntityPlayer playerIn)
    {
        return  new ContainerIronGenerator(playerInventoty, this);
    }

    public String getGuiID() {
        return "tile:examplemod_iron_generator";
    }

    public String getName()
    {
        return this.hasCustomName() ? this.ironGeneratorCustomName : "container.iron_generator";
    }

    public boolean hasCustomName()
    {
        return this.ironGeneratorCustomName != null && !this.ironGeneratorCustomName.isEmpty();
    }

    public int getSizeInventory()
    {
        return this.ironGeneratorItemStacks.length;
    }

    @Nullable
    public ItemStack getStackInSlot(int index)
    {
        return this.ironGeneratorItemStacks[index];
    }

    @Nullable
    public ItemStack decrStackSize(int index, int count)
    {
        return ItemStackHelper.getAndSplit(this.ironGeneratorItemStacks, index, count);
    }

    @Nullable
    public ItemStack removeStackFromSlot(int index)
    {
        return ItemStackHelper.getAndRemove(this.ironGeneratorItemStacks, index);
    }

    public void setInventorySlotContents(int index, @Nullable ItemStack stack)
    {
        boolean flag = stack != null && stack.isItemEqual(this.ironGeneratorItemStacks[index]) && ItemStack.areItemStackTagsEqual(stack, this.ironGeneratorItemStacks[index]);
        this.ironGeneratorItemStacks[index] = stack;

        if (stack != null && stack.stackSize > this.getInventoryStackLimit())
        {
            stack.stackSize = this.getInventoryStackLimit();
        }
    }

    public int getInventoryStackLimit()
    {
        return 64;
    }

    public boolean isUseableByPlayer(EntityPlayer player)
    {
        return this.worldObj.getTileEntity(this.pos) != this ? false : player.getDistanceSq((double)this.pos.getX() + 0.5D, (double)this.pos.getY() + 0.5D, (double)this.pos.getZ() + 0.5D) <= 64.0D;
    }

    public void openInventory(EntityPlayer player)
    {
    }

    public void closeInventory(EntityPlayer player)
    {
    }

    public boolean isItemValidForSlot(int index, ItemStack stack)
    {
        if (index == 2)
        {
            return false;
        }
        else if (index != 1)
        {
            return true;
        }
        else
        {
            ItemStack itemstack = this.ironGeneratorItemStacks[1];
            return isItemFuel(stack) || SlotFurnaceFuel.isBucket(stack) && (itemstack == null || itemstack.getItem() != Items.BUCKET);
        }
    }

    public int getField(int id) {
        switch (id) {
            case 0:
                return this.ironGeneratorBurnTime;
            case 1:
                return this.currentItemBurnTime;
            case 2:
                return this.cookTime;
            case 3:
                return this.totalCookTime;
            default:
                return 0;
        }
    }

    public void setField(int id, int value)
    {
        switch (id)
        {
            case 0:
                this.ironGeneratorBurnTime = value;
                break;
            case 1:
                this.currentItemBurnTime = value;
                break;
            case 2:
                this.cookTime = value;
                break;
            case 3:
                this.totalCookTime = value;
        }
    }

    public int getFieldCount()
    {
        return 4;
    }

    public void clear()
    {
        for (int i = 0; i < this.ironGeneratorItemStacks.length; ++i)
        {
            this.ironGeneratorItemStacks[i] = null;
        }
    }

    public int[] getSlotsForFace(EnumFacing side)
    {
        return side == EnumFacing.DOWN ? SLOTS_BOTTOM : (side == EnumFacing.UP ? SLOTS_TOP : SLOTS_SIDES);
    }

    public boolean canInsertItem(int index, ItemStack itemStackIn, EnumFacing direction)
    {
        return this.isItemValidForSlot(index, itemStackIn);
    }

    public boolean canExtractItem(int index, ItemStack stack, EnumFacing direction)
    {
        if (direction == EnumFacing.DOWN && index == 1)
        {
            Item item = stack.getItem();

            if (item != Items.WATER_BUCKET && item != Items.BUCKET)
            {
                return false;
            }
        }

        return true;
    }
}

かまどについての実装が書かれているTileEntityFurnaceを参考に実装していきました。「このinterfaceをimplementsするならこのメソッドをちゃんと定義しろ」などといろいろ怒られるので赤線が消えるまで根気良く続けます。
内部クラスを取り除いたIronGeneratorのコードを以下に示します。

IronGenerator.java
public class IronGenerator extends BlockContainer {
    public IronGenerator() {
        super(Material.IRON);
        setCreativeTab(CreativeTabs.BUILDING_BLOCKS);
        setRegistryName("iron_generator");
        setUnlocalizedName("iron_generator");
        setHardness(300);
    }

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

    @Nullable
    @Override
    public TileEntityLockable createNewTileEntity(World worldln, int meta) {
        return new IronGeneratorTileEntity();
    }
}

ContainerIronGeneratorの実装

IronGeneratorTileEntitycreateContainerメソッドでは、ContainerIronGeneratorのインスタンスを返しています。かまど等の内部実装を見ていると、Containerなるものが必要らしいので実装していきます。

ContainerIronGenerator.java
public class ContainerIronGenerator extends Container {
    private final IInventory tileIronGenerator;

    public ContainerIronGenerator(InventoryPlayer playerInventory, IInventory ironGeneratorInventory)
    {
        this.tileIronGenerator = playerInventory;
    }

    public boolean canInteractWith(EntityPlayer playerIn)
    {
        return this.tileIronGenerator.isUseableByPlayer(playerIn);
    }
}

エラーが出ない最低限度の実装をしました。

起動してみる

ブロックとレシピを登録します。コードは途中省略しています。

Mod.java
@Mod
public class GeneratorMod
{
    public static Block blockIronGenerator = new IronGenerator();

    @EventHandler
    public void preInit(FMLPreInitializationEvemt event)
    {
        registerBlock(blockIronGenerator, isClient);
        registerRecipe();
    }

    private void registerRecipe()
    {
        GameRegistry.addRecipe(new ItemStack(blockIronGenerator),
            "AAA",
            "ABA",
            "AAA",
            'A', new ItemStack(Items.IRON_INGOT),
            'B', new ItemStack(Items.COAL));
    {

    private void registerBlock(Block block, boolean isClient) {
        ItemBlock itemBlockInput = new ItemBlock(block);

        GameRegistry.register(block);
        GameRegistry.register(itemBlockInput, block.getRegistryName());

        if (isClient) {
            ModelResourceLocation modelName = new ModelResourceLocation(block.getRegistryName(), "inventory");
            ModelLoader.setCustomModelResourceLocation(itemBlockInput, 0, modelName);
        }
    }
}

起動してみます。

クラッシュせずにクラフトと設置ができました。ただし現段階ではテクスチャすら貼っていないただのブロックで、工業要素はどこにもありません。

最後に

ChatGPTはMinecraftのMODを作るのはあまり得意ではないようですが、それでも最初にいきなりそれっぽいコードを出してきたので驚きました。
所謂工業MODのようにするにはGUI(かまどとか醸造台とかで出てくるようなやつ)を実装しないといけないのですが、それを実装した経験はまだありません。春休みあたりに気分が乗れば実装してみようと思います。やるならどのバージョンがいいんでしょうか?MOD界隈で今主流のバージョンっていくつなのでしょうか?

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?