Modding:Advanced Items/ru: Difference between revisions

From Vintage Story Wiki
No edit summary
No edit summary
Tags: Mobile edit Mobile web edit
Line 25: Line 25:
Создание нашего элемента требует создания пары новых файлов <code>*.cs</code> в нашем проекте.
Создание нашего элемента требует создания пары новых файлов <code>*.cs</code> в нашем проекте.


Если вы уже читали руководство - [[Advanced Blocks]], это должно быть вам знакомо.
Если вы уже читали руководство - [[тест]], это должно быть вам знакомо.


=== Система Модов ===
=== Система Модов ===

Revision as of 15:52, 24 March 2024

Other languages:


Эта страница проверялась в последний раз для версии Vintage Story 1.19.3.


Это руководство по код моду требует наличия среды разработки. Если у вас её нет, перейдите на страницу Настройка среды разработки и следуйте инструкции. Также настоятельно рекомендуется прочитать руководство и закончить создание Простого предмета.

Создание Тоннельной Кирки

В этом уроке мы создадим предмет с более продвинутой функциональностью: Кирка, которая позволяет вырыть туннель 3x3, добыв всего один блок.

Активы Предмета

Как и в случае с Простым Предметом, нам нужно создать активы (ассеты) для нашего предмета, включая тип предмета, текстуру и lang-файл. Эти активы довольно просты, и вы можете скачать их здесь. Распакуйте файл в папку mods, и вы готовы приступить к программированию.

В json вашего itemtype есть только одно новое свойство, class. Это свойство указывает нашему новому элементу, что он будет управляться определенным классом C#.

	class: "tunnler",

Мы создадим этот класс, чтобы придать элементу желаемую функциональность.

Класс Предмета

Создание нашего элемента требует создания пары новых файлов *.cs в нашем проекте.

Если вы уже читали руководство - тест, это должно быть вам знакомо.

Система Модов

Для того чтобы зарегистрировать класс предмета, нам нужно создать мод, который представляет собой класс, расширяющий ModSystem:

public class TunnlerMod : ModSystem
{
    
}

Переопределив метод Start(ICoreAPI), мы можем зарегистрировать наш класс. Функция RegisterItemClass имеет два параметра: первый - идентификатор класса элемента, поскольку именно так мы будем ссылаться на этот класс в наших json-файлах itemtype. Убедитесь, что он идентичен классу, который мы указали в нашем предыдущем файле активов. Второй параметр - тип нашего элемента класса.

public class TunnlerMod : ModSystem
{
    public override void Start(ICoreAPI api)
    {
        base.Start(api);
        api.RegisterItemClass("tunnler", typeof(TunnlerItem));
    }
}

This should be marked as a syntax error because there is no TunnlerItem class yet.

The Item Class

When naming item scripts, it is recommended to name them in the format "{Name}Item". In the case of the tunnler pickaxe, we shall name our script TunnlerItem.cs. Any itemclass has to extend Item, giving it the functionality we need to access:

public class TunnlerItem : Item
{
</div>

<div lang="en" dir="ltr" class="mw-content-ltr">
}

This should solve all syntax errors.


So what should our tool do? Once the player mines a block with this tool every block around it should be mined as well.

As always, we can refer to the item api docs to find a function we can use. Although the item class itself does not contain an appropriate function, we can also check the CollectibleObject api docs, which the item class extends from.

In our specific case, we can override the method bool OnBlockBrokenWith(IWorldAccessor world, Entity byEntity, ItemSlot itemslot, BlockSelection blockSel, float dropQuantityMultiplier = 1).

We need to be aware of the facing (which side the player is focusing) and if the player is in creative or survival mode (whether items should be dropped or not). Before we are going to override OnBlockBrokenWith we should create a method which destroys all blocks between two block positions (min & max). It should also only drop items if the player is in survival mode:

public void DestroyBlocks(IWorldAccessor world, BlockPos min, BlockPos max, IPlayer player)
{
    BlockPos tempPos = new BlockPos();
    for (int x = min.X; x <= max.X; x++)
    {
        for (int y = min.Y; y <= max.Y; y++)
        {
            for (int z = min.Z; z <= max.Z; z++)
            {
                tempPos.Set(x, y, z);
                if (player.WorldData.CurrentGameMode == EnumGameMode.Creative)
                    world.BlockAccessor.SetBlock(0, tempPos);
                else
                    world.BlockAccessor.BreakBlock(tempPos, player);
            }
        }
    }
}

Now we can implement OnBlockBroken rather easily, by taken care of every possible axis the player could face:

public override bool OnBlockBrokenWith(IWorldAccessor world, Entity byEntity, ItemSlot itemslot, BlockSelection blockSel, float dropQuantityMultiplier = 1)
{
    if (base.OnBlockBrokenWith(world, byEntity, itemslot, blockSel))
    {
        if (byEntity is EntityPlayer)
        {
            IPlayer player = world.PlayerByUid((byEntity as EntityPlayer).PlayerUID);
            switch (blockSel.Face.Axis)
            {
                case EnumAxis.X:
                    destroyBlocks(world, blockSel.Position.AddCopy(0, -1, -1), blockSel.Position.AddCopy(0, 1, 1), player);
                    break;
                case EnumAxis.Y:
                    destroyBlocks(world, blockSel.Position.AddCopy(-1, 0, -1), blockSel.Position.AddCopy(1, 0, 1), player);
                    break;
                case EnumAxis.Z:
                    destroyBlocks(world, blockSel.Position.AddCopy(-1, -1, 0), blockSel.Position.AddCopy(1, 1, 0), player);
                    break;
            }
        }
        return true;
    }
    return false; 
}

If you have done everything right, your file should look similar to this:

using Vintagestory.API.Common;
using Vintagestory.API.Common.Entities;
using Vintagestory.API.MathTools;
</div>

<div lang="en" dir="ltr" class="mw-content-ltr">
namespace ExampleMods
{
    public class TunnlerMod : ModSystem
    {
</div>

        <div lang="en" dir="ltr" class="mw-content-ltr">
public override void Start(ICoreAPI api)
        {
            base.Start(api);
            api.RegisterItemClass("tunnler", typeof(TunnlerItem));
        }
</div>

    <div lang="en" dir="ltr" class="mw-content-ltr">
}
</div>

    <div lang="en" dir="ltr" class="mw-content-ltr">
public class TunnlerItem : Item
    {
</div>

        <div lang="en" dir="ltr" class="mw-content-ltr">
public void DestroyBlocks(IWorldAccessor world, BlockPos min, BlockPos max, IPlayer player)
        {
            BlockPos tempPos = new BlockPos();
            for (int x = min.X; x <= max.X; x++)
            {
                for (int y = min.Y; y <= max.Y; y++)
                {
                    for (int z = min.Z; z <= max.Z; z++)
                    {
                        tempPos.Set(x, y, z);
                        if (player.WorldData.CurrentGameMode == EnumGameMode.Creative)
                            world.BlockAccessor.SetBlock(0, tempPos);
                        else
                            world.BlockAccessor.BreakBlock(tempPos, player);
                    }
                }
            }
        }
</div>

        <div lang="en" dir="ltr" class="mw-content-ltr">
public override bool OnBlockBrokenWith(IWorldAccessor world, Entity byEntity, ItemSlot itemslot, BlockSelection blockSel, float dropQuantityMultiplier = 1)
        {
            if (base.OnBlockBrokenWith(world, byEntity, itemslot, blockSel))
            {
                if (byEntity is EntityPlayer)
                {
                    IPlayer player = world.PlayerByUid((byEntity as EntityPlayer).PlayerUID);
                    switch (blockSel.Face.Axis)
                    {
                        case EnumAxis.X:
                            destroyBlocks(world, blockSel.Position.AddCopy(0, -1, -1), blockSel.Position.AddCopy(0, 1, 1), player);
                            break;
                        case EnumAxis.Y:
                            destroyBlocks(world, blockSel.Position.AddCopy(-1, 0, -1), blockSel.Position.AddCopy(1, 0, 1), player);
                            break;
                        case EnumAxis.Z:
                            destroyBlocks(world, blockSel.Position.AddCopy(-1, -1, 0), blockSel.Position.AddCopy(1, 1, 0), player);
                            break;
                    }
                }
                return true;
            }
            return false; 
        }
</div>

    <div lang="en" dir="ltr" class="mw-content-ltr">
}
}
</div>

<div lang="en" dir="ltr" class="mw-content-ltr">

You can also download the file directly: Tunnler.cs.

Testing

This is how it looks ingame:

Distribution

Using the new Mod Template

If using the mod template setup, follow the instructions on Setting up your Development Environment to pack and distribute your mod.

Using the (old) Modtools

If using the modtools program, open the modtools and type in pack <your mod id>. Now you can take the zip file and share it with other people. It will work in the same way as ordinary mods, you can install it by copying it into the mods folder.

Mod Download

Here is my version: