Modding:WorldGen API: Difference between revisions

From Vintage Story Wiki
Line 70: Line 70:


== Adding items to our chest ==
== Adding items to our chest ==
We want to add some items to the chest since after all it is a "treasure" chest. For now we are going to add various ingots to the chest. However there are lots of items in Vintagestory and I encourage you to play with this and feel free to add other items. We also want to add items at random. Not only the type of items should be random but also the ingots we add should be random. There are all kinds of ways to do this but lets write a data structure that represents a grab bag full of items and lets pull items out of that bag and place them in the chest like Santa Claus! We will create a ShuffleBag class so in Visual Studio right click on your project and go to Add->Class. Call it ShuffleBag.cs and press Add. Here's the code to place in the ShuffleBag class.
We want to add some items to the chest since after all it is a "treasure" chest. For now we are going to add various ingots to the chest. However there are lots of items in Vintagestory and I encourage you to play with this and feel free to add other items. We also want to add items at random. Not only the type of items should be random but also the ingots we add should be random. There are all kinds of ways to do this but lets write a data structure that represents a grab bag full of items and lets pull items out of that bag and place them in the chest like Santa Claus! We will create a '''ShuffleBag''' class so in Visual Studio right click on your project and go to Add->Class. Call it '''ShuffleBag.cs''' and press Add. Here's the code to place in the '''ShuffleBag''' class.
<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
using System;
using System;
Line 139: Line 139:
}
}
</syntaxhighlight>
</syntaxhighlight>
This is all basic C# stuff but the idea is that we are going to call Add on our ShuffleBag several times to load it up, then we are going to call Next to pull items out of it. This will let us kind of control the probability or rarity of items placed in chests. Items that are more rare will have fewer items in the bag.
This is all basic C# stuff but the idea is that we are going to call '''Add''' on our '''ShuffleBag''' several times to load it up, then we are going to call '''Next''' to pull items out of it. This will let us kind of control the probability or rarity of items placed in chests. Items that are more rare will have fewer items in the bag.


Lets add a couple of class variables to control the minimum and maximum number of items that will go in our chest.
Lets add a couple of class variables to control the minimum and maximum number of items that will go in our chest.
Line 164: Line 164:
}
}
</syntaxhighlight>
</syntaxhighlight>
This loads up our ShuffleBag with various ingots. The only more common item is iron. Feel free to change the item counts as you see fit. One thing to note here is the API does give us an instance of Random that we can use so we pass it in to our ShuffleBag.
This loads up our '''ShuffleBag''' with various ingots. The only more common item is iron. Feel free to change the item counts as you see fit. One thing to note here is the API does give us an instance of Random that we can use so we pass it in to our ShuffleBag.


We are almost ready to place these items in the chest but we need to create an ItemStack for each slot we will be taking up in the chest. The chest is an instance of IBlockEntityContainer which has an Inventory property. An Inventory is made up of several IItemSlot instances. We need a utility method to create a list of ItemStacks to place in those slots.
We are almost ready to place these items in the chest but we need to create an '''ItemStack''' for each slot we will be taking up in the chest. The chest is an instance of '''IBlockEntityContainer''' which has an '''Inventory''' property. An '''Inventory''' is made up of several '''IItemSlot''' instances. We need a utility method to create a list of '''ItemStacks''' to place in those slots.
<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
private IEnumerable<ItemStack> MakeItemStacks()
private IEnumerable<ItemStack> MakeItemStacks()
Line 189: Line 189:
}
}
</syntaxhighlight>
</syntaxhighlight>
Here we make our ShuffleBag by calling MakeShuffleBag. We calculate a grabCount which is random number between MIN_ITEMS and MAX_ITEMS that controls the number of times Santa is going to reach into his bag. We don't want to create an ItemStack for each item because we may get 3 iron ingots for example. We don't want 3 slots with one iron ingot, we want one slot with 3 iron ingots. So we create a Dictionary and add items of the same type to the same ItemStack. This method returns an IEnumerable that we can loop over so we need to add a method that can loop over a list of ItemStacks and add them to our chest.
Here we make our '''ShuffleBag''' by calling '''MakeShuffleBag'''. We calculate a '''grabCount''' which is random number between '''MIN_ITEMS''' and '''MAX_ITEMS''' that controls the number of times Santa is going to reach into his bag. We don't want to create an '''ItemStack''' for each item because we may get 3 iron ingots for example. We don't want 3 slots with one iron ingot, we want one slot with 3 iron ingots. So we create a '''Dictionary''' and add items of the same type to the same '''ItemStack'''. This method returns an '''IEnumerable''' that we can loop over so we need to add a method that can loop over a list of '''ItemStacks''' and add them to our chest.
<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
private void AddItemStacks(IBlockEntityContainer chest, IEnumerable<ItemStack> itemStacks)
private void AddItemStacks(IBlockEntityContainer chest, IEnumerable<ItemStack> itemStacks)
Line 203: Line 203:
}
}
</syntaxhighlight>
</syntaxhighlight>
This method does just that. It advances the IItemSlot number each time, ensuring not to place more ItemStacks than there are IItemSlots, and sets the IItemSlot.ItemStack value to the current ItemStack in the loop. We are almost there! We have all the pieces. Now we just need to get a reference to our chest that we have already placed and pass it to this method along with the ItemStacks.
This method does just that. It advances the '''IItemSlot''' number each time, ensuring not to place more '''ItemStacks''' than there are '''IItemSlots''', and sets the '''IItemSlot.ItemStack''' value to the current '''ItemStack''' in the loop. We are almost there! We have all the pieces. Now we just need to get a reference to our chest that we have already placed and pass it to this method along with the '''ItemStacks'''.


Lets go back to our PlaceTreasureChest method and replace the last line with the following code snippet.
Lets go back to our '''PlaceTreasureChest''' method and replace the last line with the following code snippet.
<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
BlockPos pos = player.Entity.Pos.HorizontalAheadCopy(2).AsBlockPos;
BlockPos pos = player.Entity.Pos.HorizontalAheadCopy(2).AsBlockPos;
Line 212: Line 212:
AddItemStacks(chestEntity, MakeItemStacks());
AddItemStacks(chestEntity, MakeItemStacks());
</syntaxhighlight>
</syntaxhighlight>
The first line is just capturing the BlockPos position object so that we can use it in two places. The next is the same as before, just placing the chest in the world. Next we go back to the IBlockAccessor to get the block entity at that position. It's important to call GetBlockEntity here because a chest is an Entity. An Entity is something that has extra behavior attached to it as opposed to a normal block. This method returns an IBlockEntity which is a generic interface for all block entities. However we specifically need a block entity that has an Inventory. Since we know the block entity we just placed is a chest then it's safe to to cast the returned IBlockEntity to an IBlockEntityContainer which is a specialized version of IBlockEntity that provides access to an Inventory. Now that we have that we pass it along to our AddItemStacks method we created earlier and additionally pass in the list of ItemStacks that are created by our MakeItemStacks method that we also created earlier. Now if you run the code again and type /treasure you should have random items in there! Try it several times and you will see them change.
The first line is just capturing the '''BlockPos''' position object so that we can use it in two places. The next is the same as before, just placing the chest in the world. Next we go back to the '''IBlockAccessor''' to get the block entity at that position. It's important to call '''GetBlockEntity''' here because a chest is an '''Entity'''. An '''Entity''' is something that has extra behavior attached to it as opposed to a normal block. This method returns an IBlockEntity which is a generic interface for all block entities. However we specifically need a block entity that has an Inventory. Since we know the block entity we just placed is a chest then it's safe to to cast the returned IBlockEntity to an IBlockEntityContainer which is a specialized version of IBlockEntity that provides access to an Inventory. Now that we have that we pass it along to our AddItemStacks method we created earlier and additionally pass in the list of ItemStacks that are created by our MakeItemStacks method that we also created earlier. Now if you run the code again and type /treasure you should have random items in there! Try it several times and you will see them change.


That's really cool and all but not real fun for a game play experience. We want the player to find these chests and encourage them to explore the world! So lets plug in to world gen next!
That's really cool and all but not real fun for a game play experience. We want the player to find these chests and encourage them to explore the world! So lets plug in to world gen next!