Modding:Variants: Difference between revisions

From Vintage Story Wiki
m
(Created page.)
 
 
(11 intermediate revisions by the same user not shown)
Line 1: Line 1:
<noinclude><languages/></noinclude>
<noinclude><languages/></noinclude>__TOC__
__TOC__


{{navbox/contentmodding}}
== Basic Understanding ==
Vintage Story has a very large number of items, blocks, and entities, and this is helped by having variants of many objects.
 
Take, for example, the hammer item. There exists 9 hammers in the game and each one has identical functionality, with some minor details changed. Without variants, each hammer would have to be created individually which would result in a lot of time, and a lot of different files.
 
[[File:HammersToShowVariants.png]]
 
By using variants, a single hammer item is created, and variant groups are added to it. Most properties in our hammer item file remain the same, however some are changed dependent on the variant of hammer. Specifically, each hammer has identical functionality, however each hammer has a different texture, tooltier, durability, and attack power.
 
The hammer is referenced throughout this page, so here is the file in its entirety:
{| class="wikitable mw-collapsible mw-collapsed"
|''itemtypes/tool/hammer.json''
|-
|<syntaxhighlight lang="json">
{
code: "hammer",
class: "ItemHammer",
storageFlags: 257,
attributes: {
handbook: {
groupBy: ["hammer-*"]
},
toolrackTransform: {
rotation: { y: 1, z: -1 },
translation: { x: -0.2, y: 0.02 },
scale: 1.5,
},
groundStorageTransform: {
translation: { x: 0, y: -0.16, z: 0.33 },
rotation: { x: 26, y: 88, z: -87 },
scale: 1.01
}
},
behaviors: [{
name: "GroundStorable",
properties: {
layout: 'WallHalves',
wallOffY: 1,
sprintKey: true,
selectionBox: { x1: 0, y1: 0, z1: 0, x2: 1, y2: 0.1, z2: 1 },
collisionBox: { x1: 0, y1: 0, z1: 0, x2: 0, y2: 0, z2: 0 },
}
}],
variantgroups: [
{ code: "metal", states: ["copper", "tinbronze", "bismuthbronze", "blackbronze", "gold", "silver",
"iron", "meteoriciron", "steel"
  ] },
],
tool: "hammer",
damagedby: ["blockbreaking", "attacking"],
heldTpHitAnimation: "smithingwide",
shape: { base: "item/tool/hammer" },
texturesByType: {
"*": {
"metal": { base: "block/metal/ingot/{metal}" },
"wood": { base: "item/tool/material/wood" }
}
},
tooltierbytype: {
"*-copper": 2,
"*-gold": 2,
"*-silver": 2,
"*-bismuthbronze": 3,
"*-tinbronze": 3,
"*-blackbronze": 3,
"*-iron": 4,
"*-meteoriciron": 4,
"*-steel": 5
},
durabilitybytype: {
"hammer-stone": 60,
"hammer-gold": 250,
"hammer-silver": 250,
"hammer-copper": 500,
"hammer-tinbronze": 750,
"hammer-bismuthbronze": 900,
"hammer-blackbronze": 1100,
"hammer-iron": 1800,
"hammer-meteoriciron": 2100,
"hammer-steel": 4500
},
attackpowerbytype: {
"hammer-stone": 1,
"hammer-copper": 1.25,
"hammer-gold": 1.5,
"hammer-silver": 1.5,
"hammer-bismuthbronze": 1.5,
"hammer-tinbronze": 1.75,
"hammer-blackbronze": 2,
"hammer-iron": 2.25,
"hammer-meteoriciron": 2.35,
"hammer-steel": 2.5
},
creativeinventory: { "general": ["*"], "items": ["*"], "tools": ["*"] },
fpHandTransform: {
translation: { x: 0.0468, y: -0.2, z: 0 },
rotation: { x: -33, y: 7, z: 90 },
scale: 2.75
},
guiTransform: {
translation: { x: 0, y: 0, z: 0 },
rotation: { x: -77, y: 46, z: 8 },
origin: { x: 0.59, y: 0.5, z: 0.49 },
scale: 2.6
},
groundTransform: {
translation: { x: 0, y: 0, z: 0 },
rotation: { x: 0, y: 0, z: 0 },
origin: { x: 0.5, y: 0.45, z: 0.5 },
scale: 4.5
},
tpHandTransform: {
translation: { x: -0.65, y: -0.48, z: -0.52 },
rotation: { x: 90, y: 1, z: 0 },
scale: 1
}
}
</syntaxhighlight>
|}
 
== Usage in JSON ==
Variant groups are extremely useful for content and code modders, so it is important to know exactly how to use them in your assets.
 
=== Defining Variant Groups ===
To create a set of variants, you will need to define at least one variant group, which includes the variant ''code'', and all the ''states'' for that variant.<syntaxhighlight lang="json">
variantgroups: [
    { code: "metal", states: ["copper", "tinbronze", "bismuthbronze", "blackbronze", "gold", "silver", "iron", "meteoriciron", "steel"] }
]
</syntaxhighlight>The variant group for the hammer is shown above. The code is ''metal'', and the states are a list of metals. Each ''state'' will result in a new object, with the state ID being added onto the end of the item's code. Hammers, for example, all have a code of "''hammer-{metal}"'', where ''{metal}'' is replaced with the appropriate state.
 
An object can have any number of variant groups. Take a look at the ''seashell.json'' variant groups:<syntaxhighlight lang="json">
variantgroups: [
{ code: "type", states: ["scallop", "sundial", "turritella", "clam", "conch", "seastar", "volute"] },
{ code: "color", states: ["latte", "plain", "seafoam", "darkpurple", "cinnamon", "turquoise"  ] }
]
</syntaxhighlight>Seashells have two variant groups: t''ype'' and ''color''. For each state in ''type'', there exists every state in ''color'', giving a total of 42 different sea shells. Every sea shell in game has a code of "''seashell-{type}-{color}''".
 
Variant states can also be created from any [[Modding:World Properties|World Properties]]:<syntaxhighlight lang="json">
variantgroups: [
{ code: "rock", loadFromProperties: "block/rock" },
],
</syntaxhighlight>
 
==== SkipVariants ====
On occasion, a combination of variant groups can cause the creation of unwanted objects. Say, for example, that turquoise clams were unrealistic and shouldn't be in the game. To remove variants, you can use the '<nowiki/>''SkipVariants''' property. The seashell variants would now look like this:<syntaxhighlight lang="json">
variantgroups: [
{ code: "type", states: ["scallop", "sundial", "turritella", "clam", "conch", "seastar", "volute"] },
{ code: "color", states: ["latte", "plain", "seafoam", "darkpurple", "cinnamon", "turquoise"  ] }
],
skipVariants: [
    "seashell-clam-turquoise"
]
</syntaxhighlight>Any code within skipVariants will not be created in game.
 
==== AllowVariants ====
Sometimes, you may want to specifically choose which variants should be created in game. For example, you may only want a specific selection of seashells to be created in the game for testing purposes. To only allow specific variants, you can use the '<nowiki/>''AllowedVariants''' property. The seashell variants would now look like this:<syntaxhighlight lang="json">
variantgroups: [
{ code: "type", states: ["scallop", "sundial", "turritella", "clam", "conch", "seastar", "volute"] },
{ code: "color", states: ["latte", "plain", "seafoam", "darkpurple", "cinnamon", "turquoise"  ] }
],
allowedVariants: [
    "seashell-scallop-seafoam",
    "seashell-clam-darkpurple",
    "seashell-sundial-latte",
    "seashell-sundial-seafoam"
]
</syntaxhighlight>If ''allowedVariants'' exists, any code that is ''not'' within it will not be created in game.
 
It is important to remember that skipVariants takes presedence over allowedVariants - If an element exists in both the allowed and skip list, it will be skipped.
 
''SkipVariants'' and ''AllowedVariants'' both accept wildcard parameters.
 
=== Wildcards ===
When referencing asset locations from a json file, wildcards can be used to reference any number of assets. By using the wildcard character ('''*''') inside an asset location, any matching code with satisfy the wildcard. The use and purpose of a wildcard depends on where it is being used.
 
Some examples of wildcards are:
 
* '''hammer-*''' - Any hammer will satisfy this condition.
* '''seashell-scallop-*''' - Any seashell with the type ''scallop''.
* '''seashell-*-latte''' - Any seashell with the color ''latte''.
* '''*-copper''' - Any item, block, or entity who's last variant state is ''copper''.
Wildcards are very versatile and are used commonly throughout json files. Wildcards can be used in, but not including:
 
* SkipVariants and AllowedVariants when creating an object.
* ByType variant groups. Any matching code will use the byType property.
* Recipe ingredients. Any matching code will work as a valid ingredient for the recipe.
 
=== {Variant Group} Substitutions ===
One way to specify slight differences between object variants is by using the variant substitution system. In an asset file that defines variants, any property that accepts a string can include a variant code surrounded by { }'s. This will then get substituted by the variant's state. For example, each seashell chooses its texture based on its ''color'' variant, and its shape based on its ''type'' variant.<syntaxhighlight lang="json">
textures: {
color: { base: "block/creature/seashell/{color}" }
}
</syntaxhighlight><syntaxhighlight lang="json">
shape: {
base: "block/seashell/{type}"
}
</syntaxhighlight>For example, a plain clam would look for a texture at textures/''block/creature/seashell/plain'', and would look for a shape at shapes/''block/seashell/clam''.
 
=== ByType Properties ===
Another way to add differences to variants is by adding '''byType''<nowiki/>' onto the end of ''any'' property in a file that has defined variants.
 
When using a '''key''' with byType, the '''value''' becomes a new dictionary, with the keys being asset locations and the values being whatever the original property was.
 
For example, the following code would set all hammers to have the same durability:<syntaxhighlight lang="json">
durability: 60,
</syntaxhighlight>However, hammers different durabilities based on their variant type. The following code sets different durabilities for each hammer type:<syntaxhighlight lang="json">
durabilitybytype: {
"hammer-stone": 60,
"hammer-gold": 250,
"hammer-silver": 250,
"hammer-copper": 500,
"hammer-tinbronze": 750,
"hammer-bismuthbronze": 900,
"hammer-blackbronze": 1100,
"hammer-iron": 1800,
"hammer-meteoriciron": 2100,
"hammer-steel": 4500
},
</syntaxhighlight>Our durability property has now become durability''bytype'', and accepts a dictionary of all the hammers to each have their own durability.
 
By using a combination of ByType Properties, wildcards, and Variant Substitutions, objects can be created much more efficiently. The following code is an excerpt from the ''blade.json'' tool.<syntaxhighlight lang="json">
shapeByType: {
"blade-falx-*": { base: "item/tool/blade/falx/{metal}" },
"blade-longsword-admin": { base: "item/tool/blade/admin" },
"blade-*-ruined": { base: "item/tool/blade/ruined/{type}" },
"*": { base: "item/tool/blade/{type}" }
},
</syntaxhighlight>There's a few important things to note here:
 
* All JSON properties are case-insensitive in content mods, including the ByType suffix.
* The ByType keys can use wildcards to group variants together by their type.
* The values in ByType can still use variant substitutions.
* A wildcard of "'''*'''" will simply satisfy ''every'' object.
* ByType keys are analysed from top to bottom - If an object's code satisfies more than one key in byType, it will always use the topmost entry.
 
== Detailed Understanding ==
 
=== Calculating Variant Asset Locations ===
In Vintage Story, all items, blocks, and entities are loaded as ''registry objects'' ([https://github.com/anegostudios/vsessentialsmod/blob/master/Loading/RegistryObjectType.cs GitHub]). When a registry object is loaded into the game, before any other properties are read, both its code and variant properties are analysed. There exists 3 json properties to control the creation of variants, which are:
 
* '''VariantGroups''' - An array of all variant groups available for the object.
* '''SkipVariants''' - An array of wildcards - Any variants that match these wildcards will never be created.
* '''AllowedVariants''' - An array of wildcards - If this property exists, then ''only'' variants that match these wildcards will be created, unless they exist in the skip list.
 
When these arrays are loaded, the game begins to resolve the variant entries by creating unique asset locations for each variant entry. There can be numerous variant groups for any object, so every possible permutation for all the variant states is calculated. This is easy to see if we analyse the ''seashell.json'' file. <syntaxhighlight lang="json">
variantgroups: [
{ code: "type", states: ["scallop", "sundial", "turritella", "clam", "conch", "seastar", "volute"] },
{ code: "color", states: ["latte", "plain", "seafoam", "darkpurple", "cinnamon", "turquoise"  ] }
]
</syntaxhighlight>Seashells have two variant groups: t''ype'' and ''color,'' meaning that every variant of seashell will follow the code of "''seashell-type-color''". '''For every ''type'' of seashell, there exists every ''color'' of that type.''' In total, as there are 7 ''types'' and 6 ''colors'', this resolves into a total of 42 unique sea shell asset locations.
 
All permutations are analysed against the ''skip'' and ''allowed'' arrays, creating a populated list of every allowed code.
 
=== Analysing ByType & {VariantCodes} ===
When all the available variant permutations exist, all json properties are cloned for each resolved variant.
 
Any property in a json file can be defined as '<nowiki/>''byType''', except from the ''code'' and variant properties. The ByType properties are analysed based on the provided wildcards, and properties that do not match the variant are removed.
 
After the ''byType'' is analysed, any variant groups referenced in curly braces ('''{ }''') are substituted with their appropriate variant state.
 
When all the variants, byTypes, and variant groups are resolved, the cloned json properties are loaded as a unique object.
 
== Conclusion ==
Variants are exceptionally useful when creating content for Vintage Story, and it is highly recommended to understand how and why variants are used. To read about more content modding concepts, go to the [[Modding:Content Mod Concepts|Content Mod Concepts]] page.{{navbox/contentmodding}}
Confirmedusers
637

edits