Modding:Advanced Items: Difference between revisions

From Vintage Story Wiki
No edit summary
m (Updated navbox to new code navbox.)
 
(10 intermediate revisions by 5 users not shown)
Line 1: Line 1:
<languages/>
<!--T:1-->
__FORCETOC__
__FORCETOC__
''Mod written for version 1.9 of Vintage Story''
<translate>
<!--T:47-->
{{GameVersion|1.19.3}}
</translate>


It's highly recommended to read [[Basic Item]] first. Additionally this tutorial requires a development environment. If you don't have one already you should read the tutorial [[Setting up your Development Environment]].
<!--T:2-->
<translate>
<!--T:48-->
This ''code mod'' tutorial requires a development environment. If you do not have one, please read and complete the [[Setting up your Development Environment]] page. It is also highly recommended to have read and completed the [[Basic Item]] tutorial.
</translate>


= Tunnel pickaxe =
<translate>
= Creating a Tunnel Pickaxe = <!--T:49-->
</translate> <!--T:3-->


Let's create an item, which allows you to dig a 3x3 tunnel by mining just one block.
<!--T:4-->
<translate>
<!--T:50-->
In this tutorial will we create a item with more advanced functionality: A pickaxe which allows you to dig a 3x3 tunnel by mining just one block.
</translate>


== Item Assets ==
<translate>
== Item Assets == <!--T:51-->
</translate> <!--T:5-->


First of all we need to create a new item, including a texture and a lang file. Those assets are pretty straight forward and you can download them [https://wiki.vintagestory.at/images/c/cd/Tunnler_-_No_CS_File.zip here]. Just place the file in your mods folder and you are ready to start programming.
<!--T:6-->
<translate>
<!--T:52-->
Similar to the [[Basic Item]], we need to create our item's assets, including an itemtype, texture and a lang file. Those assets are pretty straight forward and you can download them [https://wiki.vintagestory.at/images/c/cd/Tunnler_-_No_CS_File.zip here]. Extract the file to your mods folder, and you are ready to start programming.
</translate>


There is only one new property in your json item file called <code>class</code>:
<!--T:7-->
<translate>
<!--T:53-->
There is only one new property in your itemtype json, <code>class</code>. This property tells our new item to be controlled by a certain C# class.
</translate>
<syntaxhighlight lang="json">
<syntaxhighlight lang="json">
class: "tunnler",
class: "tunnler",
</syntaxhighlight>
</syntaxhighlight>


We will create this class, to give the item the desired functionality, so if you pick a different name make sure it matches the one below.
<!--T:8-->
<translate>
<!--T:54-->
We will create this class to give the item the desired functionality.
</translate>


== The Item Class ==
<translate>
== The Item Class == <!--T:55-->
</translate> <!--T:9-->


If you have read the [[Advanced Blocks]] Tutorial already, this should be fimilar to you.
<!--T:10-->
<translate>
<!--T:56-->
Creating our item requires a couple new <code>*.cs</code> files in our project.
</translate>


In order to register your item class, we need to create a mod, which is basically a class exendting ModSystem:
<!--T:11-->
<translate>
<!--T:57-->
If you have read the [[Advanced Blocks]] Tutorial already, this should be familar to you.
</translate>


<translate>
=== The Mod System === <!--T:58-->
</translate> <!--T:12-->
<translate>
<!--T:59-->
In order to register your item class, we need to create a mod, which is a class extending ModSystem:
</translate>
<!--T:13-->
<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
public class TunnlerMod : ModSystem
public class TunnlerMod : ModSystem
Line 32: Line 80:
</syntaxhighlight>
</syntaxhighlight>


By overriding the <code>Start(ICoreAPI)</code> method, we can register our class. Remember if you have picked a different class name you have to use that one instead.
<!--T:14-->
<translate>
<!--T:60-->
By overriding the <code>Start(ICoreAPI)</code> method, we can register our class. The <code>RegisterItemClass</code> function has two parameters: the first is our item class ID, noteably this is how we link to this class in our itemtype json files. Ensure that this is identical to the class we specified in our earlier asset file. The second parameter is the type of our item class.
</translate>


<!--T:15-->
<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
public class TunnlerMod : ModSystem
public class TunnlerMod : ModSystem
Line 45: Line 98:
</syntaxhighlight>
</syntaxhighlight>


<!--T:16-->
<translate>
<!--T:61-->
This should be marked as a syntax error because there is no <code>TunnlerItem</code> class yet.
This should be marked as a syntax error because there is no <code>TunnlerItem</code> class yet.
</translate>


=== The Item Class ===
<translate>
=== The Item Class === <!--T:62-->
</translate> <!--T:17-->


Let's create our item class itself which of course has to extend <code>Item</code>:
<!--T:18-->
<translate>
<!--T:63-->
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 <code>TunnlerItem.cs</code>. Any itemclass has to extend Item, giving it the functionality we need to access:
</translate>


<!--T:19-->
<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
public class TunnlerItem : Item
public class TunnlerItem : Item
Line 58: Line 122:
</syntaxhighlight>
</syntaxhighlight>


<!--T:21-->
<translate>
<!--T:64-->
This should solve all syntax errors.
This should solve all syntax errors.
</translate>
<!--T:22-->
<translate>
<!--T:65-->
'''So what should our tool do?''' Once the player mines a block with this tool every block around it should be mined as well.
</translate>
<!--T:23-->
<translate>
<!--T:66-->
As always, we can refer to the [https://apidocs.vintagestory.at/api/Vintagestory.API.Common.Item.html#methods 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 [https://apidocs.vintagestory.at/api/Vintagestory.API.Common.CollectibleObject.html CollectibleObject api docs], which the item class extends from.
</translate>


'''So what should our tool do?''' Once the player mines a block with this tool every block around it should be mined as well. This is rather easy to implement by overriding the method <code>void OnBlockBroken(IWorldAccessor world, IEntity byEntity, IItemSlot itemslot, BlockSelection blockSel)</code>.
<!--T:24-->
<translate>
<!--T:67-->
In our specific case, we can override the method <code>bool OnBlockBrokenWith(IWorldAccessor world, Entity byEntity, ItemSlot itemslot, BlockSelection blockSel, float dropQuantityMultiplier = 1)</code>.
</translate>


We need to take care 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 OnBlockBroken 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:
<!--T:25-->
<translate>
<!--T:68-->
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 <code>OnBlockBrokenWith</code> 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:
</translate>


<!--T:26-->
<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
public void destroyBlocks(IWorldAccessor world, BlockPos min, BlockPos max, IPlayer player)
public void DestroyBlocks(IWorldAccessor world, BlockPos min, BlockPos max, IPlayer player)
{
{
     BlockPos tempPos = new BlockPos();
     BlockPos tempPos = new BlockPos();
Line 85: Line 175:
</syntaxhighlight>
</syntaxhighlight>


<!--T:27-->
<translate>
<!--T:69-->
Now we can implement <code>OnBlockBroken</code> rather easily, by taken care of every possible axis the player could face:
Now we can implement <code>OnBlockBroken</code> rather easily, by taken care of every possible axis the player could face:
</translate>
<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
public override bool OnBlockBrokenWith(IWorldAccessor world, Entity byEntity, ItemSlot itemslot, BlockSelection blockSel)
public override bool OnBlockBrokenWith(IWorldAccessor world, Entity byEntity, ItemSlot itemslot, BlockSelection blockSel, float dropQuantityMultiplier = 1)
{
{
     if (base.OnBlockBrokenWith(world, byEntity, itemslot, blockSel))
     if (base.OnBlockBrokenWith(world, byEntity, itemslot, blockSel))
Line 113: Line 207:
</syntaxhighlight>
</syntaxhighlight>


 
<!--T:28-->
----
----


<!--T:29-->
<translate>
<!--T:70-->
If you have done everything right, your file should look similar to this:
If you have done everything right, your file should look similar to this:
</translate>
<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
using Vintagestory.API.Common;
using Vintagestory.API.Common;
Line 127: Line 225:
     {
     {


        public override void Start(ICoreAPI api)
public override void Start(ICoreAPI api)
         {
         {
             base.Start(api);
             base.Start(api);
Line 133: Line 231:
         }
         }


    }
}


    public class TunnlerItem : Item
public class TunnlerItem : Item
     {
     {


        public void destroyBlocks(IWorldAccessor world, BlockPos min, BlockPos max, IPlayer player)
public void DestroyBlocks(IWorldAccessor world, BlockPos min, BlockPos max, IPlayer player)
         {
         {
             BlockPos tempPos = new BlockPos();
             BlockPos tempPos = new BlockPos();
Line 157: Line 255:
         }
         }


        public override bool OnBlockBrokenWith(IWorldAccessor world, Entity byEntity, ItemSlot itemslot, BlockSelection blockSel)
public override bool OnBlockBrokenWith(IWorldAccessor world, Entity byEntity, ItemSlot itemslot, BlockSelection blockSel, float dropQuantityMultiplier = 1)
         {
         {
             if (base.OnBlockBrokenWith(world, byEntity, itemslot, blockSel))
             if (base.OnBlockBrokenWith(world, byEntity, itemslot, blockSel))
Line 182: Line 280:
         }
         }


    }
}
}
}


</syntaxhighlight>
</syntaxhighlight>


<!--T:38-->
<translate>
<!--T:71-->
You can also download the file directly: [https://wiki.vintagestory.at/images/a/ad/Tunnler.cs Tunnler.cs].
You can also download the file directly: [https://wiki.vintagestory.at/images/a/ad/Tunnler.cs Tunnler.cs].
</translate>


== Testing ==
<translate>
== Testing == <!--T:72-->
</translate> <!--T:39-->


<!--T:40-->
<translate>
<!--T:73-->
This is how it looks ingame:
This is how it looks ingame:
</translate>


<!--T:41-->
<youtube>2MRzYKguVFY</youtube>
<youtube>2MRzYKguVFY</youtube>


== Distribution ==
<translate>
== Distribution == <!--T:74-->
</translate> <!--T:42-->


In order to finish everything, open the modtools and type in <code>pack <your mod id></code>. 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 <code>mods</code> folder.
<translate>
=== Using the new Mod Template === <!--T:75-->
</translate> <!--T:43-->
<translate>
<!--T:76-->
If using the mod template setup, follow the instructions on [[Modding:Setting up your Development Environment#Packaging%20the%20Mod|Setting up your Development Environment]] to pack and distribute your mod.
</translate>


= Mod Download =
<translate>
=== Using the (old) Modtools === <!--T:77-->
</translate> <!--T:44-->
<translate>
<!--T:78-->
If using the modtools program, open the modtools and type in <code>pack <your mod id></code>. 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 <code>mods</code> folder.
</translate>
<translate>
= Mod Download = <!--T:79-->
</translate>


<!--T:45-->
<translate>
<!--T:80-->
Here is my version:  
Here is my version:  
</translate>
* for VS v1.9: [https://wiki.vintagestory.at/images/7/7b/Tunnler_vs1.9_v1.0.0.zip Tunnler_vs1.9_v1.0.0.zip]
* for VS v1.9: [https://wiki.vintagestory.at/images/7/7b/Tunnler_vs1.9_v1.0.0.zip Tunnler_vs1.9_v1.0.0.zip]
* for VS v1.8: [https://wiki.vintagestory.at/images/6/66/Tunnler.zip Tunnler.zip]
* for VS v1.8: [https://wiki.vintagestory.at/images/6/66/Tunnler.zip Tunnler.zip]


 
<!--T:46-->
{{Navbox/modding|Vintage Story}}
{{Navbox/codemodding}}

Latest revision as of 17:00, 27 March 2024

Other languages:

This page was last verified for Vintage Story version 1.19.3.


This code mod tutorial requires a development environment. If you do not have one, please read and complete the Setting up your Development Environment page. It is also highly recommended to have read and completed the Basic Item tutorial.

Creating a Tunnel Pickaxe

In this tutorial will we create a item with more advanced functionality: A pickaxe which allows you to dig a 3x3 tunnel by mining just one block.

Item Assets

Similar to the Basic Item, we need to create our item's assets, including an itemtype, texture and a lang file. Those assets are pretty straight forward and you can download them here. Extract the file to your mods folder, and you are ready to start programming.

There is only one new property in your itemtype json, class. This property tells our new item to be controlled by a certain C# class.

	class: "tunnler",

We will create this class to give the item the desired functionality.

The Item Class

Creating our item requires a couple new *.cs files in our project.

If you have read the Advanced Blocks Tutorial already, this should be familar to you.

The Mod System

In order to register your item class, we need to create a mod, which is a class extending ModSystem:

public class TunnlerMod : ModSystem
{
    
}

By overriding the Start(ICoreAPI) method, we can register our class. The RegisterItemClass function has two parameters: the first is our item class ID, noteably this is how we link to this class in our itemtype json files. Ensure that this is identical to the class we specified in our earlier asset file. The second parameter is the type of our item class.

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
{

}

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;

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; 
        }

}
}

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:


Icon Sign.png

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.