Hey, what are the algorithms behind the vegetation generation? It looks like you are generating bushes and trees in a natural manner, I'm trying to make procedural maps for my game and I'm not sure where to start.
Thank you for your interest! The best way to start is KISS: Keep it simple, stupid :>
The apparently natural manner I'm using in 1812 is very simple. You can find the details in builder.cpp. Look for the buildWilderness() function.
The 1812 map has the overmap, and each tile of the overmap corresponds to a large submap. buildWildreness() builds one of these submaps. Each square in the submap is populated with merely a
set(x, y, weight.pickRandom());
where the pickRandom picks tree, bush, grass, dirt at random according to a weighted distribution. Now, if each submap had its own distribution, there'd be a harsh line between submaps when you are at the local level. To avoid that, the weights of each submap (that comes from the chosen overland symbol for that submap) is averaged to the corners of the submaps. Then each submap linearly interpolates the corner values within itself - that is that weight averaging you see in buildWilderness.
So the heart is very simple - spatially-independent weighted selection. The two next bits are important: how to generate the overmap; and how to set the weights.
The weights are setup in the source.txt, which is a grab bag for all my declarative constants. Deep in this you'll find DEFINE TERRAIN, followed by the weights for each type of terrain.
TERRAIN PLAINS
{
tile GRASS
grass 90
bush 10
sightrange 100
armyrange 500
}
TERRAIN BUSHES
{
tile BUSH
grass 10
bush 90
sightrange 40
armyrange 300
}
TERRAIN FOREST
{
tile FOREST
tree 90
bush 10
sightrange 15
armyrange 100
}
TERRAIN WASTE
{
tile DIRT
dirt 90
grass 10
sightrange 130
armyrange 600
}
My setup here was simple - I had a sort of hierarchy of terrain in mind, of what valid things are. The usual ecological cycle in my part of the world is pond -> meadow -> bush -> forest -> pond, so thus that natural grass/bush/forest transition order.
So then comes the question of the overmap. This you can find in the buildOvermap function in builder.cpp. This works at the very coarse level, as we are just picking the overmap terrain type. The system is a simple randomized markov chain on each tile. However, the probability tables are controlled by the surrounding tiles.
There are two transition tables used, one in the grow bushes, the second in grow forests. You'll note a comment about GS - that is Gauss-Seidel. Since we are processing the tiles in a fixed order, you'll get strong biases as the next tile will see the updated values for its upper left neighbours, and the old values for lower right. Ideally you randomize sweep orders to avoid this, but I was lazy and instead relied on the fact the noise in the transition rules to hide this. (Noise hides many sins!)
The exact nature of these rules was heuristic, and derived by just generating and tweaking until I liked the results. The second pass grow forest, IIRC, was added to try and consolidate the forest networks as otherwise it looked too patchy.
....
But back to your question: Where to start?
Make it easy to experiment. Setup a test harness so you can see your full level, hit a key, see another one, and repeat. Seeing a few hundred is a great way to get a sense of what your algorithm is doing and how to tweak it. Try to keep your level generation stuff high level so you aren't messing around with pointers and edge-cases when writing stuff. In particular, stuff like indexing your map out of bounds should silently *work* usually, because then a lot of this code becomes easier to read. It is also useful to separate the "Dressing" from the "Bones". Ie, often you can just do a 25% replacement of tile X with tile Y at random to break things up. But this can be a post-process, and the earlier stages can just work with tile X. That builder.cpp has a lot of other random algorithms in it as I've built them across many 7DRL, so I'd encourage reading them and seeing how they work.