Пользовательский блок хранения

Перевод Пользовательский блок хранения

Версия(и) Minecraft
1.7.10
Источник
https://emxtutorials.wordpress.com/custom-storage-block/
Пользовательский блок хранения

Я чувствую, что есть огромная нехватка учебников, которая охватывает это в удобном для чтения формате. Я сам действительно пытался написать свой первый код для блока хранения. После большого количества проб и ошибок, я, наконец, сделал это, и я решил поделиться им, с вами, ребята.

Я сделаю новый блок, он будет иметь те же свойства, что и сундук, за исключением того, что он имеет только 15 слотов. Я буду делиться текстурой графического интерфейса, но я не буду выдавать фактические текстуры блоков, чтобы дать как можно больше места для манёвров/кастомизаций, чтобы вы действительно создали свой собственный блок. Давайте начнем.

Сначала создайте блок. У меня не будет никаких текстур, как я уже говорил, но вы должны поместить свои собственные текстуры в свой проект. Я также добавлю дополнительный код, который я объясню через минуту,

Java:
public class BlockStorage extends BlockContainer
{
    private static final String name = "storage";

    private final Random rand = new Random();

    public BlockStorage()
    {
        super(Material.wood);
        GameRegistry.registerBlock(this, name);
        setBlockName(name);
        setCreativeTab(CreativeTabs.tabDecorations);
    }

    @Override
    public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float lx, float ly, float lz)
    {
        if (world.isRemote) return true;

        TileEntity te = world.getTileEntity(x, y, z);
        if (te != null && te instanceof TileEntityStorage)
        {
            player.openGui(StorageMod.instance, 0, world, x, y, z);
            return true;
        }
        return false;
    }

    @Override
    public void breakBlock(World world, int x, int y, int z, Block block, int par6)
    {
        if (world.isRemote) return;

        ArrayList drops = new ArrayList();

        TileEntity teRaw = world.getTileEntity(x, y, z);

        if (teRaw != null && teRaw instanceof TileEntityStorage)
        {
            TileEntityStorage te = (TileEntityStorage) teRaw;

            for (int i = 0; i < te.getSizeInventory(); i++)
            {
                ItemStack stack = te.getStackInSlot(i);

                if (stack != null) drops.add(stack.copy());
            }
        }

        for (int i = 0;i < drops.size();i++)
        {
            EntityItem item = new EntityItem(world, x + 0.5, y + 0.5, z + 0.5, drops.get(i));
            item.setVelocity((rand.nextDouble() - 0.5) * 0.25, rand.nextDouble() * 0.5 * 0.25, (rand.nextDouble() - 0.5) * 0.25);
            world.spawnEntityInWorld(item);
        }
    }

    public TileEntity createNewTileEntity(World world, int par2)
    {
        return new TileEntityStorage();
    }
}

Здесь вы можете увидеть, что я унаследовал BlockContainer и добавил createNewTileEntity (). После этого я создал класс сущности элемента. Я добавил несколько дополнительных методов: onBlockActivated и breakBlock.

onBlockActivated () служит для указания «Minecraft» , что щелкнув правой кнопкой мыши по блоку откроется графический интерфейс, и «активируется» блок. В основном это несколько проверок, а затем, наконец, вызов openGui. Он имеет несколько параметров, первый экземпляр мод, мой - StorageMod.instance, это должен быть экземпляр под аннотацией @Mod.Instance или @Instance в нашем основном классе мода. Следующий параметр - ноль (zero/0), это ваш идентификатор графического интерфейса. Это зависит от совместимости модификаций, поэтому вам не придется беспокоиться об этом, сталкиваясь с другими модами или ванильным «Minecraft». Это должно быть одинаковым во всех классах графического интерфейса блока, поэтому помните об этом. Или, если хотите, вы можете перечислить его и использовать ordinal () для получения целого числа. Но я не буду освещать это в этом уроке.

breakBlock () дает возможность выбрасывать элементы внутри блока, если вы его сломаете, чтобы не потерять элементы. Это в основном копирование и вставка из кода ванили.

Вы получите несколько ошибок в этом коде, но это потому, что у нас пока нет класса TileEntityStorage. Теперь пришло время сделать класс Tile Entity.

Java:
public class TileEntityStorage extends TileEntity implements IInventory
{
    private ItemStack[] items = new ItemStack[15];

    public int getSizeInventory()
    {
        return items.length;
    }

    public ItemStack getStackInSlot(int slot)
    {
        return items[slot];
    }

    public ItemStack decrStackSize(int slot, int amount)
    {
        if (items[slot] != null)
        {
            ItemStack itemstack;

            if (items[slot].stackSize == amount)
            {
                itemstack = items[slot];
                items[slot] = null;
                markDirty();
                return itemstack;
            }
            else
            {
                itemstack = items[slot].splitStack(amount);
                if (items[slot].stackSize == 0) items[slot] = null;
                markDirty();
                return itemstack;
            }
        }
        else
        {
            return null;
        }
    }

    public ItemStack getStackInSlotOnClosing(int slot)
    {
        if (items[slot] != null)
        {
            ItemStack itemstack = items[slot];
            items[slot] = null;
            return itemstack;
        }
        else
        {
            return null;
        }
    }

    public void setInventorySlotContents(int slot, ItemStack stack)
    {
        items[slot] = stack;
        if (stack != null && stack.stackSize > getInventoryStackLimit())
        {
            stack.stackSize = getInventoryStackLimit();
        }

        markDirty();
    }

    public String getInventoryName()
    {
        return "container.storage";
    }

    public boolean hasCustomInventoryName()
    {
        return false;
    }

    @Override
    public void readFromNBT(NBTTagCompound nbt)
    {
        super.readFromNBT(nbt);
        NBTTagList list = nbt.getTagList("Items", Constants.NBT.TAG_COMPOUND);
        items = new ItemStack[getSizeInventory()];

        for (int i = 0; i < list.tagCount(); ++i) { NBTTagCompound comp = list.getCompoundTagAt(i); int j = comp.getByte("Slot") & 255; if (j >= 0 && j < items.length)
            {
                items[j] = ItemStack.loadItemStackFromNBT(comp);
            }
        }
    }

    @Override
    public void writeToNBT(NBTTagCompound nbt)
    {
        super.writeToNBT(nbt);
        NBTTagList list = new NBTTagList();

        for (int i = 0; i < items.length; ++i)
        {
            if (items[i] != null)
            {
                NBTTagCompound comp = new NBTTagCompound();
                comp.setByte("Slot", (byte)i);
                items[i].writeToNBT(comp);
                list.appendTag(comp);
            }
        }

        nbt.setTag("Items", list);
    }

    public int getInventoryStackLimit()
    {
        return 64;
    }

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

    public void openInventory() {}

    public void closeInventory() {}

    public boolean isItemValidForSlot(int slot, ItemStack stack)
    {
        return true;
    }
}

Большинство этих кодов просто копируются и вставляются из кода ванили с некоторыми изменениями. Я еще не реализовал пользовательские имена инвентаря, которые могут появиться в другом учебнике. На данный момент имя gui всегда будет «container.storage», поэтому вы можете добавить это в свой файл с локализацией.

Теперь давайте начнем возиться с файлами графического интерфейса. Но прежде чем делать графический интерфейс, нам нужен класс для нашего мода для обработки графических интерфейсов, а «Forge» предоставляет интерфейс для так называемого IGuiHandler.

Java:
public class GuiHandler implements IGuiHandler
{
    public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z)
    {
        TileEntity te = world.getTileEntity(x, y, z);

        if (te != null)
        {
            if (ID == 0) //ID графического интерфейса для блока хранения, добавят позже
            {
                return new ContainerStorage((TileEntityStorage)te, player);
            }
        }
        return null;
    }

    public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z)
    {
        TileEntity te = world.getTileEntity(x, y, z);

        if (te != null)
        {
            if (ID == 0) //ID графического интерфейса для блока хранения, добавят позже
            {
                return new GuiStorage((TileEntityStorage)te, player);
            }
        }
        return null;
    }
}

Это наш класс GuiHandler. Mы должны зарегистрировать это в своем CommonProxy с помощью этой строки:

Java:
NetworkRegistry.INSTANCE.registerGuiHandler(StorageMod.instance, new GuiHandler());

Кроме того, обратите внимание, что чем больше графических интерфейсов вы добавляете, вы также не должны забывать их зарегистрировать в своем классе GuiHandler с соответствующими идентификаторами графического интерфейса.

Теперь, давайте наконец сделаем графический интерфейс нашего блока хранения.

Java:
public class GuiStorage extends GuiContainer
{
    private ResourceLocation texture = new ResourceLocation(StorageMod.MODID, "textures/gui/container/storage.png");

    private InventoryPlayer inventory;
    private TileEntityStorage te;

    public GuiStorage(TileEntityStorage te, EntityPlayer player)
    {
        super(new ContainerStorage(te, player));
        inventory = player.inventory;
        this.te = te;
    }

    @Override
    protected void drawGuiContainerBackgroundLayer(float par1, int par2, int par3)
    {
        Minecraft.getMinecraft().renderEngine.bindTexture(texture);

        GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);

        int x = (width - xSize) / 2;
        int y = (height - ySize) / 2;

        drawTexturedModalRect(x, y, 0, 0, xSize, ySize);
    }

    @Override
    protected void drawGuiContainerForegroundLayer(int par1, int par2)
    {
        fontRendererObj.drawString(I18n.format(te.getInventoryName()), (xSize / 2) - (fontRendererObj.getStringWidth(I18n.format(te.getInventoryName())) / 2), 6, 4210752, false);
        fontRendererObj.drawString(I18n.format(inventory.getInventoryName()), 8, ySize - 96 + 2, 4210752);
    }
}

drawGuiContainerBackgroundLayer () рисует наш реальный фон, поэтому нужен только один прямоугольник. drawGuiContainerForegroundLayer () рисует любые другие вещи, такие как текст. Две строки в этом методе добавляют имя графического интерфейса и "инвентарь" на инвентаре игрока.

Если вы хотите текстуру графического интерфейса, то вот она:

DcHvLIy.png

Теперь давайте сделаем сам контейнер, где он регистрирует слоты графического интерфейса.

Java:
public class ContainerStorage extends Container
{
    private TileEntityStorage te;

    private int slotID = 0;

    public ContainerStorage(TileEntityStorage te, EntityPlayer player)
    {
        this.te = te;

        //Хранение
        for (int i = 0; i < 3; i++)
        {
            for (int j = 0; j < 5; j++)
            {
                addSlotToContainer(new Slot(te, slotID++, 44 + j * 18, 17 + i * 18));
            }
        }

        //Инвентарь
        for (int i = 0; i < 3; i++)
        {
            for (int j = 0; j < 9; j++)
            {
                addSlotToContainer(new Slot(player.inventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18));
            }
        }
        //Хотбар
        for (int i = 0; i < 9; i++)
        {
            addSlotToContainer(new Slot(player.inventory, i, 8 + i * 18, 142));
        }
    }

    @Override
    public ItemStack transferStackInSlot(EntityPlayer player, int slotRaw)
    {
        ItemStack stack = null;
        Slot slot = (Slot)inventorySlots.get(slotRaw);

        if (slot != null && slot.getHasStack())
        {
            ItemStack stackInSlot = slot.getStack();
            stack = stackInSlot.copy();

            if (slotRaw < 3 * 9)
            {
                if (!mergeItemStack(stackInSlot, 3 * 9, inventorySlots.size(), true))
                {
                    return null;
                }
            }
            else if (!mergeItemStack(stackInSlot, 0, 3 * 9, false))
            {
                return null;
            }

            if (stackInSlot.stackSize == 0)
            {
                slot.putStack((ItemStack)null);
            }
            else
            {
                slot.onSlotChanged();
            }
        }
        return stack;
    }

    @Override
    public boolean canInteractWith(EntityPlayer player)
    {
        return te.isUseableByPlayer(player);
    }
}

Опять же, как обычно, это в основном копия и вставка кода из ванили с изменениями.
Ниже
// Хранение комментариев, где я регистрирую слоты, у меня 5 в широну и 3 в высоту. Если вам нужен другой размер слотов, вы можете изменить числа в цикле for.

Вот и все готово! Это довольно длинный учебник, так что если кто-то замечает какие-либо неправильные или неполные данные в учебнике, пожалуйста, добавьте свой комментарий ниже!
Автор
Garik
Просмотры
1,325
Первый выпуск
Обновление
Оценка
4.00 звёзд 3 оценок

Другие ресурсы пользователя Garik

Последние рецензии

Довольно хорошо, а главное нормальный перевод.
Просто хороший человек)
Спасибо за помощь!
Сверху