Modding:Code Tutorial Simple Item: Difference between revisions

From Vintage Story Wiki
mNo edit summary
 
(2 intermediate revisions by the same user not shown)
Line 44: Line 44:
To keep the code organized, you should create a new folder called "Items". Right click on your project, hover over add, and select ''New Folder''.  
To keep the code organized, you should create a new folder called "Items". Right click on your project, hover over add, and select ''New Folder''.  


You need a name for the new item class. These follow the same convention as the Blocks, and most other types. In this case, our thorny blade item class, you should name this class "ItemThornsBlade".  
You also need a name for the new item class. These follow the same convention as the Blocks, and most other types. In this case, our thorny blade item class, you should name this class "ItemThornsBlade".  


Create your new class. Right click the new folder, hover over ''Add'', and select ''Class''. Ensure you have the standard ''Class'' selected in the template list, enter your class name, and click create.
Create your new class. Right click the new folder, hover over ''Add'', and select ''Class''. Ensure you have the standard ''Class'' selected in the template list, enter your class name, and click create.


=== Registering the Block Class ===
The class you created needs to extend the 'Item' class. This will allow you to use many of the available functions for items. Change the class definition to now be: <syntaxhighlight lang="csharp">
The class for the trampoline class is created, however before it can be used it needs to be registered through the game API. When you created your mod, a ''mod system'' will have been automatically made. Look for it in your solution explorer, it should be called ''VSTutorialModSystem'', and double click it to open it.
internal class ItemThornsBlade : Item
</syntaxhighlight>Most of the time, Visual Studio will automatically add 'using' statements. However, if this errors, click on "Item", press Alt+Enter, and select the "using Vintagestory.API.Common" option.
[[File:VisualStudioModdingTutorialAutoErrorSolving.png|center|frameless|623x623px]]
The ItemThornsBlade class now exists, but it does nothing. Time to override a function, I suppose.


Your mod system will likely contain three functions at this point - ''Start, StartServerSide,'' and ''StartClientSide.'' The server and client side functions are not required here, so feel free to delete them or keep them in for the future. Either is fine.
Inside the empty class, type "override " and a list of methods we can use will be displayed. In this instance, you will want to override the 'OnAttackingWith' function. This function is called whenever our item is used to attack an entity. Search for this in the list and double click on it to automatically add the required code sample.
[[File:VisualStudioModdingTutorialOverrideOnAttackingWithFunction.png|center|frameless|767x767px]]
Your class should now look like this:
{| class="wikitable mw-collapsible mw-collapsed"
|ItemThornsBlade.cs
|-
|<syntaxhighlight lang="csharp">
using Vintagestory.API.Common;
using Vintagestory.API.Common.Entities;
 
namespace VSTutorial.Items
{
    internal class ItemThornsBlade : Item
    {
        public override void OnAttackingWith(IWorldAccessor world, Entity byEntity, Entity attackedEntity, ItemSlot itemslot)
        {
            base.OnAttackingWith(world, byEntity, attackedEntity, itemslot);
        }
    }
}
</syntaxhighlight>
|}
Note that the the sample code in ''<nowiki/>'OnAttackingWith' contains a function called 'base.OnAttackingWith'.'' This essentially runs any default behavior for the function. If you were to remove this line then any default behavior would not happen, and, in this case, the item will not lose any durability when used for attacking.
 
In the OnAttackingWith function, you are given access to the world, the entity who is attacking, the entity who is being attacked, and the itemslot that this item exists in. For testing purposes, add some code that outputs a message to the console when we attack an entity. You'll need to add the following code below the '''base''<nowiki/>' line:<syntaxhighlight lang="csharp">
world.Api.Logger.Event("Got attack with thorns blade!");
</syntaxhighlight>You can access the current game API from a lot of places. This particular line accesses the API through the world, however it can also be accessed through any entity with very similar code. You should be aware that this will create a message to the console when we attack an entity with the new item.
 
=== Registering the Item Class ===
The class for the thorns blade is created, however before it can be used it needs to be registered through the game API. In your mod system, which you used in the previous tutorial, you need to register an item class for both the client and server.


In the ''Start'' function, you need to make a call to the RegisterBlockClass function in the API class. Add the following code on a new line inside of the ''Start'' function's block:<syntaxhighlight lang="csharp">
In the ''Start'' function, make a call to the RegisterItemClass function in the API. Add the following code on a new line inside of the ''Start'' function's block:<syntaxhighlight lang="csharp">
api.RegisterBlockClass();
api.RegisterItemClass();
</syntaxhighlight>You'll notice that, currently, this results in an error. Hover over the 'RegisterBlockClass' with your mouse and it will show you a breakdown of the function.
</syntaxhighlight>Similarly to the previous tutorial, this will result in an error. You'll need to add some parameters to the function. Don't forget, you can hover over the function with your mouse and the required parameters will be shown. In this case, we once again need a class name, and a type.
[[File:VSBlockTutorialRegisterBlockClassFunction.png|center|frameless|659x659px]]
Your class name argument should be fully lowercase, and should generally be your mod ID, joined with the name of the class you just made. The itemType will be the class you just made. Replace the function with the following:<syntaxhighlight lang="csharp">
This tells you the function's parameters and a description of what it does. Not all functions in the code have descriptions attached to them, however as the modding API becomes more documented, these will hopefully become more populated. In this particular case, you need to pass in a class name, and a block type to the function.
api.RegisterItemClass(Mod.Info.ModID + ".thornsblade", typeof(ItemThornsBlade));
The class name argument should be fully lowercase, and should generally be your mod ID, joined with the name of the class you just made. The blockType will be the class you just made. Replace the function with the following:<syntaxhighlight lang="csharp">
</syntaxhighlight>This will register the ItemThornsBlade class with the name "vstutorial.thornsblade".
api.RegisterBlockClass(Mod.Info.ModID + ".trampoline", typeof(BlockTrampoline));
</syntaxhighlight>This will register the BlockTrampoline class with the name "vstutorial.trampoline".
{| class="wikitable mw-collapsible mw-collapsed"
{| class="wikitable mw-collapsible mw-collapsed"
|Why include the mod id here?
|Why include the mod id here?
Line 68: Line 98:
|}
|}


=== Adding Block Class to Asset ===
=== Adding Item Class to Asset ===
Before your block class will work, you need to add a new property to the blocktype JSON asset. In the solution explorer, open the file at ''assets/vstutorial/blocktypes/trampoline.json.''  
Before your item will work, you need to add the class to the asset file. Open the file in ''assets/vstutorial/itemtypes/thornsblade.json''.


You need to add the following property to the file:<syntaxhighlight lang="json">
You need to add the following property to the file:<syntaxhighlight lang="json">
"class": "vstutorial.trampoline",
"class": "vstutorial.thornsblade",
</syntaxhighlight>Generally, this element is placed as the second property in your json file, immediately below the code property.
</syntaxhighlight>Generally, this element is placed as the second property in your json file, immediately below the code property.
Note that the value of this property is identical to the value we used in the ''RegisterBlockClass'' function. This is how the game links the JSON asset files to your code's registered classes.
Note that the value of this property is identical to the value we used in the ''RegisterItemClass'' function. This is how the game links the JSON asset files to your code's registered classes.


== Testing the Block Class ==
== Testing the Item Class ==
Press F5 again to run the game with the mod. Remember to set the game to windowed mode by pressing F11. Find the trampoline in the creative menu, place one, and destroy it.
Press F5 again to run the game with the mod. Remember to set the game to windowed mode by pressing F11. Find the thorns blade in the creative menu, and use it to attack an entity.After doing so, take a look at the console that opened when launching the game. You should be able to see the following logs:<syntaxhighlight>
[Client Event] Got attack with thorns blade!
[Server Event] Got attack with thorns blade!
</syntaxhighlight>Remember what's going on here? Although you only attacked the entity once, there are two entries. One of these is sent to the console by the client, and the other is sent by the server. You should start to become aware that a lot of the code you write is going to happen on both the client and server. You'll be shown ways of controlling this later.


After doing so, take a look at the console that opened when launching the game. You should be able to see the following logs:<syntaxhighlight>
The thorns blade class and function are working, so close the game and go back to the ItemThornsBlade class.  
[Client Event] Trampoline Block Placed!
[Server Event] Trampoline Block Placed!
[Client Event] Trampoline Block Broken!
[Server Event] Trampoline Block Broken!
</syntaxhighlight>Wait... what?
The trampoline block was placed once, and broken once, so why are there two entries? Notice that each entry tells you whether it came from the client or server, and in this case, we have one of each. Due to running a singleplayer instance, there is both a client and server running at the same time. The overriden functions are being called once on the client, and once on the server.  


In many instances, this is exactly what is wanted. Most features are synced between the client and server, but it is important to remember that many functions get called twice in this system. There are a number of ways you can verify what 'side' is calling the function, or to limit certain code to one side.
== Thorns Blade Functionality ==
 
You now need to add the real functionality for the thorns blade. Remove the line of code that logs the attack event, since you don't need that anymore. Your code should look like the following. Feel free to copy and paste this, as it contains some useful code comments too. 
Anyway, it's clear that the trampoline class is working, so close the game and go back to the ''BlockTrampoline'' class.
 
== Trampoline Functionality ==
You need to create the actual functionality for the trampoline. When an entity collides with this block, the entity's vertical velocity should be reversed and multiplied by a certain amount.
 
The placed and break functions are unnecessary for this block, so you can remove them. Your script should look like the following:
{| class="wikitable mw-collapsible mw-collapsed"
{| class="wikitable mw-collapsible mw-collapsed"
|BlockTrampoline.cs
|ItemThornsBlade.cs
|-
|-
|<syntaxhighlight lang="csharp">
|<syntaxhighlight lang="csharp">
//Here are the imports for this script. Most of these will add automatically.
using Vintagestory.API.Common;
using Vintagestory.API.Common;
using Vintagestory.API.MathTools;
using Vintagestory.API.Common.Entities;


/*
namespace VSTutorial.Items
* The namespace the class will be in. This is essentially the folder the script is found in.
* If you need to use the BlockTrampoline class in any other script, you will have to add 'using VSTutorial.Blocks' to that script.
*/
namespace VSTutorial.Blocks
{
{
     /*
     /*
    * The class definition. Here, you define BlockTrampoline as a child of Block, which
    * As this is an item, you need to inherit the Item class. This gives access to functions within Item, and CollectibleObject.
     * means you can 'override' many of the functions within the general Block class.  
    * Take a look at https://apidocs.vintagestory.at/api/Vintagestory.API.Common.Item.html#methods and
    */
     *   https://apidocs.vintagestory.at/api/Vintagestory.API.Common.CollectibleObject.html#methods for all the methods that can be overriden.
     internal class BlockTrampoline : Block
    */
     internal class ItemThornsBlade : Item
     {
     {
          
         /*
        * This function is called whenever this item is used by an entity to attack another entity.
        * You have access to the world, the entity who is attacking, the entity who is being attacked, and the held item's data.
        */
        public override void OnAttackingWith(IWorldAccessor world, Entity byEntity, Entity attackedEntity, ItemSlot itemslot)
        {
            base.OnAttackingWith(world, byEntity, attackedEntity, itemslot);
        }
 
     }
     }
}
}
</syntaxhighlight>
</syntaxhighlight>
|}
|}
[[File:VisualStudioListBlockMethodsFromOverride.png|left|frameless|355x355px]]
Note that any further code in this function can be placed above or below the 'base' call. In this case, the order does not matter.
Click on the empty space, and type 'override ' (including the space). Visual Studio will show you a scrollable list of functions that can be overriden. If the menu closes, you can simply delete the space and replace it, and the menu will return.


Feel free to scroll through this menu and take a look at what is available. The functions here are all listed in the API documentation mentioned earlier.
=== Reflecting Damage ===
To inflict damage on an entity, there are two small steps:


The function you need to use is called ''OnEntityCollide''. With the mentioned menu above, type 'collide', and you should see a single result in the list. Press enter, tab, or double click on the entry, and Visual Studio will generate the function for you.
# A ''DamageSource'' instance must be created.
# The damage source must be inflicted on the entity using the Entity.ReceiveDamage function.


Take a look at the supplied arguments for this function. You have access to the world, the entity, the block's position, the 'facing' position of the collision, the collision speed, and whether this is an impact.
Before you can inflict damage on an entity, you must create a damage source. This includes the type of damage, and can contain information about where the damage has come from. Note that it does not contain the amount of damage.<syntaxhighlight lang="csharp">
DamageSource damage = new DamageSource()
{
    Type = EnumDamageType.PiercingAttack,
    CauseEntity = byEntity
};
</syntaxhighlight>
This creates a new instance of damage source called 'damage'. Type can be any instance of [https://github.com/anegostudios/vsapi/blob/cad83424ee89915ef206d0b23845af0a4ef72348/Common/Combat/EnumDamageType.cs#L3 EnumDamageType], but for this purpose you should use PiercingAttack. CauseEntity is the entity that caused the damage, which should be whatever is using our sword.


=== Making it Bounce ===
Now the damage source is created, you need to inflict it upon the entity. The entity that used the item is 'byEntity', so you'll want to damage them:<syntaxhighlight lang="csharp">
The following points determine when an entity should bounce on the trampoline block.
byEntity.ReceiveDamage(damage, 0.25f);
 
# The entity should bounce in the moment it lands on top of the block, and not if it is standing on it already. Therefore, ''isImpact'' needs to be ''true''.
# The entity should be colliding vertically. The sides of the block shouldn't push an entity away. In effect, ''facing.IsVertical'' needs to be ''true''.
 
So, add the following block inside the function:<syntaxhighlight lang="csharp">
if (isImpact && facing.IsVertical)
{
   
}
</syntaxhighlight>Now, you need to flip the motion of the entity.
To do this, you can change the value at ''entity.Pos.Motion.Y.'' Add the following code inside the ''if'' block.<syntaxhighlight lang="csharp">
entity.Pos.Motion.Y *= -0.8f;
</syntaxhighlight>This is a quicker way of writing:<syntaxhighlight lang="csharp">
entity.Pos.Motion.Y = entity.Pos.Motion.Y * -0.8f;
</syntaxhighlight>
</syntaxhighlight>
Multiplying by -0.8 will result in reversing the velocity, and reducing it by 20%. Feel free to play with this value to see some different effects.
This will inflict 0.25 points of damage onto the entity, using our 'damage' instance we just created.And that's it. When the item is used to attack, 0.25 points of damage will be inflicted on the player using the weapon.


The final script should look like the following:
The full code should be as follows:
{| class="wikitable mw-collapsible mw-collapsed"
{| class="wikitable mw-collapsible mw-collapsed"
|BlockTrampoline.cs
|ItemThornsBlade.cs
|-
|-
|<syntaxhighlight lang="csharp">
|<syntaxhighlight lang="csharp">
//Here are the imports for this script. Most of these will add automatically.
using Vintagestory.API.Common;
using Vintagestory.API.Common;
using Vintagestory.API.Common.Entities;
using Vintagestory.API.Common.Entities;
using Vintagestory.API.MathTools;


/*
namespace VSTutorial.Items
* The namespace the class will be in. This is essentially the folder the script is found in.
* If you need to use the BlockTrampoline class in any other script, you will have to add 'using VSTutorial.Blocks' to that script.
*/
namespace VSTutorial.Blocks
{
{
     /*
     /*
    * The class definition. Here, you define BlockTrampoline as a child of Block, which
    * As this is an item, you need to inherit the Item class. This gives access to functions within Item, and CollectibleObject.
     * means you can 'override' many of the functions within the general Block class.  
    * Take a look at https://apidocs.vintagestory.at/api/Vintagestory.API.Common.Item.html#methods and
    */
     *   https://apidocs.vintagestory.at/api/Vintagestory.API.Common.CollectibleObject.html#methods for all the methods that can be overriden.
     internal class BlockTrampoline : Block
    */
     internal class ItemThornsBlade : Item
     {
     {
         public override void OnEntityCollide(IWorldAccessor world, Entity entity, BlockPos pos, BlockFacing facing, Vec3d collideSpeed, bool isImpact)
        /*
        * This function is called whenever this item is used by an entity to attack another entity.
        * You have access to the world, the entity who is attacking, the entity who is being attacked, and the held item's data.
        */
         public override void OnAttackingWith(IWorldAccessor world, Entity byEntity, Entity attackedEntity, ItemSlot itemslot)
         {
         {
             if (isImpact && facing.IsVertical)
             DamageSource damage = new DamageSource()
             {
             {
                 entity.Pos.Motion.Y *= -0.8f;
                 Type = EnumDamageType.PiercingAttack,
             }
                CauseEntity = byEntity
            };
            byEntity.ReceiveDamage(damage, 0.25f);
             base.OnAttackingWith(world, byEntity, attackedEntity, itemslot);           
         }
         }
     }
     }
}
}
</syntaxhighlight>
</syntaxhighlight>
|}
|}
Give it a test! Falling onto your trampoline block should cause you to bounce!
Go ahead and test the new sword. You'll need to be in survival mode, but you should find that hitting an entity will also inflict damage on yourself.


== Conclusion ==
== Conclusion ==
Congratulations, you've created, registered and tested a new block class! Although it's been a long tutorial, you've covered many topics here that will give you a great understanding of how coding for Vintage Story works.
Congratulations, you've created, registered and tested a new item class!  


=== Next Steps... ===
=== Next Steps... ===
If you want to test your knowledge consider doing the tasks under the ''Going Further'' section below.  
If you want to test your knowledge consider doing the tasks under the ''Going Further'' section below.  


When you're ready, take a look at the next tutorial. This will show you how to give extra functionality to an item.
When you're ready, take a look at the next tutorial. This will show you how to register and add commands into the game!


== Going Further ==
== Going Further ==
Want to make some additional changes to this mod? Try and achieve the following things!
Want to make some additional changes to this mod? Try and achieve the following things!


Make the trampoline super bouncy.
Currently, the sword will damage you even if it is used on mobs that are dead. Make it inflict damage only if the entity being attacked is alive.
{| class="wikitable mw-collapsible mw-collapsed"
{| class="wikitable mw-collapsible mw-collapsed"
|To achieve this...
|To achieve this...
|-
|-
|Replace the -0.8 value with -1.5 or lower. Although setting to -1 would technically reflect the motion perfectly, some velocity is lost on the frame the entity collides with the block.
|Check the 'Alive' field on 'attackedEntity' is true before inflicting damage.<syntaxhighlight lang="csharp">
if (attackedEntity.Alive)
{
    byEntity.ReceiveDamage(damage, 0.25f);
}
</syntaxhighlight>
|}
|}
Change the functionality of the block to allow an entity to bounce on any side of the block. You should use ''facing.IsAxisNS'' and ''facing.IsAxisWE'' to determine what side the entity collides with.
Using the revive function in the Entity class, make the sword revive any dead entities hit by it.
{| class="wikitable mw-collapsible mw-collapsed"
{| class="wikitable mw-collapsible mw-collapsed"
|To achieve this...
|To achieve this...
|-
|-
|You need to replace the contents of the ''OnEntityCollide'' function with something similar to the following:<syntaxhighlight lang="csharp">
|Check the 'alive' field is false, and then call the Revive function on 'attackedEntity'.<syntaxhighlight lang="csharp">
if (isImpact && facing.IsVertical)
if (!attackedEntity.Alive)
{
{
     entity.Pos.Motion.Y *= -0.8f;
     attackedEntity.Revive();
}
}
else if (facing.IsAxisNS)
</syntaxhighlight>
{
    entity.Pos.Motion.Z *= -1.2f;
}
else if (facing.IsAxisWE)
{
    entity.Pos.Motion.X *= -1.2f;
}
</syntaxhighlight>Note that you will need to jump into the trampoline block to bounce off of it.
|}
|}
</translate>
</translate>
{{navbox/codemodding}}
{{navbox/codemodding}}

Latest revision as of 13:29, 29 June 2024

This page is in progress. Please come back later!

Introduction

Objective

In this tutorial, you will be creating an item with basic custom functionality. You will find out how item classes are registered, and how to interact further with entities. The item you will be creating is a new 'thorny' sword, which will slightly hurt the player when used to attack.

Prerequisites

It is recommended to use the same project for all newer code tutorials, with the project name "VSTutorial". If you have not yet done this, please follow the following tutorials:

  1. Preparing For Code Mods
  2. Creating A Code Mod

This tutorial also assumes that you have read:

It is also highly recommended to have completed previous tutorial in the code series:

Assets

This tutorial uses the finished project from the previous tutorial. If you need to, you can download the finished version of the previous tutorial from GitHub here.

As usual, you need the required assets for this tutorial. These can be downloaded from GitHub here, and should replace the entirety of your assets folder as it also contains the previous tutorial's assets. When these files are added, you should have new itemtype, shape, and texture files.

Run the game, launch a world, and check the creative menu. You should be able to find the "Thorns Blade".

Playing Vintage Story in fullscreen mode?
It is highly recommended to run the game in windowed mode when making code mods. If your code results in an error, Visual Studio will attempt to gain focus, and Vintage Story will stop responding. You can press F11 to easily switch between the fullscreen and windowed modes.

Item Class

Close the game and go back to Visual Studio. To add custom functionality to the item, you'll need to create an item class.

An item class is a script that offers custom functionality for any specific itemtype assets. Note that each itemtype can only have a single attached item class.

Creating an item class is extremely similar to creating a block class.

Creating an Item Class

To keep the code organized, you should create a new folder called "Items". Right click on your project, hover over add, and select New Folder.

You also need a name for the new item class. These follow the same convention as the Blocks, and most other types. In this case, our thorny blade item class, you should name this class "ItemThornsBlade".

Create your new class. Right click the new folder, hover over Add, and select Class. Ensure you have the standard Class selected in the template list, enter your class name, and click create.

The class you created needs to extend the 'Item' class. This will allow you to use many of the available functions for items. Change the class definition to now be:

internal class ItemThornsBlade : Item

Most of the time, Visual Studio will automatically add 'using' statements. However, if this errors, click on "Item", press Alt+Enter, and select the "using Vintagestory.API.Common" option.

VisualStudioModdingTutorialAutoErrorSolving.png

The ItemThornsBlade class now exists, but it does nothing. Time to override a function, I suppose.

Inside the empty class, type "override " and a list of methods we can use will be displayed. In this instance, you will want to override the 'OnAttackingWith' function. This function is called whenever our item is used to attack an entity. Search for this in the list and double click on it to automatically add the required code sample.

VisualStudioModdingTutorialOverrideOnAttackingWithFunction.png

Your class should now look like this:

ItemThornsBlade.cs
using Vintagestory.API.Common;
using Vintagestory.API.Common.Entities;

namespace VSTutorial.Items
{
    internal class ItemThornsBlade : Item
    {
        public override void OnAttackingWith(IWorldAccessor world, Entity byEntity, Entity attackedEntity, ItemSlot itemslot)
        {
            base.OnAttackingWith(world, byEntity, attackedEntity, itemslot);
        }
    }
}

Note that the the sample code in 'OnAttackingWith' contains a function called 'base.OnAttackingWith'. This essentially runs any default behavior for the function. If you were to remove this line then any default behavior would not happen, and, in this case, the item will not lose any durability when used for attacking.

In the OnAttackingWith function, you are given access to the world, the entity who is attacking, the entity who is being attacked, and the itemslot that this item exists in. For testing purposes, add some code that outputs a message to the console when we attack an entity. You'll need to add the following code below the 'base' line:

world.Api.Logger.Event("Got attack with thorns blade!");

You can access the current game API from a lot of places. This particular line accesses the API through the world, however it can also be accessed through any entity with very similar code. You should be aware that this will create a message to the console when we attack an entity with the new item.

Registering the Item Class

The class for the thorns blade is created, however before it can be used it needs to be registered through the game API. In your mod system, which you used in the previous tutorial, you need to register an item class for both the client and server.

In the Start function, make a call to the RegisterItemClass function in the API. Add the following code on a new line inside of the Start function's block:

api.RegisterItemClass();

Similarly to the previous tutorial, this will result in an error. You'll need to add some parameters to the function. Don't forget, you can hover over the function with your mouse and the required parameters will be shown. In this case, we once again need a class name, and a type. Your class name argument should be fully lowercase, and should generally be your mod ID, joined with the name of the class you just made. The itemType will be the class you just made. Replace the function with the following:

api.RegisterItemClass(Mod.Info.ModID + ".thornsblade", typeof(ItemThornsBlade));

This will register the ItemThornsBlade class with the name "vstutorial.thornsblade".

Why include the mod id here?
If you've made code mods before the introduction of these new tutorials, you probably haven't included your mod ID when registering classes. As more and more mods are being made, there are occasional collisions in regards to class names. Say, for example, two mods both add in a 'trampoline' block class. Only one instance of the trampoline class will exist, which can cause issues if they had slightly different functionality.

So, including your mod ID when registering classes will ensure that these 'collisions' do not occur.

Adding Item Class to Asset

Before your item will work, you need to add the class to the asset file. Open the file in assets/vstutorial/itemtypes/thornsblade.json.

You need to add the following property to the file:

"class": "vstutorial.thornsblade",

Generally, this element is placed as the second property in your json file, immediately below the code property.

Note that the value of this property is identical to the value we used in the RegisterItemClass function. This is how the game links the JSON asset files to your code's registered classes.

Testing the Item Class

Press F5 again to run the game with the mod. Remember to set the game to windowed mode by pressing F11. Find the thorns blade in the creative menu, and use it to attack an entity.After doing so, take a look at the console that opened when launching the game. You should be able to see the following logs:

[Client Event] Got attack with thorns blade!
[Server Event] Got attack with thorns blade!

Remember what's going on here? Although you only attacked the entity once, there are two entries. One of these is sent to the console by the client, and the other is sent by the server. You should start to become aware that a lot of the code you write is going to happen on both the client and server. You'll be shown ways of controlling this later.

The thorns blade class and function are working, so close the game and go back to the ItemThornsBlade class.

Thorns Blade Functionality

You now need to add the real functionality for the thorns blade. Remove the line of code that logs the attack event, since you don't need that anymore. Your code should look like the following. Feel free to copy and paste this, as it contains some useful code comments too.

ItemThornsBlade.cs
using Vintagestory.API.Common;
using Vintagestory.API.Common.Entities;

namespace VSTutorial.Items
{
    /*
     * As this is an item, you need to inherit the Item class. This gives access to functions within Item, and CollectibleObject.
     * Take a look at https://apidocs.vintagestory.at/api/Vintagestory.API.Common.Item.html#methods and
    *   https://apidocs.vintagestory.at/api/Vintagestory.API.Common.CollectibleObject.html#methods for all the methods that can be overriden.
     */
    internal class ItemThornsBlade : Item
    {
        /*
         * This function is called whenever this item is used by an entity to attack another entity.
         * You have access to the world, the entity who is attacking, the entity who is being attacked, and the held item's data.
         */
        public override void OnAttackingWith(IWorldAccessor world, Entity byEntity, Entity attackedEntity, ItemSlot itemslot)
        {
            base.OnAttackingWith(world, byEntity, attackedEntity, itemslot);
        }

    }
}

Note that any further code in this function can be placed above or below the 'base' call. In this case, the order does not matter.

Reflecting Damage

To inflict damage on an entity, there are two small steps:

  1. A DamageSource instance must be created.
  2. The damage source must be inflicted on the entity using the Entity.ReceiveDamage function.

Before you can inflict damage on an entity, you must create a damage source. This includes the type of damage, and can contain information about where the damage has come from. Note that it does not contain the amount of damage.

DamageSource damage = new DamageSource()
{
    Type = EnumDamageType.PiercingAttack,
    CauseEntity = byEntity
};

This creates a new instance of damage source called 'damage'. Type can be any instance of EnumDamageType, but for this purpose you should use PiercingAttack. CauseEntity is the entity that caused the damage, which should be whatever is using our sword.

Now the damage source is created, you need to inflict it upon the entity. The entity that used the item is 'byEntity', so you'll want to damage them:

byEntity.ReceiveDamage(damage, 0.25f);

This will inflict 0.25 points of damage onto the entity, using our 'damage' instance we just created.And that's it. When the item is used to attack, 0.25 points of damage will be inflicted on the player using the weapon.

The full code should be as follows:

ItemThornsBlade.cs
using Vintagestory.API.Common;
using Vintagestory.API.Common.Entities;

namespace VSTutorial.Items
{
    /*
     * As this is an item, you need to inherit the Item class. This gives access to functions within Item, and CollectibleObject.
     * Take a look at https://apidocs.vintagestory.at/api/Vintagestory.API.Common.Item.html#methods and
    *   https://apidocs.vintagestory.at/api/Vintagestory.API.Common.CollectibleObject.html#methods for all the methods that can be overriden.
     */
    internal class ItemThornsBlade : Item
    {
        /*
         * This function is called whenever this item is used by an entity to attack another entity.
         * You have access to the world, the entity who is attacking, the entity who is being attacked, and the held item's data.
         */
        public override void OnAttackingWith(IWorldAccessor world, Entity byEntity, Entity attackedEntity, ItemSlot itemslot)
        {
            DamageSource damage = new DamageSource()
            {
                Type = EnumDamageType.PiercingAttack,
                CauseEntity = byEntity
            };
            byEntity.ReceiveDamage(damage, 0.25f);
            base.OnAttackingWith(world, byEntity, attackedEntity, itemslot);            
        }

    }
}

Go ahead and test the new sword. You'll need to be in survival mode, but you should find that hitting an entity will also inflict damage on yourself.

Conclusion

Congratulations, you've created, registered and tested a new item class!

Next Steps...

If you want to test your knowledge consider doing the tasks under the Going Further section below.

When you're ready, take a look at the next tutorial. This will show you how to register and add commands into the game!

Going Further

Want to make some additional changes to this mod? Try and achieve the following things!

Currently, the sword will damage you even if it is used on mobs that are dead. Make it inflict damage only if the entity being attacked is alive.

To achieve this...
Check the 'Alive' field on 'attackedEntity' is true before inflicting damage.
if (attackedEntity.Alive)
{
    byEntity.ReceiveDamage(damage, 0.25f);
}

Using the revive function in the Entity class, make the sword revive any dead entities hit by it.

To achieve this...
Check the 'alive' field is false, and then call the Revive function on 'attackedEntity'.
if (!attackedEntity.Alive)
{
    attackedEntity.Revive();
}
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.

Modding
Modding Introduction Getting Started Theme Pack
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 ItemEntityBlockBlock BehaviorsBlock ClassesBlock EntitiesBlock Entity BehaviorsWorld properties
Workflows & Infrastructure Modding Efficiency TipsMod-engine compatibilityMod ExtensibilityVS Engine
Additional Resources Community Resources Modding API Updates Programming Languages List of server commandsList of client commandsClient startup parametersServer startup parameters
Example ModsAPI DocsGitHub Repository