Modding:Adding Block Behavior: Difference between revisions
Lazy Warlock (talk | contribs) No edit summary |
(Make it easier to see that OnBlockInteractStart is a link) |
||
(19 intermediate revisions by 6 users not shown) | |||
Line 1: | Line 1: | ||
<languages /> | <languages/> | ||
<translate> | <translate> | ||
<!--T:89--> | |||
__FORCETOC__ | __FORCETOC__ | ||
</translate> | |||
<translate> | |||
<!--T:48--> | |||
{{GameVersion|1.19.3}} | |||
</translate> | |||
<translate> | |||
= Introduction = <!--T:49--> | |||
Block Behaviors are useful when you want different blocks to act in the same way, as you can attach one or more block behaviors to an arbitrary number of blocks. | Block Behaviors are useful when you want different blocks to act in the same way, as you can attach one or more block behaviors to an arbitrary number of blocks. | ||
You may want to have a look at the existing [[Block Json Properties#p_behaviors|block behaviors]] before implementing your own. | You may want to have a look at the existing [[Block Json Properties#p_behaviors|block behaviors]] before implementing your own. | ||
<!--T:50--> | |||
In this tutorial we'll create a new Behavior that we can attach to blocks to make them movable by right clicking them. | In this tutorial we'll create a new Behavior that we can attach to blocks to make them movable by right clicking them. | ||
</translate> | |||
== Setting up == | <translate> | ||
== Setting up == <!--T:51--> | |||
<!--T:52--> | |||
A [[Setting up your Development Environment|development workspace]] is required. Additionally you will need the assets (blocktype, texture and lang file). You can either create your one owns or use those pre-made ones: [https://wiki.vintagestory.at/images/2/2f/Moving_-_No_CS_File.zip Moving - No CS File.zip] | A [[Setting up your Development Environment|development workspace]] is required. Additionally you will need the assets (blocktype, texture and lang file). You can either create your one owns or use those pre-made ones: [https://wiki.vintagestory.at/images/2/2f/Moving_-_No_CS_File.zip Moving - No CS File.zip] | ||
</translate> | |||
== Creating the behavior == | <translate> | ||
== Creating the behavior == <!--T:53--> | |||
<!--T:54--> | |||
So first of all we need to create the behavior itself, which is a class extending BlockBehavior | So first of all we need to create the behavior itself, which is a class extending BlockBehavior | ||
</translate> | |||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
class Moving : BlockBehavior | class Moving : BlockBehavior | ||
Line 28: | Line 42: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<translate> | |||
<!--T:55--> | |||
This class provides several methods we can override. When you use Visual Studio you can find a full list of a methods by hovering with the mouse of "BlockBehavior" and pressing "F12". | This class provides several methods we can override. When you use Visual Studio you can find a full list of a methods by hovering with the mouse of "BlockBehavior" and pressing "F12". | ||
<!--T:56--> | |||
---- | ---- | ||
The method | <!--T:57--> | ||
The method [[Modding:Right_click_events|'''bool OnBlockInteractStart(IWorldAccessor world, IPlayer byPlayer, BlockSelection blockSel, ref EnumHandling handling)''']] looks to be ideal for our purpose. | |||
<!--T:58--> | |||
What should it do? | What should it do? | ||
# Calculate the new position | <!--T:59--> | ||
# Check if the block can be placed at this position | # Calculate the new position to move the block to, based on the block face the player is looking at. | ||
# Remove the | # Check if the block can be placed at this new position. | ||
# Place the new | # Remove the block at the old position. | ||
# Place the same type of block at the new position. | |||
# [[Modding:Behavior_Traversal|Skip]] the default logic that would otherwise place whatever item is held at the old position. | |||
</translate> | |||
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
public override bool | public override bool OnBlockInteractStart(IWorldAccessor world, IPlayer byPlayer, BlockSelection blockSel, ref EnumHandling handling) | ||
{ | { | ||
// Find the target position | <translate> | ||
BlockPos pos = blockSel.Position.AddCopy(blockSel.Face. | <!--T:60--> | ||
// Find the target position | |||
// Can we place the block there? | </translate> | ||
BlockPos pos = blockSel.Position.AddCopy(blockSel.Face.Opposite); | |||
<translate> | |||
<!--T:61--> | |||
// Can we place the block there? | |||
</translate> | |||
if (world.BlockAccessor.GetBlock(pos).IsReplacableBy(block)) | if (world.BlockAccessor.GetBlock(pos).IsReplacableBy(block)) | ||
{ | { | ||
// Remove the block at the current position and place it at the target position | <translate> | ||
<!--T:88--> | |||
// Remove the block at the current position and place it at the target position | |||
</translate> | |||
world.BlockAccessor.SetBlock(0, blockSel.Position); | world.BlockAccessor.SetBlock(0, blockSel.Position); | ||
world.BlockAccessor.SetBlock(block.BlockId, pos); | world.BlockAccessor.SetBlock(block.BlockId, pos); | ||
} | } | ||
<translate> | |||
// Notify the game engine other block behaviors that we handled the players interaction with the block. | <!--T:62--> | ||
// Notify the game engine other block behaviors that we handled the players interaction with the block. | |||
// If we would not set the handling field the player would still be able to place blocks if he has them in hands. | // If we would not set the handling field the player would still be able to place blocks if he has them in hands. | ||
</translate> | |||
handling = EnumHandling.PreventDefault; | handling = EnumHandling.PreventDefault; | ||
return true; | return true; | ||
Line 62: | Line 94: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Register == | <translate> | ||
== Register == <!--T:63--> | |||
<!--T:64--> | |||
In order the register the BlockBehavior we need to create a mod class, override <code>Start(ICoreAPI)</code> and register it with the given name: | In order the register the BlockBehavior we need to create a mod class, override <code>Start(ICoreAPI)</code> and register it with the given name: | ||
</translate> | |||
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
Line 70: | Line 106: | ||
{ | { | ||
public override void Start(ICoreAPI api) | |||
{ | { | ||
base.Start(api); | base.Start(api); | ||
Line 76: | Line 112: | ||
} | } | ||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
== Distribution == | <translate> | ||
== Distribution == <!--T:65--> | |||
<!--T:66--> | |||
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. | 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. | ||
* for VS 1.9: [https://wiki.vintagestory.at/images/2/2a/Moving_v1.0.0.zip Moving_v1.0.0.zip] | * for VS 1.9: [https://wiki.vintagestory.at/images/2/2a/Moving_v1.0.0.zip Moving_v1.0.0.zip] | ||
* for VS 1.6: [https://wiki.vintagestory.at/images/c/cb/Moving.zip Moving.zip] | * for VS 1.6: [https://wiki.vintagestory.at/images/c/cb/Moving.zip Moving.zip] | ||
== Testing == | == Testing == <!--T:67--> | ||
</translate> | |||
<youtube>8eVG0uQF2xs</youtube> | <youtube>8eVG0uQF2xs</youtube> | ||
= Advanced Behavior = | <translate> | ||
= Advanced Behavior = <!--T:68--> | |||
<!--T:69--> | |||
Our behavior is still rather simple, but there are a lot more possibilities. A behavior can have special properties, which can be defined by the blocktype itself. | Our behavior is still rather simple, but there are a lot more possibilities. A behavior can have special properties, which can be defined by the blocktype itself. | ||
</translate> | |||
== Example == | <translate> | ||
== Example == <!--T:70--> | |||
<!--T:71--> | |||
The behavior liquid supports some special properties as shown in this example of the water blocktype: | The behavior liquid supports some special properties as shown in this example of the water blocktype: | ||
</translate> | |||
<syntaxhighlight lang="json"> | <syntaxhighlight lang="json"> | ||
Line 112: | Line 157: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Parsing properties == | <translate> | ||
== Parsing properties == <!--T:72--> | |||
<!--T:73--> | |||
In order to take care of special properties there is a method called <code>Initialize(JsonObject)</code>. Each blocktype creates a new instance of the behavior, so the method can be used to parse the properties. | In order to take care of special properties there is a method called <code>Initialize(JsonObject)</code>. Each blocktype creates a new instance of the behavior, so the method can be used to parse the properties. | ||
<!--T:74--> | |||
So what kind of properties could we add? | So what kind of properties could we add? | ||
* push distance | * push distance | ||
* pull block if player is sneaking | * pull block if player is sneaking | ||
<!--T:75--> | |||
First of all, we need to override the method in our block behavior class ... | First of all, we need to override the method in our block behavior class ... | ||
</translate> | |||
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
Line 129: | Line 179: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<translate> | |||
<!--T:76--> | |||
Additionally we need to add two fields, one for the distance and another one if the player should pull the block while sneaking ... | Additionally we need to add two fields, one for the distance and another one if the player should pull the block while sneaking ... | ||
</translate> | |||
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
Line 136: | Line 189: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<translate> | |||
<!--T:77--> | |||
Now we can parse the two properties like so: | Now we can parse the two properties like so: | ||
</translate> | |||
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
Line 145: | Line 201: | ||
---- | ---- | ||
<translate> | |||
<!--T:78--> | |||
The next thing we need to change is the interact method itself, so that it takes care of the distance and the pull properties ... | The next thing we need to change is the interact method itself, so that it takes care of the distance and the pull properties ... | ||
</translate> | |||
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
public override bool | public override bool OnBlockInteractStart(IWorldAccessor world, IPlayer byPlayer, BlockSelection blockSel, ref EnumHandling handling) | ||
{ | { | ||
BlockPos pos = blockSel.Position.AddCopy(pull && byPlayer.WorldData.EntityControls.Sneak ? blockSel.Face : blockSel.Face. | <translate> | ||
<!--T:79--> | |||
// Find the target position | |||
</translate> | |||
BlockPos pos = blockSel.Position.AddCopy(pull && byPlayer.WorldData.EntityControls.Sneak ? blockSel.Face : blockSel.Face.Opposite, distance); | |||
<translate> | |||
<!--T:80--> | |||
// Can we place the block there? | |||
</translate> | |||
if (world.BlockAccessor.GetBlock(pos).IsReplacableBy(block)) | if (world.BlockAccessor.GetBlock(pos).IsReplacableBy(block)) | ||
{ | { | ||
<translate> | |||
<!--T:81--> | |||
// Remove the block at the current position and place it at the target position | |||
</translate> | |||
world.BlockAccessor.SetBlock(0, blockSel.Position); | world.BlockAccessor.SetBlock(0, blockSel.Position); | ||
world.BlockAccessor.SetBlock(block.BlockId, pos); | world.BlockAccessor.SetBlock(block.BlockId, pos); | ||
} | } | ||
<translate> | |||
<!--T:82--> | |||
// Notify the game engine other block behaviors that we handled the players interaction with the block. | |||
// If we would not set the handling field the player would still be able to place blocks if he has them in hands. | |||
</translate> | |||
handling = EnumHandling.PreventDefault; | handling = EnumHandling.PreventDefault; | ||
return true; | return true; | ||
Line 160: | Line 238: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Adding another block == | <translate> | ||
== Adding another block == <!--T:83--> | |||
<!--T:84--> | |||
Let's create another block using this behavior, but this time we will configure some additional properties ... | Let's create another block using this behavior, but this time we will configure some additional properties ... | ||
</translate> | |||
<syntaxhighlight lang="json"> | <syntaxhighlight lang="json"> | ||
Line 176: | Line 257: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<translate> | |||
<!--T:85--> | |||
The block will be pushed two blocks instead of one and the player can pull it by sneaking while right clicking. | The block will be pushed two blocks instead of one and the player can pull it by sneaking while right clicking. | ||
</translate> | |||
<translate> | |||
= Mod Download = <!--T:86--> | |||
<!--T:87--> | |||
* for VS 1.9: [https://wiki.vintagestory.at/images/7/7b/Advancedmoving_v1.0.0.zip AdvancedMoving_v1.0.0.zip] | * for VS 1.9: [https://wiki.vintagestory.at/images/7/7b/Advancedmoving_v1.0.0.zip AdvancedMoving_v1.0.0.zip] | ||
* for VS 1.6: [https://wiki.vintagestory.at/images/7/72/AdvancedMoving.zip AdvancedMoving.zip] | * for VS 1.6: [https://wiki.vintagestory.at/images/7/72/AdvancedMoving.zip AdvancedMoving.zip] | ||
</translate> | |||
{{Navbox/codemodding}} | |||
{{Navbox/ | |||
Latest revision as of 07:36, 15 July 2024
This page was last verified for Vintage Story version 1.19.3.
Introduction
Block Behaviors are useful when you want different blocks to act in the same way, as you can attach one or more block behaviors to an arbitrary number of blocks. You may want to have a look at the existing block behaviors before implementing your own.
In this tutorial we'll create a new Behavior that we can attach to blocks to make them movable by right clicking them.
Setting up
A development workspace is required. Additionally you will need the assets (blocktype, texture and lang file). You can either create your one owns or use those pre-made ones: Moving - No CS File.zip
Creating the behavior
So first of all we need to create the behavior itself, which is a class extending BlockBehavior
class Moving : BlockBehavior
{
public Moving(Block block) : base(block)
{
}
}
This class provides several methods we can override. When you use Visual Studio you can find a full list of a methods by hovering with the mouse of "BlockBehavior" and pressing "F12".
The method bool OnBlockInteractStart(IWorldAccessor world, IPlayer byPlayer, BlockSelection blockSel, ref EnumHandling handling) looks to be ideal for our purpose.
What should it do?
- Calculate the new position to move the block to, based on the block face the player is looking at.
- Check if the block can be placed at this new position.
- Remove the block at the old position.
- Place the same type of block at the new position.
- Skip the default logic that would otherwise place whatever item is held at the old position.
public override bool OnBlockInteractStart(IWorldAccessor world, IPlayer byPlayer, BlockSelection blockSel, ref EnumHandling handling)
{
// Find the target position
BlockPos pos = blockSel.Position.AddCopy(blockSel.Face.Opposite);
// Can we place the block there?
if (world.BlockAccessor.GetBlock(pos).IsReplacableBy(block))
{
// Remove the block at the current position and place it at the target position
world.BlockAccessor.SetBlock(0, blockSel.Position);
world.BlockAccessor.SetBlock(block.BlockId, pos);
}
// Notify the game engine other block behaviors that we handled the players interaction with the block.
// If we would not set the handling field the player would still be able to place blocks if he has them in hands.
handling = EnumHandling.PreventDefault;
return true;
}
Register
In order the register the BlockBehavior we need to create a mod class, override Start(ICoreAPI)
and register it with the given name:
public class MovingBlocks : ModSystem
{
public override void Start(ICoreAPI api)
{
base.Start(api);
api.RegisterBlockBehaviorClass("Moving", typeof(Moving));
}
}
Distribution
In order to finish everything, open the modtools and type in pack <your mod id>
. Now you can take the zip file and share it with other people.
- for VS 1.9: Moving_v1.0.0.zip
- for VS 1.6: Moving.zip
Testing
Advanced Behavior
Our behavior is still rather simple, but there are a lot more possibilities. A behavior can have special properties, which can be defined by the blocktype itself.
Example
The behavior liquid supports some special properties as shown in this example of the water blocktype:
behaviors: [
{
name: "FiniteSpreadingLiquid",
properties:
{
spreadDelay: 150,
liquidCollisionSound: "hotmetal",
sourceReplacementCode: "obsidian",
flowingReplacementCode: "basalt"
}
}
],
Parsing properties
In order to take care of special properties there is a method called Initialize(JsonObject)
. Each blocktype creates a new instance of the behavior, so the method can be used to parse the properties.
So what kind of properties could we add?
- push distance
- pull block if player is sneaking
First of all, we need to override the method in our block behavior class ...
public override void Initialize(JsonObject properties)
{
base.Initialize(properties);
}
Additionally we need to add two fields, one for the distance and another one if the player should pull the block while sneaking ...
public int distance = 1;
public bool pull = false;
Now we can parse the two properties like so:
distance = properties["distance"].AsInt(1);
pull = properties["pull"].AsBool(false);
The next thing we need to change is the interact method itself, so that it takes care of the distance and the pull properties ...
public override bool OnBlockInteractStart(IWorldAccessor world, IPlayer byPlayer, BlockSelection blockSel, ref EnumHandling handling)
{
// Find the target position
BlockPos pos = blockSel.Position.AddCopy(pull && byPlayer.WorldData.EntityControls.Sneak ? blockSel.Face : blockSel.Face.Opposite, distance);
// Can we place the block there?
if (world.BlockAccessor.GetBlock(pos).IsReplacableBy(block))
{
// Remove the block at the current position and place it at the target position
world.BlockAccessor.SetBlock(0, blockSel.Position);
world.BlockAccessor.SetBlock(block.BlockId, pos);
}
// Notify the game engine other block behaviors that we handled the players interaction with the block.
// If we would not set the handling field the player would still be able to place blocks if he has them in hands.
handling = EnumHandling.PreventDefault;
return true;
}
Adding another block
Let's create another block using this behavior, but this time we will configure some additional properties ...
behaviors: [
{
name: "Moving",
properties: {
"distance": 2,
"pull": true
}
}
],
The block will be pushed two blocks instead of one and the player can pull it by sneaking while right clicking.
Mod Download
- for VS 1.9: AdvancedMoving_v1.0.0.zip
- for VS 1.6: AdvancedMoving.zip
Code Modding | |||||||
---|---|---|---|---|---|---|---|
Basics | Code Mods • Preparing For Code Mods • Creating A Code Mod | ||||||
Tutorials |
|
||||||
Advanced | Server-Client Considerations • Setting up your Development Environment • Advanced Blocks • Advanced Items • Block and Item Interactions • Block Behavior • Block Entity • Particle Effects • World Access • Inventory Handling • Commands • GUIs • Network API • Monkey patching (Harmony) | ||||||
Data Management | VCDBS format • Savegame Moddata • ModConfig File • Chunk Moddata • Serialization Formats • TreeAttribute | ||||||
Worldgen | WorldGen API • NatFloat • EvolvingNatFloat | ||||||
Rendering | Shaders and Renderers |
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 | 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 |