News:

Do you need help?
Simutrans Wiki Manual can help you to play and extend Simutrans. In 9 languages.

[Tutorial] You got a graphic - now what?

Started by Leartin, February 01, 2021, 02:50:57 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Leartin

There already exist a bunch of guides on how to create a graphic, both in the Simutrans community and outside. This tutorial picks up from there - it's assumed that you can draw or render computer graphics and only need to know the Simutrans specific parts.

Some things to get out of the way first:
  • I am not done - there is more to come (some already written).
  • I request that discussions, questions, corrections etc. are posted in another thread that accompanies this one - "You got a graphic - now what?" - Discussions
  • If you feel like this shouldn't be in the forum but somewhere else, you can place it there! Since the images are CC-BY-SA 3.0 anyway due to the pakset they come from, let's just make the whole thing CC-BY-SA 3.0 or higher. To fulfill the attribution part, just link back to this thread on the international Simutrans forums.
  • For that matter, all images were made by the pak192.comic team and I credit images individually if they are not by me or a team effort.
  • When you mirror this, please also provide shelter for the images, as they could get lost no matter where they are.

Leartin

A dat-file is a plain text file which contains the description of what an object is, how it should behave and which images it is represented by.
A pak-file is a package that contains both the image and the description from the dat, readable for Simutrans.
makeobj is the tool which allows to merge dat-files and images to create pak-files. You can get it from sourceforge:
https://sourceforge.net/projects/simutrans/files/makeobj/
makeobj has no graphical interface, it's a command line tool. Place a dat-file and makeobj in the same folder, open the terminal/command prompt in that folder, and write "makeobj pak<size>", where <size> is to be replaced with the size of the pakset you want to pak for. Our examples will be from pak192.comic, for these you'd use "makeobj pak192".

Try to do this with existing source-files (pngs and dats) from open Simutrans paksets to make sure it works before you try to write your own dat-files. Public repositories are:
https://sourceforge.net/p/simutrans/code/HEAD/tree/
https://github.com/Flemmbrav/Pak192.Comic

Leartin

Probably the easiest building to start with is a single-tile citybuilding.
The graphic we use is this one:

Since it's located in the desert, the artist didn't bother to provide a snowy image. On most maps, that won't matter, but if map settings allow snow in the desert climate, these will stick out.
Writing a minimal dat-file for this building is straightforward:


name=desert_building
obj=building
type=res
BackImage[0][0][0][0][0][0]=./desert_building.0.0


Let's see what these lines do.
Generally, they consist of three parts. A 'parameter' on the left, an equal sign, and a 'value' on the right. For most parameters, a default value exists and is used when the dat-file does not specify it. That's how those few lines work, but in most cases you'd want more than that.

The parameter "name" is used for an unique identifier, such that the object can be referenced. No two objects can share a name, as they would overwrite each other and only one would exist in the game. Ideally, the value for the name parameter only consists of alphanumeric symbols and underscores (latin alphabet and digits, no language-specifics like Umlauts, no spaces, no special symbols like #%&§, no brackets,...).
Don't worry about the appearance of that identifier in the game - that's what translations are for (though that's a different topic)

The parameter "obj" is used to tell which type of object it is. Possible values are eg. vehicle, way or - in our case - building.
Since there are many kinds of buildings that are treated differently by the game, we also need to use the parameter "type" to be a bit more specific. "res" is a residential building that cities are composed of, together with "ind" (industrial) and "com" (commercial).

The parameter "BackImage" defines the graphic. There are several different parameters for graphic definition that depend on the type of object, but the main one for buildings is BackImage. We'll look at the numbers in the six brackets later, what we are interested in right now is the value - the part right of the equal sign.
First, we need the relative path and filename of our image. "./" stands for "this folder", the folder the dat-file is in. Our image in that same folder is named "desert_building.png", but we ommit the file extension ".png"
It's a good idea to have the image file in the same folder as the dat-file or in a subfolder of that folder (eg. "./images/desert_building.0.0" if it was in a subfolder "images"). But it is possible to access other folders. To go up in the hierarchy, use "../" For example, if the image is in some folder "my_building" and the dat file in a subfolder "dats", "../desert_building.0.0" would work.
The two zeroes indicate the position of the image in our image file. Since it's pak192.comic, any image gets cut into squares of 192x192 pixels, and those two numbers tell row and column of the square that contains the image. Now in this case, there is only one square, so let's look at a different example:


name=desert_building_01
obj=building
type=res
BackImage[0][0][0][0][0][0]=./desert_buildings.0.0
-
name=desert_building_02
obj=building
type=res
BackImage[0][0][0][0][0][0]=./desert_buildings.0.1
-
name=desert_building_03
obj=building
type=res
BackImage[0][0][0][0][0][0]=./desert_buildings.1.0
-
name=desert_building_04
obj=building
type=res
BackImage[0][0][0][0][0][0]=./desert_buildings.1.1

We now have four different buildings (hence we give them different unique names). A minus/dash is used to seperate two objects within the same dat file. Since the first number in the BackImage-value (on the right side) indicates the row and the second number the column, building_01 is in the top left, building_02 in the top right, building_03 in the bottom left and building_04 in the bottom right of the image file.

Those four lines are all you need to get the building into the game. You might see it spawned in naturally if you started a new game, or you can find it in the citybuilding dialog of the map editor accessible as public player.

If we look at these buildings in the game now, they seem a bit off though:

They are 10 pixel too far up. There is a reason for that: The sandy ground that comes with the building is intended to overlap roads that run next to them. But if we moved them down in a graphics software, makeobj would think that those sand pixels are outside the tile and shouldn't be drawn. So instead of moving those 10 pixel in a graphics software, we tell the game to move the graphic with an offset.

obj=building
name=desert_building
BackImage[0][0][0][0][0][0]=./desert_building.0.0,0,10

As you can see, an offset is added by extending the value by two pairs of a comma and a number. The first number is the horizontal offset and causes the graphic to appear that many pixels to the right. The second number is the vertical offset and causes the graphic to appear that many pixels lower. In case you want to move the graphic up or to the left, you can use negative numbers.

Let's set some more of the optional parameters and have a look at those:

name=desert_building
obj=building
type=res
copyright=Leartin
intro_year=1500
level=3
chance=30
climates=desert
BackImage[0][0][0][0][0][0]=./desert_building.0.0,0,10

By the way, the order you place them in doesn't matter technically, but it's still nice to have same or similar order, at least within a pakset.

"copyright" is where you place your name, it will show up in the game as "painted by" in almost all objects. If you edited someone elses work, don't forget to credit them here as well!
"intro_year" defines when this building can spawn. It's often accompanied by "intro_month" to be more precise, especially for real-life-vehicles. Likewise, the parameters "retire_year" and "retire_month" end the spawning. There is also "preservation_year" and "preservation_month" to disable replacement of old buildings.
"level" states how many pax and how much post is generated (or capacity in case of stations). For citybuildings, buildings of higher levels replace those of lower levels over time, and are typically visually bigger. Compare with other buildings of the pakset you work for to find a good value.
"chance" is used to make some buildings spawn more or less often than others. The base spawn rate is 100. Imagine that whenever a building is buildt or upgraded, all buildings that could fit the spot throw that many snippets of paper with their name in a big barrel and hope to get drawn.
To define where a building might appear, use the parameter "climate" with allowed climates as value, seperated by commata. The game knows 7 climates: desert, tropic, mediterran, temperate, tundra, rocky and arctic. Most paksets renamed them via translations, but in the dat you still need to use the original name.

Leartin

#3
Let's look at a different building for a change.

This is one building, but it has five seasons ("snowy" is a season different from "winter") and four rotations. This is where the brackets for the image definition come into play - most objects can have several images, and the numbers in the brackets help differentiate them. What each number means can be different from object to object, but in case of buildings, it works like this:
BackImage[A][B][C][D][E][F]
A: rotation
B: north-south-position (=row)
C: west-east-position (=column)
D: heightlevel
E: animation frame
F: season


B, C and D become relevant for larger building. For this one, we only need A and F. Since there are 20 images, you can write 20 lines, one for each image:
BackImage[0][0][0][0][0][0]=./rot_seas.0.3
BackImage[1][0][0][0][0][0]=./rot_seas.0.2
BackImage[2][0][0][0][0][0]=./rot_seas.0.1
BackImage[3][0][0][0][0][0]=./rot_seas.0.0
BackImage[0][0][0][0][0][1]=./rot_seas.1.3
BackImage[1][0][0][0][0][1]=./rot_seas.1.2
BackImage[2][0][0][0][0][1]=./rot_seas.1.1
BackImage[3][0][0][0][0][1]=./rot_seas.1.0
BackImage[0][0][0][0][0][2]=./rot_seas.2.3
BackImage[1][0][0][0][0][2]=./rot_seas.2.2
BackImage[2][0][0][0][0][2]=./rot_seas.2.1
BackImage[3][0][0][0][0][2]=./rot_seas.2.0
BackImage[0][0][0][0][0][3]=./rot_seas.3.3
BackImage[1][0][0][0][0][3]=./rot_seas.3.2
BackImage[2][0][0][0][0][3]=./rot_seas.3.1
BackImage[3][0][0][0][0][3]=./rot_seas.3.0
BackImage[0][0][0][0][0][4]=./rot_seas.4.3
BackImage[1][0][0][0][0][4]=./rot_seas.4.2
BackImage[2][0][0][0][0][4]=./rot_seas.4.1
BackImage[3][0][0][0][0][4]=./rot_seas.4.0

For rotations, "0" is facing south (that is, if the building spawns north of a road tile, it will spawn in rotation 0) and from there, rotate against the clock (east, north, west). For this building, the "front" is the side with the door.
For seasons, it always starts with summer. With five seasons, it goes summer, fall, winter, spring, snowy. If there are only two seasons defined, the first (0) is "non-snowy" and the second (1) is snowy, though most people just call "non-snowy" summer. Using four seasons is also possible, it goes summer, fall, winter, spring and ignores snowfall.

This block of image definitions can be reduced to just one line using shorthand notation.
BackImage[0-3][0][0][0][0][0-4]=./rot_seas.<$1>.<3-$0>

Probably doesn't look any less intimidating, but this can be quite useful.
On the parameter side, in the brackets, you can write several numbers at once, separated with commas, or a range with a dash. So [0-3] is the same as [0,1,2,3] and expands into 4 lines with these four different numbers.
On the value side, you can put calculations in pointy brackets and reference the numbers on the parameter side using a dollar sign and a digit which corresponds to the order of appearance, starting with zero.
A very small example:

[0-1][0-2]=<2-$1>.<$0>
=
[0][0-2]=<2-$1>.0
[1][0-2]=<2-$1>.1
=
[0][0]=<2-0>.0
[0][1]=<2-1>.0
[0][2]=<2-2>.0
[1][0]=<2-0>.1
[1][1]=<2-1>.1
[1][2]=<2-2>.1
=
[0][0]=2.0
[0][1]=1.0
[0][2]=0.0
[1][0]=2.1
[1][1]=1.1
[1][2]=0.1


Allowed calculations are addition, subtraction, multiplication, division and modulo. Naturally, this shorthand notation requires a bit of planning when creating the graphics.

There are two more parameters required for that building, "needs_ground" and "dims"
needs_ground=1
dims=1,1,4


"needs_ground" states whether or not the building fills the tile. Especially larger buildings are usually on completely plastered ground, so having the ground underneath would be a waste of resources and is by default disabled. But it's better to use transparency for grass rather than actually painting it if your building is supposed to blend in well in several climates with different grass colors. If you forget that your building needs ground, you'll get some nasty visual bugs. Especially nasty because you might not even see them on first glance.

"dims" is short for dimensions and states how long and wide the building is, as well as the number of rotations. Essentially, you can think of dims as "dims= B,C,A" using the letters for image definition. If no dimensions are given, it defaults to a single-tile building without rotations, and any images defined for rotations or other tiles will be ignored.
We'll look closer at dimensions in a moment, but let's first look at building blocks.

Leartin


painted by Flemmbrav

This building takes advantage of eight rotations to get corner buildings. If there is a road to the south, you get rotation 0. If there is a road to the east, you get rotation 1. But if there is a road to the south and the east, you get rotation 4 - a corner. 8 rotations in this way are only possible for single-tile citybuildings (res, com, ind).

In Pak192.Comic, most of the 8-rotational buildings just repeat the same graphic for south-facing and north-facing as well as east-facing and west-facing. You may also note that this building is higher than one tile. This is where [D] comes into play, the height level. Let's just do the summer graphic for now:

BackImage[0][0][0][0][0][0]=./blox.1.0
BackImage[0][0][0][1][0][0]=./blox.0.0
BackImage[1][0][0][0][0][0]=./blox.1.1
BackImage[1][0][0][1][0][0]=./blox.0.1
BackImage[2][0][0][0][0][0]=./blox.1.0
BackImage[2][0][0][1][0][0]=./blox.0.0
BackImage[3][0][0][0][0][0]=./blox.1.1
BackImage[3][0][0][1][0][0]=./blox.0.1
BackImage[4][0][0][0][0][0]=./blox.1.2
BackImage[4][0][0][1][0][0]=./blox.0.2
BackImage[5][0][0][0][0][0]=./blox.1.3
BackImage[5][0][0][1][0][0]=./blox.0.3
BackImage[6][0][0][0][0][0]=./blox.1.4
BackImage[6][0][0][1][0][0]=./blox.0.4
BackImage[7][0][0][0][0][0]=./blox.1.5
BackImage[7][0][0][1][0][0]=./blox.0.5


Each rotation requires two images, one for the bottom (eg. the first line) and one for the roof (eg. the second line). And of course, here is the shorthand version:
BackImage[0-1][0][0][0-1][0][0]=./blox.<1-$1>.<$0>
BackImage[2-7][0][0][0-1][0][0]=./blox.<1-$1>.<$0-2>


And shorthand including snowy:
BackImage[0-1][0][0][0-1][0][0-1]=./blox.<1-$1+2*$2>.<$0>
BackImage[2-7][0][0][0-1][0][0-1]=./blox.<1-$1+2*$2>.<$0-2>


For these 8-rotational buildings, pak192.comic also uses the parameter "clusters". Clusters can be used to define that a building belongs to a specific group. The clusters are numbers from 1 to 32, any building can be member of any number of clusters. Remember how chance decides which building gets to be buildt using the barrel-lottery? Clusters corrupt that system with some nepotism: If buildings next to the building spot belong to a cluster group, all buildings also belonging to that cluster group get much higher chances of being buildt.

Leartin

You may notice that there are transparent (light blue) areas in front of the building. One could just use the needs_ground parameter to make them grass (or snow), but there are actually plants. They are just in a seperate graphic.

If you think about it, buildings rarely change throughout the seasons, except for when they are snow-covered. But foliage changes a lot. So in this example, the foliage was seperated from the main building and will be placed as a FrontImage. So we gotta talk about Back- and Frontimage.

FrontImage is intended to be used for platforms and depots, buildings that share the tile with vehicles. BackImages are drawn behind vehicles, FrontImages in front of them. Therefore, it's possible for a vehicle to go inside a depot, simply by having the back wall as BackImage and the front wall and roof as FrontImage.
But most Buildings don't come into contact with vehicles directly, so FrontImages are just an additional image on the same tile. A common application is to use BackImage for anything static and FrontImage for animated parts (smoke, industrial fans etc.)

First of all, we need 5 seasons now, four of which use the same graphic. Writing each line seperately that's 80 lines, so we only do shorthand. We can just have snowy as extra lines:
BackImage[0-1][0][0][0-1][0][0-3]=./blox.<1-$1>.<$0>
BackImage[2-7][0][0][0-1][0][0-3]=./blox.<1-$1>.<$0-2>
BackImage[0-1][0][0][0-1][0][4]=./blox.<3-$1>.<$0>
BackImage[2-7][0][0][0-1][0][4]=./blox.<3-$1>.<$0-2>


Frontimages work exactly the same as backimages, except they don't exist on upper height levels.
FrontImage[0-1][0][0][0][0][0-4]=./foliage.<$1>.<$0>
FrontImage[2-7][0][0][0][0][0-4]=./foliage.<$1>.<$0-2>


In p192c, the same foliage is used for several different buildings. Likewise, different foliage could be applied to the same building. Mixing and matching Back-and FrontImage which happen to contain compatible images can be used to get more variety without increasing the amount of images.

Leartin

#6


This time, I start with a screenshot so we know where we want to end up. It's a building large enough to spread over two tiles, a "one by two". The crane is a FrontImage, so that we can animate via offset without a need for additional graphics. Not all animations work like that - most of the time, each frame shows a different image, which just means instead of changing the offset, you change which image is to be shown.

First, an important consideration: This building is not square. So if you rotate the map, it won't stay in the same shape, but will turn from a "one by two" into a "two by one". This means any rectangular building needs at least two rotations to avoid breaking when the map is rotated. If the shape is weirder than a rectangle, maybe with a missing corner, all four rotations might be required.

So fresh from the pixel manufacture, we have created these images:



Now we need to cut them up. At this point, Tilecutter comes in handy, since it automates that process. We'll do it manually anyway, since it's a small example and it's useful to understand the process. All we need to do is create a tile-sized square (192x192 in our case) and shave off the bottom corners to get this shield-like shape.



This shape represents the possible content of a single tile, hence we want to take chunks of that size from our multi-tiled building. Place your graphic in a 48px grid, move the shield to the backmost tile (0,0), get a selection from it, and transfer that selection of the building to a new layer. Then, you can move it to an appropriate place on the sheet. If you are not done, move the shield by half a tilesize (96px) left or right and a quarter tilesize (48px) down to grab the next part.



Since this building has only two tiles, you only have to do it once for each rotation and season, so 4 times in total, to get to this:


One thing you need to understand though: There is technically no reason to cut precisely like that. A building that's larger than one tile is basically the same as individual single-tile-buildings that just happen to be next to each other. Since our example isn't very 3D, it's not the best to showcase this, but you could cut a bit more content aware between two tiles as well:


What's better? In earlier versions of the game, it didn't matter at all. But ever since it's possible that tiles of a multitile building are located in different climates, it's also possible one of them is in the arctic. In this case, half the building might be snow-covered while the other half isn't. That's why it would be slightly better to cut with more awareness of the buildings threedimensional shapes, but it's more work for little gain, especially considering that non-content-aware cutting can be done automatically (eg. Tileccutter)

Now to the actual dat file. We start with the dims parameter. It's a "one by two" with two rotations, so the value is "1,2,2". Make sure you always look at the dimensions in rotation zero, else it might get scrambled a bit. Any "one by two" can also be defined as "two by one" by switching even and odd rotations, but there isn't any reason to do that.

For the images, we can set them one by one:
BackImage[0][1][0][0][0][0]=./crane.0.0
BackImage[0][0][0][0][0][0]=./crane.0.1
BackImage[1][0][0][0][0][0]=./crane.0.2
BackImage[1][0][1][0][0][0]=./crane.0.3
BackImage[0][1][0][0][0][1]=./crane.0.4
BackImage[0][0][0][0][0][1]=./crane.0.5
BackImage[1][0][0][0][0][1]=./crane.0.6
BackImage[1][0][1][0][0][1]=./crane.0.7


or, using shorthand (not that useful here):
BackImage[0][0-1][0][0][0][0-1]=./crane.0.<1-$0+$1*4>
BackImage[1][0][0-1][0][0][0-1]=./crane.0.<2+$0+$1*4>


So we want our crane to go back and forth, using offsets. In a nutshell, that's surprisingly simple:
FrontImage[0][1][0][0][0][0-1]=./crane.0.8,0,0
FrontImage[0][1][0][0][1][0-1]=./crane.0.8,2,-1
FrontImage[0][1][0][0][2][0-1]=./crane.0.8,4,-2
FrontImage[0][1][0][0][3][0-1]=./crane.0.8,6,-3
FrontImage[0][1][0][0][4][0-1]=./crane.0.8,8,-4
[...]

The number in [E] is increasing to denote following frames, and with each frame, the crane moves one pixel up and two to the right. I didn't write it out in full here, but it would have to stop and return eventually since after the last frame, it loops back to the first frame. Note that shorthand notation was used for the seasons in the parameter, but there are no pointy brackets on the right. This just means the crane is exactly the same whether it snows or not.

Doing the same thing with shorthand, you get to something like this:
FrontImage[0][1][0][0][0-9][0-1]=./crane.0.8,<-2*$0>,<$0>
FrontImage[0][1][0][0][10-19][0-1]=./crane.0.8,-20,10
FrontImage[0][1][0][0][20-39][0-1]=./crane.0.8,<-2*(30-$0)>,<30-$0>
FrontImage[0][1][0][0][40-49][0-1]=./crane.0.8,20,-10
FrontImage[0][1][0][0][50-59][0-1]=./crane.0.8,<-2*($0-60),<$0-60>

So the crane moves down-left for 10 frames, waits for 10 frames, moves up-right for 20 frames, waits for 10 frames and moves back down-left. It's nice, but starts and stops very abruptly. So here is an advanced suggestion (please don't feel like you have to understand that, though):
FrontImage[0][1][0][0][0-59][0-1]=./crane.0.8,<($0*$0)/50>,<-($0*$0)/100>
FrontImage[0][1][0][0][60-179][0-1]=./crane.0.8,<144-((($0-120)*($0-120))/50)>,<-(72-((($0-120)*($0-120))/100))>
FrontImage[0][1][0][0][180-239][0-1]=./crane.0.8,<(($0-240)*($0-240))/50>,<-(($0-240)*($0-240))/100>


Here, we multiply $0 with itself to get a square. Square numbers grow quadratic rather than linear, so in the first 60 frames, the movement speed increases. Then it gets inverted, so after half the distance the crane starts slowing down. Same on the way back.

We used 60 frames on linear movement, but 240 frames on quadratic movement. Does that mean the linear crane will go 4 times as fast? Good question - yes, indeed it will, unless we tell it not to. The animation speed can be set manually using the parameter "animation_time". The value is how long each frame is visible in milliseconds. But don't think of fast modern games with 60 fps - Animations in Simutrans are better enjoyed at around 4-5 fps, or 200 to 250 milliseconds.
Sidenote: Factories can do animations, but won't play the animation unless they are producing, which makes them a bit annoying to test ingame.

Another difference to the buildings that came before: This would be an industrial citybuilding, so the type value is "ind". Opposed to residential, industrial buildings receive half as much mail, while commercial buildings (com) receive twice as much. More importantly though, the three types work a bit like clusters in that they tend to stick together (with the specifics defined by the pakset), such that you can get industrial districts, residential districts and commercial districts without "districts" being an actual thing.

Leartin


This cathedral takes up 3x3 tiles and it's spires are high enough to reach up in the next height level. We can use the same technique we used for the crane, but it gets tiresome. To tighten the workflow, let's instead create a cutting template. To do so, we again begin with our one-tile "shield". Instead of using it's selection starting from the back, we copy it and start from the front, using the same distances of half the tilesize to a side and a quarter tilesize up. Each time, we alter the color of the shield slightly.

Here, I made it 3x3 just like the cathedral. If we need a bigger template, we can copy and move this 3x3 rather than singletons to get a 6x6 quickly.
What's missing with this template is additional height for the spires. To get them, we need a square in tile-size and work similarly to the shield, combining them in the end.

For even higher buildings, we would just stack the square elements a few times, but that's rarely required.

Place the template on a separate layer in the same position as the building and get selections from the differently colored segments to rip the cathedral apart, as is shown:


But there is something to consider here. The back end of the roof would be split in two different images. While this is generally fine, we could just keep it in the corner graphics and ignore the center ones. This doesn't change much, but our sprite sheet gets a bit neater.

In case you use Photoshop or other tools that allow macro recording, you can simply record the process of cutting and moving tiles around. Then, you can apply it on the snowy version, or other buildings of the same size.


It's dat-time again. First the dimensions: It's a 3x3 without rotation, so the value for the parameter "dims" is "3,3,1". You can use comments to keep track of the various definitions, it's just a line started with a number sign/hash tag (#)

# The regular images on the ground are:
BackImage[0][0][0][0][0][0]=./cathedral.0.0
BackImage[0][0][1][0][0][0]=./cathedral.0.1
BackImage[0][0][2][0][0][0]=./cathedral.0.2
BackImage[0][1][0][0][0][0]=./cathedral.1.0
BackImage[0][1][1][0][0][0]=./cathedral.1.1
BackImage[0][1][2][0][0][0]=./cathedral.1.2
BackImage[0][2][0][0][0][0]=./cathedral.2.0
BackImage[0][2][1][0][0][0]=./cathedral.2.1
BackImage[0][2][2][0][0][0]=./cathedral.2.2

# And the snowy images are this:
BackImage[0][0][0][0][0][1]=./cathedral.0.3
BackImage[0][0][1][0][0][1]=./cathedral.0.4
BackImage[0][0][2][0][0][1]=./cathedral.0.5
BackImage[0][1][0][0][0][1]=./cathedral.1.3
BackImage[0][1][1][0][0][1]=./cathedral.1.4
BackImage[0][1][2][0][0][1]=./cathedral.1.5
BackImage[0][2][0][0][0][1]=./cathedral.2.3
BackImage[0][2][1][0][0][1]=./cathedral.2.4
BackImage[0][2][2][0][0][1]=./cathedral.2.5

# For the spires higher up, it's this:
BackImage[0][0][0][1][0][0]=./cathedral.3.1
BackImage[0][2][0][1][0][0]=./cathedral.3.0
BackImage[0][0][2][1][0][0]=./cathedral.3.2
BackImage[0][0][0][1][0][1]=./cathedral.3.4
BackImage[0][2][0][1][0][1]=./cathedral.3.3
BackImage[0][0][2][1][0][1]=./cathedral.3.4


But of course this can be all be shortened a bit again:
dims=3,3,1
BackImage[0][0-2][0-2][0][0][0-1]=./cathedral.<$0>.<$1+$2*3>
BackImage[0][0][0][1][0][0-1]=./cathedral.3.<1+$0*3>
BackImage[0][2][0][1][0][0-1]=./cathedral.3.<0+$0*3>
BackImage[0][0][2][1][0][0-1]=./cathedral.3.<2+$0*3>

If you like puzzles, you might wonder if there is a way to shorten it down to one line. It certainly is, if you rearrange the image file. All images of the higher layer need to be positioned like the images on the ground, just 3 rows further down. This means you get two more lines of images, with most positions empty. I rather have some more lines of text than some more lines of images, but that's a personal preference.

BackImage[0][0-2][0-2][0-1][0][0-1]=./cathedral.<$0+$2*3>.<$1+$3*3>
Note how seasons became referenced by $3 instead of $2 due to introduction of a new variable. (Another square bracket now has more than one number in it)

This building is also a curiosity, not a regular citybuilding like the ones before. The value for the "type" parameter is "cur". Curiosities can be separated in two kinds, city and countryside. Curiosities in the countryside only spawn at the beginning of the game, while curiosities in the city spawn when a certain population is reached.
The parameter used for this distinction is "build_time". If it's value is 0, it's a countryside curiosity. If it's value is any other number, it's a city curiosity and will spawn in each city when that many inhabitants are reached. While countryside curiosities still play the chance-barrel-lottery, city curiosities do not. Instead, the chance parameter decides the probability they actually spawn when the inhabitant requirement is met. That is, a city curiosity with a chance value of 30 will be in 30% of all cities which are large enough and have a suitable area (free flat space of the right climate).

prissi

It is important how buildings are cut. If the building is on an artifical slope, the tile in front of it must not have something produding into it, or it will be overdrawn by the back tile. So one must leave a hole, like you did in your example.

Also, if you use tile cutter, then there is already an output of the dat file which make live easier for most but the very advanced examples in your tutorial.

Leartin

Quote from: prissi on February 02, 2021, 02:23:43 AM
It is important how buildings are cut. If the building is on an artifical slope, the tile in front of it must not have something produding into it, or it will be overdrawn by the back tile. So one must leave a hole, like you did in your example.
Sorry, I'm not clear what you are referring to at all. I don't think I expierienced what you described, even if there is no "hole" in the back tile and the graphic is actually different there from the front tile.
Quote from: prissi on February 02, 2021, 02:23:43 AM
Also, if you use tile cutter, then there is already an output of the dat file which make live easier for most but the very advanced examples in your tutorial.
Certainly, but "Just use a tool that does everything for you" won't be much of a tutorial. Tilecutter is mentioned and linked. The initiating thread started because tilecutter is bugged with transparency, though, so that doesn't help at this point in time.


If a moderator could please move this and the post above to the appropriate thread - https://forum.simutrans.com/index.php/topic,20779


Leartin

We used shorthand notation before, but we cheated a little bit - the graphics already were arranged in such a way that shorthand would be easy. So let's have another look, going more into details.
Let's start out with a simple 2x2 building - no rotations, no seasons. It's cut in such a way that the tile at position (0,0), the north-west corner, is also at location (0,0) in the sprite sheet, first row, first column.

The top line is just a comment to keep track which variable refers to which bracket.

#         -  $0   $1   -  -  -
BackImage[0][0-1][0-1][0][0][0]=./townhall.<$0>.<$1>


Now, we want to add seasons. How can we do this, conceptually?
First, imagine it was a simple 1x1. We'd just arrange the seasons in a row or column:

#         -  -  -  -  -  $0
BackImage[0][0][0][0][0][0-4]=./townhall.0.<$0>

A 2x2 is the same, but instead of having one column per season, it's two. So we multiply the season-variable by two and add it to the tile-variable.


          -  $0   $1   -  -  $2
BackImage[0][0-1][0-1][0][0][0-4]=./townhall.<$0>.<$1+$2*2>

The season-variable, which was "$0" when it was the only one, became $2, since $0 and $1 are already occupied by earlier brackets for the building dimensions.
Let's repeat the process for rotations. Again, we can choose between column and rows. Rotations alone:

#         $0   -  -  -  -  -
BackImage[0-3][0][0][0][0][0]=./townhall.<$0>.0

And therefore, combined with what we have already:


          $0   $1   $2   -  -  $3
BackImage[0-3][0-1][0-1][0][0][0-4]=./townhall.<$1+$0*2>.<$2+$3*2>

This time, because rotation comes first, it stayed "$0", but all other variables increased by one.

There are other options as well. What if we wanted to have our graphics in a long thin strip?

#         $0   -  -  -  -  -
BackImage[0-3][0][0][0][0][0]=./townhall.0.<$0>

Could be combined with the rest as


          $0   $1   $2   -  -  $3
BackImage[0-3][0-1][0-1][0][0][0-4]=./townhall.<$1>.<$2+$3*2+$0*10>

...so where does that ten come from? Before, we multiplied by two to have space for two tiles of the building. Now, we need to have space for those two tiles in all their seasons, and 2*5 is 10.
Rotations and seasons could be switched up. In that case, you'd have all rotations of a season next to each other, rather than all seasons of a rotation.


          $0   $1   $2   -  -  $3
BackImage[0-3][0-1][0-1][0][0][0-4]=./townhall.<$1>.<$2+$0*2+$3*8>


Admittedly, it's a bit annoying that variables can change what they stand for. It would be easier if $0 was always rotation, $5 always seasons etc. With a bit of a trick, you can achieve that. Instead of 

#         -  -  -  -  -  $0
BackImage[0][0][0][0][0][0-4]=./townhall.0.<$0>

you can also write

#         $0   $1   $2   $3   $4   $5
BackImage[0-0][0-0][0-0][0-0][0-0][0-4]=./townhall.0.<$5>


The dash/minus is enough for makeobj to recognize that there are several values in the brackets, so variables are assigned to them. When it comes to expanding, the range from zero to zero contains only zero. Even though there is nothing to expand, makeobj still has to go through all the procedures, so this takes a bit longer.
Just don't write it with a comma instead.

#         $0   $1   $2   $3   $4   $5
BackImage[0,0][0,0][0,0][0,0][0,0][0-4]=./townhall.0.<$5>

Internally, both zeroes are treated as if they were distinct numbers. As such, it's equivalent to the following definition, which has 32 times as many lines. Even though the resulting pak file will be the same, this is just pointless.

BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][0]=./townhall.0.0
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][1]=./townhall.0.1
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][2]=./townhall.0.2
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][3]=./townhall.0.3
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4
BackImage[0][0][0][0][0][4]=./townhall.0.4



The building in question here is a townhall, a building with type "tow". In the game, townhalls represent cities, show the city name and display statistics upon clicking.
Apart from the usual parameters, townhalls use the parameter "build_time" just like curiosities. The value 0 means that a new city could be founded with that townhall. A larger value means that the townhall will be built (or rather: has a chance to be built) once a city reaches that many inhabitants. Since each city can have only one townhall, the previous one will be replaced. The new one will spawn at the same location if both townhalls have the same size.
This opens an opportunity - we can show how the same building grows over time.



It is not possible to specify which townhalls should be replaced by which directly, so this only works well if there is only one such chain per climate. A restriction by time period works too, as there will be only one time an old townhall updates to a newer one, without back and forth.
I won't show the complete sprite sheet again, it's just the different townhalls on top of each other.


name=tow_east01
obj=building
type=tow
level=10
build_time=0
needs_ground=1
climates=tundra
dims=2,2,4
BackImage[0-3][0-1][0-1][0][0][0-4]=./images/tow_east.<$1>.<$2+$0*2+$3*8>
-----
name=tow_east02
obj=building
type=tow
level=20
build_time=2000
needs_ground=1
climates=tundra
dims=2,2,4
BackImage[0-3][0-1][0-1][0][0][0-4]=./images/tow_east.<2+$1>.<$2+$0*2+$3*8>
-----
name=tow_east03
obj=building
type=tow
level=30
build_time=4000
needs_ground=1
climates=tundra
dims=2,2,4
BackImage[0-3][0-1][0-1][0][0][0-4]=./images/tow_east.<4+$1>.<$2+$0*2+$3*8>
-----
name=tow_east04
obj=building
type=tow
level=40
build_time=6000
needs_ground=1
climates=tundra
dims=2,2,4
BackImage[0-3][0-1][0-1][0][0][0-4]=./images/tow_east.<6+$1>.<$2+$0*2+$3*8>
-----
name=tow_east05
obj=building
type=tow
level=50
build_time=8000
needs_ground=1
climates=tundra
dims=2,2,4
BackImage[0-3][0-1][0-1][0][0][0-4]=./images/tow_east.<8+$1>.<$2+$0*2+$3*8>

Leartin

Monuments are a unique type of building, since it's unique: Any building of type "mon" pretty much works like a curiosity, except that it will only spawn once on the map. It also comes with a road around it.
The intended use of monuments is to celebrate those who put in work for the specific pakset or Simutrans as a whole, often the description explains who exactly it's dedicated to. Pak192.comic also uses them for outlandish art installations celebrating various other games or franchises, and in one case the uniqueness is (ab)used, as it would be weird if the "oldest oilpump" would spawn several times.


There are no special parameters for monuments, just the usual (level, introduction, climate,...) and build_time, so there is not much technical stuff to explain about them.

Perhaps, since that's important for monuments, how translations work.
Simutrans and its paksets are translated via the Simutranslator. Pakset maintainers and game developers are supposed to upload new objects into the translator such that anyone can translate them. The Simutranslator also offers language file downloads for the game itself and paksets. Those files are simple text files, in which an object name in one line is followed by that objects translation in the next line. The files name is a two-letter abbreviation of the language it translates to, with the ending .tab - you can find such files in the "text" subfolder of the game and paksets.

However, as helpful as the translator is once you are part of a team and have full access, when you are just starting out you don't get your object names in there. You can write translation files for your objects manually though. Just create a new document and save it as <xx>_<yourname>.tab, where <xx> is the language indicator (it's ISO-639-1 - but you can just open existing files in the text-folder, they contain information what language they are for) and <yourname> is your name (keep it short). In that file, you write the unique name of your object as it is in the .dat-file in the first line, then the name as it should be shown in the game in the second line. If you have several objects, repeat it, but make sure all unique object names are in the odd lines (first, third, ...127th) and their translations in the even lines (second, fourth, ...128th). If you want your translation to be displayed with several lines, use "\n" to indicate where a new line should begin.

When you offer your pak-file for download, add such translation files as well, in a subfolder "text". Simutrans will read translation files in the addon folder, so long as they are in a "text" subfolder and begin with the correct two-letter indicator. (Your name is needed to make the filename unique, else each addon would come with it's own "en.tab" and they just overwrite each other).

Don't feel like you actually have to translate to many languages, for addons, the languages are rarely the issue. But you probably care what your creation is displayed as in the game, prefering "Class 4884-1 'Big Boy'" over "4884_1_big_boy" and "ÖBB R2048 (V 100.10)" over "oebb_r2048". It's tempting to just use such names as the object names and only fixing them if they somehow don't work - but just because it works on your system doesn't mean it works universally. Hence: Stick to alphanumeric and underscores. Add one translation file for english or your native language, and maybe tell users to change the language code to their own language themself to get nicer names displayed.

Leartin

Instead of creating one big graphic and cutting it up into pieces, it can be useful to create tile-sized modules that can fit together in more than one way to build up larger buildings.



For example, this non-trademark-infringing generic-beverage bottling plant was built using several branded segments that were made to fit together. Because each segment can rotate, the building as a whole can rotate as well - and rotating single-tile buildings is typically easier than rotating a complete 6x6.



As you can see, the sprite sheet is not ordered by the tiles location in the building, since that would be impossible. Some images are reused, others are not used at all in some rotations. This means that shorthand dat cannot be used, at least not as efficiently. Instead, each tile must be hand-picked.
A word to the weird shape: It's still a rectangle as far as the parameter "dims" is concerned, there just are no images defined for those tiles that are supposed to be empty. To highlight such a tile and make sure later readers understand it's intentionally missing, it's also possible to use the value "-".


dims=6,6,4
BackImage[0][0][0][0][0][0-1]=./coka.0.<0+$0*4>
BackImage[0][1][0][0][0][0-1]=./coka.3.<3+$0*4>
BackImage[0][2][0][0][0][0-1]=./coka.4.<3+$0*4>
BackImage[0][3][0][0][0][0-1]=./coka.9.<2+$0*4>
BackImage[0][4][0][0][0][0-1]=./coka.9.<1+$0*4>
BackImage[0][0][1-3][0][0][0-1]=./coka.1.<0+$1*4>
BackImage[0][1][1-3][0][0][0-1]=./coka.8.<2+$1*4>
BackImage[0][2][1][0][0][0-1]=./coka.6.<3+$0*4>
BackImage[0][3][1][0][0][0-1]=./coka.1.<3+$0*4>
BackImage[0][4][1][0][0][0-1]=./coka.5.<3+$0*4>
BackImage[0][2][2][0][0][0-1]=./coka.8.<0+$0*4>
BackImage[0][3][2][0][0][0-1]=./coka.2.<1+$0*4>
BackImage[0][4][2][0][0][0-1]=./coka.0.<2+$0*4>
BackImage[0][2][3][0][0][0-1]=./coka.2.<2+$0*4>
BackImage[0][0][4][0][0][0-1]=./coka.4.<1+$0*4>
BackImage[0][1][4][0][0][0-1]=./coka.7.<1+$0*4>
BackImage[0][2][4][0][0][0-1]=./coka.7.<3+$0*4>
BackImage[0][3-4][4][0][0][0-1]=./coka.2.<3+$1*4>
BackImage[0][5][4][0][0][0-1]=./coka.0.<3+$0*4>
BackImage[0][1][5][0][0][0-1]=./coka.5.<1+$0*4>
BackImage[0][2-4][5][0][0][0-1]=./coka.1.<1+$1*4>
BackImage[0][5][5][0][0][0-1]=./coka.5.<2+$0*4>

BackImage[1][0][0][0][0][0-1]=-
BackImage[1][1][0][0][0][0-1]=./coka.5.<0+$0*4>
BackImage[1][2-4][0][0][0][0-1]=./coka.1.<3+$1*4>
BackImage[1][5][0][0][0][0-1]=./coka.0.<3+$0*4>
BackImage[1][0][1][0][0][0-1]=./coka.4.<0+$0*4>
BackImage[1][1][1][0][0][0-1]=./coka.6.<0+$0*4>
BackImage[1][2-4][1][0][0][0-1]=./coka.8.<2+$1*4>
BackImage[1][5][1][0][0][0-1]=./coka.3.<2+$0*4>
BackImage[1][0][2-4][0][0][0-1]=./coka.1.<0+$1*4>
BackImage[1][1][2][0][0][0-1]=./coka.8.<0+$0*4>
BackImage[1][2][2][0][0][0-1]=./coka.2.<1+$0*4>
BackImage[1][3][2][0][0][0-1]=./coka.7.<1+$0*4>
BackImage[1][4][2][0][0][0-1]=./coka.7.<2+$0*4>
BackImage[1][5][2][0][0][0-1]=./coka.5.<2+$0*4>
BackImage[1][1][3-4][0][0][0-1]=./coka.2.<2+$1*4>
BackImage[1][3][3][0][0][0-1]=./coka.2.<0+$0*4>
BackImage[1][4][3][0][0][0-1]=./coka.1.<2+$0*4>
BackImage[1][5][3][0][0][0-1]=./coka.9.<1+$0*4>
BackImage[1][3][4][0][0][0-1]=./coka.0.<1+$0*4>
BackImage[1][4][4][0][0][0-1]=./coka.4.<2+$0*4>
BackImage[1][5][4][0][0][0-1]=./coka.9.<0+$0*4>
BackImage[1][0][5][0][0][0-1]=./coka.4.<1+$0*4>
BackImage[1][1][5][0][0][0-1]=./coka.0.<2+$0*4>

BackImage[2][0][0][0][0][0-1]=./coka.5.<0+$0*4>
BackImage[2][1-3][0][0][0][0-1]=./coka.1.<3+$1*4>
BackImage[2][4][0][0][0][0-1]=./coka.5.<3+$0*4>
BackImage[2][0][1][0][0][0-1]=./coka.0.<1+$0*4>
BackImage[2][1-2][1][0][0][0-1]=./coka.2.<1+$1*4>
BackImage[2][3][1][0][0][0-1]=./coka.7.<1+$0*4>
BackImage[2][4][1][0][0][0-1]=./coka.7.<3+$0*4>
BackImage[2][5][1][0][0][0-1]=./coka.4.<3+$0*4>
BackImage[2][3][2][0][0][0-1]=./coka.2.<0+$0*4>
BackImage[2][4][2-4][0][0][0-1]=./coka.8.<2+$1*4>
BackImage[2][5][2-4][0][0][0-1]=./coka.1.<2+$1*4>
BackImage[2][1][3][0][0][0-1]=./coka.0.<0+$0*4>
BackImage[2][2][3][0][0][0-1]=./coka.2.<3+$0*4>
BackImage[2][3][3][0][0][0-1]=./coka.6.<0+$0*4>
BackImage[2][1][4][0][0][0-1]=./coka.5.<1+$0*4>
BackImage[2][2][4][0][0][0-1]=./coka.1.<1+$0*4>
BackImage[2][3][4][0][0][0-1]=./coka.6.<1+$0*4>
BackImage[2][1][5][0][0][0-1]=./coka.9.<3+$0*4>
BackImage[2][2][5][0][0][0-1]=./coka.9.<0+$0*4>
BackImage[2][3][5][0][0][0-1]=./coka.4.<1+$0*4>
BackImage[2][4][5][0][0][0-1]=./coka.3.<1+$0*4>
BackImage[2][5][5][0][0][0-1]=./coka.0.<2+$0*4>

BackImage[3][0][0][0][0][0-1]=-
BackImage[3][4][0][0][0][0-1]=./coka.0.<0+$0*4>
BackImage[3][5][0][0][0][0-1]=./coka.4.<2+$0*4>
BackImage[3][0][1][0][0][0-1]=./coka.9.<2+$0*4>
BackImage[3][1][1][0][0][0-1]=./coka.4.<0+$0*4>
BackImage[3][2][1][0][0][0-1]=./coka.0.<3+$0*4>
BackImage[3][4][1-2][0][0][0-1]=./coka.2.<0+$1*4>
BackImage[3][5][1-3][0][0][0-1]=./coka.1.<2+$1*4>
BackImage[3][0][2][0][0][0-1]=./coka.9.<3+$0*4>
BackImage[3][1][2][0][0][0-1]=./coka.1.<0+$0*4>
BackImage[3][2][2][0][0][0-1]=./coka.1.<2+$0*4>
BackImage[3][0][3][0][0][0-1]=./coka.5.<0+$0*4>
BackImage[3][1][3][0][0][0-1]=./coka.7.<0+$0*4>
BackImage[3][2][3][0][0][0-1]=./coka.7.<3+$0*4>
BackImage[3][3][3][0][0][0-1]=./coka.2.<3+$0*4>
BackImage[3][4][3][0][0][0-1]=./coka.6.<0+$0*4>
BackImage[3][0][4][0][0][0-1]=./coka.3.<0+$0*4>
BackImage[3][1-3][4][0][0][0-1]=./coka.8.<2+$1*4>
BackImage[3][4][4][0][0][0-1]=./coka.6.<2+$0*4>
BackImage[3][5][4][0][0][0-1]=./coka.5.<2+$0*4>
BackImage[3][0][5][0][0][0-1]=./coka.0.<1+$0*4>
BackImage[3][1-3][5][0][0][0-1]=./coka.1.<1+$1*4>
BackImage[3][4][5][0][0][0-1]=./coka.5.<2+$0*4>


The main advantage of a modular approach is reusability. Just by writing a different dat, a different version - perhaps a 4x4 - of the bottling plant can be created. The very same image file wouldn't work for completely unrelated factories, but with a few alterations - turning it blue and yellow and adding parking spots - it becomes a nice furniture store.

This bottling plant is our first factory, so we have a lot of new parameters to define. To begin with, factories are not buildings - internally, they are an object which holds all the factory-specific information (goods, productivity, ...) and a building object. So the parameter "obj" needs the value "factory". The parameters you can use same as for buildings are "intro_year" and "retire_year" (and _month), climates with the additional value "water" for sea-based factories like an oil platform, the graphic-related BackImage, FrontImage, dims and needs_ground, and of course name and copyright.

Some are similar. Instead of "level", the parameter "pax_level" can be used to define how many workers/customers and mail a factory has. Instead of "chance", it's "DistributionWeight, which works a bit differently. After all, for citybuildings, the location is decided first, and then which building should be placed there. For factories, first an end consumer is picked - that's a factory that does not produce anything, but consumes goods. It's the same barrel-lottery (using the value of "DistributionWeight")to see which end consumer should be created, and then the game looks for a location. Afterwards, for each good that end consumer needs, there is a tiny lottery between all factories that can produce that good, and again, a location is looked up. If no location can be found, the lottery is repeated a few times, after a few ignoring climate restrictions, before the game gives up and the chain is incomplete.
If, instead, a producer can be placed, the same process is repeated for the goods that producer needs if there are any.
Therefore, DistributionWeight is important for end consumers, as the existence of whole chains depends on it. For producing factories, it's completely irrelevant if there is no other factory to produce the same good.

New parameters are "location" and "MapColor".
Location, by default, is "land", which means a factory can be placed wherever. "river", "shore" and "forest" are kind of self-explanatory, as "river" requires a river on a tile next to the factory, "shore" requires water and "forest" requires trees around. "city" is a bit more complicated - for end consumers, the location city actually tries to place them within city boundaries. But for producing factories, "city" just means they are placed along a road, which can be part of a city but also an intercity connection.
MapColor indicates the color used for this factory on the minimap. The value is a number, which is the index in a bunch of predefined colors Simutrans uses. In this overview, the numbers are the indizes of the first color in each row.



The rate at which factories produce (and/or consume) goods is mainly given by the parameter "productivity". The value for that parameter is the number of units produced/consumed per month, if the month is 18 bits long (via game setting "bits_per_month". As a rule of thumb: If there is anything you set in the dat-file for an object and it seems to be very different in the game, there is probably some setting that applies another factor to the values.)
The parameter "range indicates a variable amount in addition to productivity. Whenever a factory spawns, a number between 0 and the value for range is added to the productivity.
Production can increase over time with the expand-parameters. "expand_probability sets how likely it is for a factory to expand, with the highest value of 10000 causing it to expand every time it is working. "expand_times" limits how often it can happen in total. "expand_minimum" and "expand_range" act like "productivity" and "range", they tell by how much the monthly productivity increases when an expansion happens.

The parameters "InputGood[0]" and "OutputGood[0]" are used to reference which goods the factory requires and produces. The values are the name-value of the corresponding good object. If a factory requires or produces several goods, each of them needs an increasing index in the square bracket.
"InputCapacity[0]" and "OutputCapacity[0]" are used to set the storage space for each good. This is especially important for inputs, since it decides when a factory is already "full" and stops ordering that good. If the storage capacity is too small compared to what vehicles can haul it might be troublesome for players, so unless you know what you are doing keep it large enough. For outputs, it's much less relevant, since produce is dumped in adjacent stations once a connection is established.
Normally, one unit of each input is consumed to create one unit of each output, and this is done as often as the factories productivity allows each month. But the parameters "InputFactor[0]" and "OutputFactor[0]" can alter that relation. If you want to consume two units of the input to produce one unit of the output, you either set InputFactor to 200 and leave OutputFactor at 100, or you leave the InputFactor at 100 and set the OutputFactor to 50. The difference is that the first method has the output equal to the production rate, while the second method has the input equal to the production rate, producing only half as much. Technically, neither InputFactor nor OutputFactor has to be 100, so you could have two different outputs whose factors sum up to 100. The main goal is to try and make it as comprehendable as possible
Finally, "InputSupplier[0]" can be used to manually choose how many suppliers the factory should have, rather than the game trying to figure out via supply and demand. Since consumers spawn producers and never the other way around, a corresponding parameter for outputs does not exist.

Those were a lot of parameters. Let's look at them (minus the graphics) all set:


name=coka_factory
obj=factory
copyright=Leartin
climates=tropic
location=land
intro_year=1892
DistributionWeight=100
MapColor=214
#-----
InputGood[0]=sugar
InputCapacity[0]=400
InputFactor[0]=80
InputGood[1]=coca_leaves
InputCapacity[1]=100
InputFactor[1]=20
OutputGood[0]=coka_drink
OutputCapacity[0]=500
OutputFactor[0]=100
#-----
productivity=800
range=200
expand_probability=100
expand_times=10
expand_minimum=80
expand_range=20

Leartin

Fields are seperate objects which can be made to spawn around a factory. As the name implies, this was intended for agricultural produce. Allowing fields as seperate objects allows for farms and plantations to span over vast tracts of land without forcing the game to find a suitable building spot for an oversized building.

We start out with a regular factory as the main building of the farm.

Since it's tropical, p192c only uses 3 seasons - dry, wet and snowy. We just make both spring and summer "wet" while fall and winter are "dry". Our factory starts out like this:

name=coca_plantation
obj=factory
copyright=Leartin
climates=tropic
location=land
intro_year=1800
MapColor=214
#-----
OutputGood[0]=coca_leaves
OutputCapacity[0]=400
OutputFactor[0]=100
#-----
productivity=80
range=10
#-----
needs_ground=1
BackImage[0][0][0][0][0][0,3]=./coca_plantation.0.0
BackImage[0][0][0][0][0][1,2]=./coca_plantation.1.0
BackImage[0][0][0][0][0][4]=./coca_plantation.2.0


Our fields look like this:

Each of these fields is their own tiny object, though they only contain bare essentials for parameters: "name", "obj" with the value "field", "copyright" and "image[0]". Note that the image parameter is not BackImage, and only has one square bracket. That is used for seasons.

name=coca_field_0
obj=field
copyright=Leartin
image[0-4]=./coca_fields.0.<$0>
-
name=coca_field_1
obj=field
copyright=Leartin
image[0-4]=./coca_fields.1.<$0>
-
name=coca_field_2
obj=field
copyright=Leartin
image[0-4]=./coca_fields.2.<$0>

You probably guessed it: Since there are no brackets for rotations, dimensions, animations or height, we can conclude that they don't exist for field objects.

The more interesting parameters regarding fields are part of the factory definition. First the general handling of fields. With the parameter "start_fields" we can decide with how many fields the factory will start out (if space is available).
"max_fields" limits how many fields there can be, you can think of it as equivalent to "expand_times". But no need to worry about swamping the whole map if you forget it: Fields can only be built in a five tile radius around the factory. So a 1x1 factory can have 120 fields at most.
"min_fields" is used to limit how destructive players can be. It's not enough for fields to be in the catchment radius of stations, the main building needs to be in there as well. If the factory spawns with a lot of fields, players may need to delete some to get their station close enough. With "min_fields" set, players won't be able to remove fields anymore if there are only the set amount left (or even less).
"probability_to_spawn" is the equivalent to "expand_probability", the higher the quicker new fields spawn when the factory is active.

Many of the parameters are specific to one of the fields. In order to connect any of the field objects with your factory, you need the parameter "fields[0]" and use the fields unique name as a value. Increase the number in the brackets if you want to define multiple fields.
"production_per_field[0] defines by how much the factories productivity increases if that field spawns (and likewise by how much productivity decreases when the field is removed). "That" field is the field object defined with the same number in brackets. There is no "range"-equivalent for fields.
"storage_capacity[0]", similarly, increases the storage capacity of a factory. That storage capacity is distributed among all consumed and produced goods of the factory, though most farms only have one output anyway.
"spawn_weight[0] is equivalent to the chance parameter, as it decides the likelyhood for each of the fields to spawn. If one of the fields stands our from the rest, it might be better to reduce it's spawning chance compared to the rest.
"has_snow[0]" finally decides whether the last season graphic defined is a snowy graphic or just a regular season. If it's not defined, it's value is 0 and the field has no snow. This is required because field seasons work quite differently from building seasons. To allow a more accurate display of the growth cycle of whatever plant is farmed, the game just takes however many season images are defined (minus the last if the value for has_snow is set to 1) and spreads them out equally among the year. Eg. if there are 12 season, there'd be a different graphic each month.

A regular farm like the coca plantation would just have definitions like these:
start_fields=20
min_fields=10
probability_to_spawn=50
fields[0-2]=coca_field_<$0>
production_per_field[0-2]=20
has_snow[0-2]=1

...Yes, shorthand can be used for things other than image definitions. It applies wherever there are square brackets in a parameter.

Now let's have a look at a more elaborate field setup:

These sugarcane fields have more growth stages. The fields need to be defined accordingly:

name=sugarcane_field_0
obj=field
copyright=Leartin
image[0-7]=./sugarcane_fields.5.0
image[8-23]=./sugarcane_fields.6.0
image[24-35]=./sugarcane_fields.0.0
image[36-47]=./sugarcane_fields.1.0
image[48-59]=./sugarcane_fields.2.0
image[60-71]=./sugarcane_fields.3.0
image[72-87]=./sugarcane_fields.4.0
image[88-95]=./sugarcane_fields.5.0
image[96]=./sugarcane_fields.7.0
---
obj=field
name=sugarcane_field_1
copyright=Leartin
image[0-6]=./sugarcane_fields.5.1
image[7-22]=./sugarcane_fields.6.1
image[23-34]=./sugarcane_fields.0.1
image[35-46]=./sugarcane_fields.1.1
image[47-58]=./sugarcane_fields.2.1
image[59-70]=./sugarcane_fields.3.1
image[71-86]=./sugarcane_fields.4.1
image[87-95]=./sugarcane_fields.5.1
image[96]=./sugarcane_fields.7.1
---
obj=field
name=sugarcane_field_2
copyright=Leartin
image[0-8]=./sugarcane_fields.5.2
image[9-24]=./sugarcane_fields.6.2
image[25-36]=./sugarcane_fields.0.2
image[37-48]=./sugarcane_fields.1.2
image[49-60]=./sugarcane_fields.2.2
image[61-72]=./sugarcane_fields.3.2
image[73-88]=./sugarcane_fields.4.2
image[89-95]=./sugarcane_fields.5.2
image[96]=./sugarcane_fields.7.2
---
obj=field
name=sugarcane_field_3
copyright=Leartin
image[0-8]=./sugarcane_fields.5.3
image[9-24]=./sugarcane_fields.6.3
image[25-36]=./sugarcane_fields.0.3
image[37-48]=./sugarcane_fields.1.3
image[49-60]=./sugarcane_fields.2.3
image[61-72]=./sugarcane_fields.3.3
image[73-88]=./sugarcane_fields.4.3
image[89-95]=./sugarcane_fields.5.3
image[96]=./sugarcane_fields.7.3

As you can see, the year is divided into 96 seasons (+snowy). How many of them show the same graphic isn't always the same - that way, you can decide how long each of the growth stages is displayed. eg. "Image[24-35]=./sugarcane_fields.0.0" in the first field object means there are ~45 days in which the first graphic (0,0) is displayed, starting ~95 days after the start of the year.
By comparing the fields, you might also notice that the interval isn't the same for each field. Slight variations make it so that for some time, only part of the fields are in the next growth stage, causing a more interesting look.



What goes on in the factory definition?

productivity=80
range=10
#-----
start_fields=20
min_fields=10
probability_to_spawn=50
fields[0-3]=sugarcane_field_<$0>
production_per_field[0-2]=20
production_per_field[3]=10
storage_capacity[3]=50
spawn_weight[0-2]=50
spawn_weight[3]=10
has_snow[0-3]=1

Three of the fields increase the productivity by 20, One only by 10, but it also increases the capacity. That fourth field is the one displaying little huts, and also has a decreased spawn_weight compared to the rest. About every 16th field is a hut, so it's likely for plantations to spawn with one or two.

Now, you might be thinking: Why would any farm or plantation have any productivity without fields? Shouldn't the base productivity be 0? Yes and no. When the game looks for a spawning spot for a factory with fields, it never checks if fields can actually spawn. A farm might spawn on a tiny island, or on a hill surrounded by a different climate that doesn't allow the fields to spawn. In such a case, if the factory does not have any productivity on it's own, it is completely useless and will stay useless. Worst case, a chain depending on it will be just as useless, and the player may be forced to edit the map, replacing it with a factory in a better location.
If there is some base productivity, the player can still connect it. Probably won't be a cash cow, but if the player causes the farm to be productive and manually scapes the land arround it such that more fields can spawn, they will over time. It's kind of a minigame and not every player would spend time doing that, but at least there is potential for a reward when they see their "pet farm" prosper, rather than the all-negative feeling of manipulating the game to correct it's mistakes.

Lastly, don't be fooled by a name. Yes, it's called a field, but that doesn't mean you can't get creative.

This is an oil refinery. It's a 5x5, but the center tiles are undefined. Instead, this factory is built up from buildings defined as fields. Every time it spawns, it looks a little different, and can rotate despite the weird shape.

Leartin

#14


This is an oil power plant. It turns oil into electricity, which can be used to boost other factories. It also produces a lot of smoke. All of which needs to be defined somehow.

First the graphics. We have a 3x3 without rotations, so it's pretty straightforward:

needs_ground=1
Dims=3,3,1
BackImage[0][0-2][0-2][0][0][0-1]=./oilpower.<$0>.<$1+$2*3>
BackImage[0][2][0][1][0][0-1]=./oilpower.<$0>.6


Now to the smoke. Most paksets already have at least one type of industrial smoke that can be reused for new factories, but if there aren't any, a smoke object is just as simple as a field object - a "name","obj=smoke" and "image[0]" for any number of frames of a smoke animation. You can think of the smoke-object as an animated gif. Old smoke objects are often only 2-5 frames long, but you can go up as much as you like (though it does cost performance to display them)

name=industry_smoke
obj=smoke
image[0-4]=./smoke.0.<$0>


Just like with fields, most parameters are part of the factory.
Each factory can only have one smoke object, so if it has two chimneys only one of them can actually produce emissions. (If you really need several chimneys to smoke, you can create a smoke animation that already contains emissions for all chimneys to sidestep that restriction, but that only works for a factory without rotations)
First, decide which smoke fits the factory by naming it as value for the parameter "smoke". Then, decide how fast the animation should play. By default, it's about 2.5 seconds long, but using the parameter "SmokeLifetime" you can set a different value in milliseconds. Don't set it to 2497 though, as that is a special value reserved for vehicle emissions (it won't break the game, but can cause graphical glitches)
Typically, smoke moves upward. The default setting for SmokeUplift is 16, which causes a smoke with a lifetime of 2500 to move up by 10 pixels. You can think of that parameter as "how many pixels would the smoke move in 4 seconds".
For example, say you want your smoke to move up by 30 pixels within a lifetime of 5000ms. 30 pixals in 5 seconds requires 6 pixels per second, hence in 4 seconds it moves 24 pixels, so 24 is the value you need. Too much math? It's usually a good idea to just check what it looks like with default values and just make it stay longer or move quicker if something looks odd.
While a smoke rises with SmokeUplift, it also has a chance to move to the left or to the right, to simulate air currents. If this isn't something you want to happen, you can set SmokeUplift to 0, prohibiting any movement, and instead have it raise as part of the animation within the graphics.

Where should your smoke spawn? Define the tile the chimney is on with "SmokeTile[0]", using "<x>,<y>" as the value (eg. "0,0" for the northwest corner). The number in brackets corresponds to the rotation of the factory, since the chimney will usually be on a different tile. The exact location is to be defined via "SmokeOffset[0]", again using the "<x>,<y>" format and stating the rotation in the brackets. However, unlike the offset you use for graphics (which is measured in pixels), this offset is based on a 64px grid. Essentially, divide your paksize by 64 and you know how many pixels each SmokeOffset-step will move you. In case of p192c, it's 3 pixels/step.
You'll find that many existing factories use the parameters SmokeTile and SmokeOffset without a bracket for rotations, as well as a parameter SmokeSpeed. Let's start out by saying that SmokeSpeed doesn't do anything, makeobj doesn't even accept that parameter. SmokeTile and SmokeOffset without brackets work for factories with only one rotation, but ignore SmokeUplift and SmokeLifetime, they also raise the smoke by 8 steps on their own. While there is no harm in keeping this legacy system in existing factories, it shouldn't be used for new ones anymore.

Our factory has no rotations, but two chimneys. As such, it's possible to define two different rotations with the same graphics, and have a different chimney smoke depending on the rotation, just to get the tiniest bit of variety:

Smoke=industry_smoke
SmokeLifetime=3000
SmokeUplift=14
SmokeTile[0]=2,1
SmokeOffset[0]=3,-77
SmokeTile[1]=2,0
SmokeOffset[1]=-13,-87



Another emission a factory may have is noise. Add noise emissions with the parameter "sound" and use the name of a sound file in the sound subfolder of the pakset. Alternatively, a sound can be registered by number in the file "sound.tab" in the paksets sound subfolder, and the factory may reference that number instead.
The sound will start playing at the same time one puff of smoke is produced, these two types of emission are in sync. Just like smoke, no sound will play if nothing is produced. However, while air pollution within the game doesn't bother a player much, noise pollution would. To prevent loud annoying factory sounds from constantly playing, you can use the parameter "sound_interval" and state - in milliseconds - a minimum pause between soundclips. Even with a rather large interval, the player would typically hear the sound once the factory is on screen, since it hasn't played while the player wasn't around.


So, the factory is a powerplant. The output good is electricity then, right? Not at all. Producers of electricity are counted as end consumers. For that to work, any factory that is supposed to produce electricity cannot produce any side products. Eg. nuclear waste production in a nuclear power plant isn't possible.
To define an end consumer as a provider of electric power, it's name has to end in "kraftwerk", the german word for powerplant. Electric producers are also special in regards to their DistributionWeight. Before the lottery, the game decides - based on a setting "electric_promille" - whether the newest factory chain should be a regular end consumer or an electricity producer. Because of this, power plants don't compete with regular end consumers at all, and their DistributionWeight only needs to be adjusted against other power plants.

And what is electricity even good for? Boosts - Short time increases of productivity in factories for as long as something is provided. That "something" can be mail, passengers or electricity.
Let's start with electricity. It has two parameters - "electricity_boost" and "electricity_amount". Boost-parameters are set in permille of a factories productivity. The default value for electricity_boost is 1000, which means if the demand is steadily satisfied, the factory can produce twice as much (or twice as fast). electricity_amount, meanwhile, specifies how much electricity is required. Using the default of 65535 falls back to making the required electricity equal to the factories total productivity (including all additions through expansions and fields)
Since our factory here produces electricity, the parameter electricity_boost does not apply (a powerplant can never be boosted by electricity) Furthermore, electricity_amount states how much electricity this powerplant produces, unless it's set to the default of 65535, which means the electricity produced is equal to four times the factories productivity.
"mail_demand" and "passenger_demand" state how many mail or pax need to be delivered to the factory in order to get the full boost. If these are set, they replace the values from "pax_level". "mail_boost" and "passenger_boost" are the corresponding boosts in permille, defaulting to 0.
Especially in the beginning of the game, large factories might have more demand for mail and pax then the small villages on the map can satisfy. Still, partial delivery of pax and mail still gives a partial boost.

Boosts can be tricky to balance, especially if you are more of a creative type, rather than a number cruncher. As a rule of thumb, just make sure all three boosts added together sum to 1000, thus satisfying everything doubles productivity of a factory, and don't use electricity_amount at all.