Modding:Функциональные Предметы
Эта страница проверялась в последний раз для версии 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));
}
}
Это должно быть отмечено как синтаксическая ошибка, потому что класса TunnlerItem
пока не существует.
Класс Предмета
При наименовании скриптов предметов рекомендуется называть их в формате "{Name}Item". В случае с тоннельной киркой мы назовем наш скрипт TunnlerItem.cs
. Любой itemclass должен расширять Item, предоставляя ему необходимую нам функциональность:
public class TunnlerItem : Item
{
}
Это должно решить все синтаксические ошибки.
Так что же должен делать наш инструмент? Когда игрок добывает блок с помощью этого инструмента, все блоки вокруг него также должны быть добыты.
Как обычно, мы можем обратиться к item api docs, чтобы найти функцию, которую мы можем использовать. Хотя сам класс item не содержит соответствующей функции, мы также можем обратиться к CollectibleObject api docs, от которого класс item расширяется.
В нашем конкретном случае мы можем переопределить метод bool OnBlockBrokenWith(IWorldAccessor world, Entity byEntity, ItemSlot itemslot, BlockSelection blockSel, float dropQuantityMultiplier = 1)
.
Нам нужно знать, с какой стороны стоит игрок (на какую сторону он ориентируется) и находится ли он в творческом режиме или режиме выживания (нужно ли бросать предметы или нет). Прежде чем переопределять OnBlockBrokenWith
, мы должны создать метод, который уничтожает все блоки между двумя позициями блока (min и max). Он также должен сбрасывать предметы, только если игрок находится в режиме выживания:
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);
}
}
}
}
Теперь мы можем реализовать OnBlockBroken
довольно легко, позаботившись обо всех возможных осях, с которыми может столкнуться игрок:
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;
}
Если вы все сделали правильно, ваш файл должен выглядеть примерно так:
using Vintagestory.API.Common;
using Vintagestory.API.Common.Entities;
using Vintagestory.API.MathTools;
namespace ExampleMods
{
public class TunnlerMod : ModSystem
{
public override void Start(ICoreAPI api)
{
base.Start(api);
api.RegisterItemClass("tunnler", typeof(TunnlerItem));
}
}
public class TunnlerItem : Item
{
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);
}
}
}
}
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;
}
}
}
Вы также можете скачать файл напрямую: Tunnler.cs.
Тестирование
Вот как это выглядит в игре:
Распределение
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:
- for VS v1.9: Tunnler_vs1.9_v1.0.0.zip
- for VS v1.8: Tunnler.zip
Wondering where some links have gone?
The modding navbox is going through some changes! Check out Navigation Box Updates for more info and help finding specific pages.
Modding | |
---|---|
Modding Introduction | Getting Started • Пакет тем |
Content Modding | Content Mods • Developing a Content Mod • Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Content Mod Concepts |
Code Modding | Code Mods • Setting up your Development Environment |
Property Overview | Item • Entity • Block • Block Behaviors • Block Classes • Block Entities • Block Entity Behaviors • World properties |
Workflows & Infrastructure | Modding Efficiency Tips • Mod-engine compatibility • Mod Extensibility • VS Engine |
Additional Resources | Community Resources • Modding API Updates • Programming Languages • List of server commands • List of client commands • Client startup parameters • Server startup parameters Example Mods • API Docs • GitHub Repository |