Automap rules : input multiple layers

Is there a way to have the input of several layers count towards the same condition in a rule?
Suppose for instance that I have a terrain type layer, and a height layer.
I would like to have (for the same tile)
input1_terraintype : desert + input1_height : hill => output terrain : desert_hill
input1_terraintype : grassland + input1_height : hill => output terrain : green_hill
Currently, it seems that if layer1 is different from layer2, the inputs are considered to be different conditions if the index is not the same (so it works the same as having a given layer with 2 different indexes), but even if given the same index, it seems that the condition is always considered false (at least, I could not make my simple exemple work)…
Did I miss something? Is there a way to do so currently?

I hope @Stefan_Beller is available, since he’s really the best person to answer this question.

So the way I understand the question is to have 2 layers which define 2 different orthogonal properties such as height and terrain type and the output is dependant on these 2 input properties.

If I understand this correctly, I added an example to https://github.com/stefanbeller/tiled_examples (in the folder orthogonal_height_type/) which has these different 2 input layers and one output layer covering that.
To do that I had the shortest possible index (empty, as in “”).

Thank you! It works very well indeed. I don’t know why I thought it wouldn’t.

Ah, thanks. I could have sworn this wasn’t supported either but I’m happy to confirm it’s in there and works.

Automapping really is one of Tiled’s most unsung features. I use it a lot. Thanks again.

It has been 1 year already, but I think I have rediscovered why I had an issue with input from multiple layers:
There will be no output if any cell of any of the input are void:
Simple exemple:
rulefile:

region:          xx
input_layer1:    1.
input_layer2:    .a
output_layerc:   +-

If the entry is as follow
layer1: 1? where ? is any tile
layer2: ?a
The output on the layer c will be ±
but if the entry is
layer1: 1. where . is no tile at all
layer2: .a
Then there will be no match, so no output on layer c.
There is an easy workaround to fill the layers with a blank tile, but it is not very elegant.

This seems related to the discussion I had with @stefanbeller on this pull request:

https://github.com/bjorn/tiled/pull/1276

I hope he can explain the reason for this behavior, or if we could just change it.

Indeed, it seems to come from the empty tiles being on the exclude list.
However, there are some times when you want it to be the case, and some other where you don’t, but I think most of the time, I prefer not to exclude empty tiles.

Can you elaborate a bit on cases where you would want this? I’m still trying to understand this part.

In most cases, I would want not to check empty tiles (ie to exclude tiles because they are empty on the input layer, while the rule input layer is also empty).

But you are correct, I don’t really need the match to fail if there is nothing in the input layer.
It is not the desired behaviour indeed, but it may be used as a workaround to have a fail or pass if there is anything (or nothing) in a given position, which can be useful.

The cases where I want it to fail is if there is anything in the input layer specified by inputnot_layer, but I am not sure it is related:

For instance, in my game, I have 3 alternating pieces for the wall, with the corner always being connected to the second piece of these 3.
The input is the terrain_type layer, which then outputs the wall_type to the wall_type layer.
So I do several pass. The first one outputs a Corner on the wall_type layer where the terrain_type indicates the first wall piece in a line and a wall_unspecified tile on the other wall pieces positions.
Then, on the next ones, the wall_unspecified tiles are replaced with the relevant wall pieces (wall1, wall2, wall3):

I want to set up the rule for the intermediary wall tiles (wall terrain to wall_unspecified tile) to fail if there is anything in the wall layer (either wall_unspecified, wall_1, wall_corner, wall_2, wall3…), so I want the match for inputnot_wall_type to pass if there is nothing in the wall_type layer at the same position as a wall in the terrain_type layer, and to fail if there is anything, so that the first rule does not overwrite the wall types(w1, w2, w3) with wall unspecified on subsequent automapping passes.

For these cases, (and letting people decide to fail/match if there is something or nothing), I think the most flexible way to do that would be to have an inputanything_layerId and inputnothing_layerId that would return a pass or a fail if there is anything (or respectively nothing) on the layerid input layer (so the tiles on these layer would work as the region tiles, and only the presence or absence of tile would be considered).
An outputdelete_layerId that would work the same, and delete any tile on the relevant layer if there is a match would also help make the system more flexible.

But it might be too much hassle for what it is worth (I would much prefer having the system changed to what you suggested than the current one, and not fail the matching test whenever there is any empty tile in any layer of the checked region).

I added an issue to the list here.
Are these issues still present after the changes you have made to automapper.cpp?
If not, I can try to modify automapper.cpp if you think the behaviour should be changed(basically, make rule checking less likely to fail if not all inputlayers are used for all regions).
I am more of a C programmer, and I don’t know qt, but I think I should be able to modify the relevant checks in automaper.cpp without breaking everything.

I didn’t really change the behavior in any way, apart from that it now no longer forgets to remove empty object layers, so I’m sure this issue is still relevant.

I did just merge https://github.com/bjorn/tiled/pull/1276, but that change doesn’t affect the behavior of excluding empty tiles.

You’re welcome to try your hand at it!

Is it OK if I “greenlight” tiles that don’t have any condition then?

I think having blank tiles is a useful features, because it allows the user to have a rule over non adjacent input tiles (especially for me as I use hexagons that are visually adjacent, but not adjacent for tiled). Of course, a workaround would be to add an inputnot_layer with dummy tiles for the same result, but would there be any problem in returning true if no condition is given for a tile?

This is about when both the listYes and listNo are empty. Are you sure that can even happen? As far as I understand, this code will always cause InputConditions to have a layer in either listNo or listYes:

mInputRules.names.insert(name);

if (!mInputRules.contains(index))
    mInputRules.insert(index, InputIndex());

if (!mInputRules[index].contains(name))
    mInputRules[index].insert(name, InputConditions());

if (isNotList)
    mInputRules[index][name].listNo.append(tileLayer);
else
    mInputRules[index][name].listYes.append(tileLayer);

If both are empty, the rule for that layer shouldn’t even exist.

You are right, I was misled by this:

            for (const TileLayer *comparedTileLayer : listYes) {
                if (!comparedTileLayer->contains(x, y))
                    return false;

My bad, I thought contains would fail if the layer contained a zero tile at (x,y), but it only checks map bounds.

I made some changes so that:

  1. Having a missing layer won’t be a problem for regions in which all cells in the corresponding input_layer are empty

  2. If a cell in a region is empty for a given layer, and the input_layer is also empty, it will not count as a mismatch.

For item 1, I created a temporary empty layer as input to setLayer if it was missing, and destroyed it after compareLayerTo. It is not very elegant, but this way, I skip having to copy paste compareLayerTo, or check for empty setLayer.

For item 2, I just changed the folling block :

            if (listNo.isEmpty()) {
                if (matchListYes)
                    continue;
                if (!ruleDefinedListYes)  && !cells.contains(c1))
                    continue;
                return false;
            }

by removing the second condition

if (!ruleDefinedListYes)

I will test today, and commit tonight or tomorrow if I don’t get any unexpected result.

I didn’t encounter any problem while testing, so I issued a pull request.
I think it should also solve this issue.
I hope it doesn’t break anything for anyone.
Is there a tiled automap documentation I should also update in the future when it gets merged?

Yes, when this gets merged the following page will probably need an update somewhere: Automapping · mapeditor/tiled Wiki · GitHub

(I also still need to move that into the Tiled documentation website at some point)