Getting started with CrT:
So, you installed CraftTweaker, congratulations!
Now, the next time you start your game, a new folder will appear in your minecraft instance, called scripts
.
Also, note that a new file has appeared called crafttweaker.log
. This file will be important later on!
The scripts folder is the place where all the magic will happen. Initially it is empty, so no actions will take place.
First of all, we will need to create a new file with the file extension .zs
You need to be very careful that it really ends with .zs
, since some Operating systems don't show "known file extensions" like .txt, so it may show the file as myFile.zs
but actually, it is myFile.zs.txt
.
In order to be sure to save it as that name, either go to your windows explorer settings and set Hide known extensions
to false, or create the file using a text editor, and when you save make sure that all File Types
has been selected.
You can verify if the file is of the correct type by looking at it in the file explorer:
When you see ZS-File
then it's correct, if it only sais Text File
then it most likely still is a .txt file!
Now, crating a file is not enough, we need to actually tell CraftTweaker what to do as well!
For this, CraftTweaker uses a scripting language known as ZenScript
.
ZS is closely related to other scripting languages but still it has some own special quirks, so just copy and pasting a JavaScript or Python script will most likely not work.
We will not dive into the differences of ZS to other scripting languages just here, it's time to start scripting!
How else to start a tutorial than by using a Hello World
example?
print("Hello World");
Let's take this example apart:
print
a global function. Any function can be referenced by its qualified name()
this tells the function to actually be executed. Everything between the Round brackets will be anargument
to the function"Hello World"
This is the word(s) to be printed. So-called strings are always in""
;
This lets ZenScript know that the expression is completed. ZS ignores whitespaces after the first one, so without that delimiter ZS would not know when something is done. Just add it at the end of your method calls.
Now, having seen how a method call may look like, let's check the wiki for how to add a recipe:
click
Here we can see that the syntax to create a shaped recipe looks like so:
recipes.addShaped(name,output,inputs,function,action);
From the description we can see that the latter two arguments are optional so we will ignore them for now:
recipes.addShaped(name,output,inputs);
Now, we can see that this time, our function is not called print
but recipes.addShaped
.
Also, we don't only have one argument, but 3: the recipe name, the recipe output and the recipe ingredients.
But what do we actually need to write in there?
Let's have another look at the wiki and check the types of the arguments:
A type is an identifier saying what this is. Not every argument is of the same type after all, some are Items, some are Words, some are Liquids and so on.
A look at the wiki tells us that:
name
is a string and needs to be unique
output
is an IItemStack
inputs
is an IIngredient[][]
Now what does that mean?
We aready encountered the type string, as it was the argument type for the print
function.
A string consists of characters in between ""
;
With strings you need to be careful because usually there are still some guidelines to follow as you can theoretically put anything in between the ""
.
So, let's have a look at the other types:
IItemStack is an item like an Iron ingot.
We can also see that we can get an IItemStack by using the respecting Bracket handler.
Also, we can see that IItemStack extends IIngredient, so we already have a type that we can also use in inputs!
The phrase IItemStack extends IIngredient
means that an IItemStack is a specialized version of an IIngredient.
In other words, every IItemStack is an IIngredient but not every IIngredient is also an IItemStack!
So, we now know how to create a string, IItemStacks and IIngredients.
But we can see that the type of inputs
is not IIngredient but IIngredient**[][]**.
So, what do those square brackets mean?
If you add []
to at type creates a new type that is an array of that type.
An array (also sometimes known as Vector) can be seen as a list of things.
So, an IIngredient[] is a list that contains 0 to more single IIngredient objects.
Successively, an IIngredient[][] is a list that contains IItemStack[] objects, so it is a list of lists of ingredients.
This is also called a 2-Dimensional Array of IIngredient objects.
Why two-dimensional? If you start drawing the array contents you will need to go into two dimensions to do so effectively. An IIngredient[] is one-dimensional, because it can be written in a straight line:
[<minecraft:iron_ingot>, <minecraft:iron_ingot>, <minecraft:iron_ingot>, <minecraft:iron_ingot>, ...]
Now, if we want to do the same for a two-dimensional array, we will first have to create the outer array
[
]
And within that we put many IIngredient[] objects
[
[<minecraft:iron_ingot>, <minecraft:iron_ingot>],
[<minecraft:iron_ingot>, <minecraft:iron_ingot>],
[<minecraft:iron_ingot>, <minecraft:iron_ingot>]
]
If you draw it like that you can see that this creates a table-like structure where each element of the outer array is a row and each element of the inner array is a column in that row.
So you have two dimensions, row and column, wehereas a one-dimensional array only contains the index, which is one dimension.
Okay, so now we know that we need to create a list of list of things to use as ingredients.
We also know that IItemStacks can be used as ingredients.
So, now we know that the array needs to look something like the above.
But now comes the question, how to use that table to show the ingredients of a crafting recipe?
Let's consult the wiki again:
There we can see an example for iron leggings:
recipes.addShaped("CTLeggings", leggings,
[[iron,iron,iron],
[iron,null,iron],
[iron,null,iron]]);
Throughout ZenScript (and therefore also crafttweaker), the word null
is special and means as much as nothing
.
So we can see that the recipe for standard iron leggings has three iron ingots in the first row, one iron ingot, then nothing, then another iron ingot in the second row and that the third row looks like the second row.
So, the position in the array correlates to the position in the recipe, with the inner arrays being the rows in the craftin table consisting of the items to be put in that row (in order).
Now, go ahead and try another recipe.
Now, the example in the CrT wiki for shaped recipes also contains this:
val iron = <minecraft:iron_ingot>;
What does this mean?
val
This is a value, a readonly variable (you can also usevar
which is mutable)iron
The name of the variable= <minecraft:iron_ingot>
The value of the variable;
As always, a statement needs to be ended by this.
This creates a variable named iron
that will hold a reference to an iron ingot.
For you that means that after you have declared that variable, whenever you use the word iron
in this script, ZS will know that you are talking about an iron ingot.
This helps you make your intentions clearer to other readers, for example, if a mod material A is named <my_mod:materials:123>
you can not really see what it stands for.
If you however declare a variable like val materialA = <my_mod:materials:123>;
then you can use that variable inside the scripts and everyone will know what you are talking about.
It also facilitates changing recipes later on, if you for example decided that in your recipes to use any plank instead of a specific one you only need to change what the variable points to (i.e. the thing after the =
)
Let's revive our Hello-World example:
print("Hello World");
Now, say we want to change that to use a variable.
var myVariable = "Hello World";
print(myVariable);
If you want to know more about variable types, you can read more about it here