📜 ⬆️ ⬇️

By Dwarf Fortress Tarn Adams talks about game development.

image

One of the best ways to use powerful entertainment processors for a long time was Dwarf Fortress - a game in which the whole world consists of ASCII characters, and which would happily eat a gigabyte of memory and a large share of processor time.

But unlike some other games, in the case of DF, the player feels that she really needs everything that she needs. Her detailed calculations create a whole world with buildings, cities, merchants, rivers, volcanoes, monsters and, of course, dwarves. If one person created all this, then this would be a terrific achievement; Dwarf Fortress is a program that creates all these objects on its own.

The author of the game Tarn Adams agreed to answer our questions about his creation, which, despite the existence of many imitations, still remains a completely unique game.

Gamasutra: we heard that Dwarf Fortress comes out on Steam and itch.io in a paid version with a simplified interface. They say that this is due to your future expenses for treatment. Is it difficult for an indie developer to stay afloat?
')
Adams: Yes, a lot of effort has to be applied in order not to leave the business, and almost all developers are forced to invent creative ways in order to be able to pay rent and other primary needs. And sometimes you run into problems you can't handle. We have been lucky so far, but over time we have to change: retire and get another job, switch to Patreon, and now on Steam with itch, or draw pencil drawings, which is very far from creating video games.

What does your daily code development work for Dwarf Fortress look like? What languages ​​do you use? What libraries? What IDEs if you use them? What computer are you developing the game on?

I work on a plain, unremarkable Toshiba laptop with Windows 10. I am writing code on a terrible mix of C / C ++ in the MSVC Community. For the legacy version, I use OpenGL, for the main one - SDL, and for sound I use FMod. I do not use anything else in Windows, except for some of the MSVC headers (and before), which I have been using for decades. I am not entirely aware of what happens in the Linux / Mac versions, because I do not develop them on a regular basis.


Dwarf Fortress has been under development for almost 17 years, and its code base should be gigantic. On my machine, to create a world with standard settings it takes more than 1.2 gigabytes of RAM. The problem with such megaprojects is that they become too large and do not fit entirely in the brain. What strategies do you use to keep the project intelligible and understandable for work?

I have a strict naming system, and I do not save on the long names of variables and functions, so that everything will be readable even after several years. In general, I try to take care of the future myself. All my comments in the code are aimed at this. I actively use the “find in files” function. But there are situations when I have to re-understand what is happening, for example, when expanding the old system or removing the bug; in this case, it may take an hour or more to do the research. This allows me to leave additional helpful comments that I hadn’t thought of initially.

I remember that Threetoe (Tarna's partner in the development of the game) writes stories, and then you try to create a game engine in which they can occur. It still seems to me that this is an inspiring way of working. Were there any stories that you rejected as being too complicated to implement? Has it ever happened that the plot of one of them was completely repeated in the game?

Ha, I think in a sense, they are all too complicated. Motivation of characters, setting goals, etc. continue to lag far behind how they occur in stories. However, this is still a useful process, because there are always easier elements to generate a story; In addition, we can approach the basic mechanics of the characters, even if we never reach it.

Wikipedia says that the version number of the game (now it’s .44) shows how far you are from completion (that is, 44%). What awaits Dwarf Fortress in the future? Do you have any premonitions about what will happen? What serious aspects do you need to realize?

I am completing the release with the villains, which will be released in the next few months. He must be quite curious. Then we will implement the graphics and improve the convenience of the game for versions on Steam / itch. Then we will improve the siege process and do some more work, and then move on to Big Wait. This is the largest restructuring and expansion in the history of DF . It will allow us to generate creation myths and create fully procedural systems of magic, as well as open several view windows of different parts of the world, etc. It will be a great addition. Then there will be a release with property / laws / customs. After that, the order has not yet been determined, but we will work on the economy, ships, and other important components that are not there yet. We still have a lot to do! We have not even gone halfway to version 1.0. But version 1.0 of the game’s development will not really end ... perhaps, by the time it is released, we will not have much time left.


In games, there is a balance between plot and simulation, between a pre-written story, which most games have, and the creation of a deep world with a set of rules that allows many different stories to arise. I would say that Dwarf Fortress is one of the most serious arguments in favor of simulation. Do the characters at the stage of generating the world or during the game do something that even surprises you? Can you give any interesting / memorable examples?

Yes, it happens all the time! This is partly due to the fact that when you play, it is difficult to keep all the rules in your head. However, all my memorable stories are bugs, because I rarely have the opportunity to play the game long enough, so from the point of view of the self-generated gameplay they are of little interest (even if they surprise me). Good stories can be found on the forums, streamers, and so on.

The border between “micro” and “macro”: why did you make it exactly that, between what the dwarves can do themselves and what the player orders the fortresses?

This is a difficult balance, and it is not always easy to keep, but at the moment the concept is that the player is the “official spokesman for the will of the fortress,” and the dwarves show autonomy, which must be present outside of their official duties. This allows them to be actors in their own stories, which are the main source of the emerging (emergent) narration. At the same time, the player should be able to control the main flow of his part of the game (in fact, this is not critical, but often more interesting than watching the simulation).

These two goals may conflict, and often this is so that the player continues to enjoy the game - for example, if the emergency lever really needs to be dropped, then the task priority system can practically cause the gnome to do it autonomously or not, depending on whether whether he should “know” about it or not. In the past, we had problems with adding too much bureaucracy when, for example, we had a dwarf quartermaster carrying out equipment. But this system was too slow, prone to bugs and confusing players. It is very important to think about how each individual game mechanic can add potential stories, and the quartermaster almost did not play any role in this.

What steps does the program perform when building the world?

It allocates memory for the card. Then she chooses which pole she will have (for example, north, south) (or takes into account the parameters transmitted by the player). Seed random number generator sets the basic values ​​of the map fields (height, precipitation, temperature, drainage, volcanic activity, wildlife) for a grid of variable size, taking into account various parameters (oceans, island sizes, other variability), and then the program fractally fills them. The temperature change depends on the poles, and the program selects points for the highest peaks. Here she makes the first pass to see how the process is performed, and tries to change the heights so that the map fits into the desired parameters. At this stage, if the world cannot be rectified, it is discarded, and everything begins anew.

Then the first derived field is defined - vegetation - depending on the height, amount of precipitation, temperature, etc. The program checks whether the biomes correspond to the intervals specified in the parameters. At this stage, the heights of the middle of the level are smoothed in order to create more flat areas, and volcanoes are also placed in accordance with the field of volcanic activity.

Then we move to the erosion and rivers stage. Small oceans are draining, the program finds the edges of the mountain slopes, from which it can launch test rivers. In addition, she has a camera on one of them so that the player can follow the process. A lot of fake rivers flow down from these points, breaking through the channels if they cannot find their way to the sea. Too great heights are sometimes smoothed so that the whole map does not turn into canyons. Ideally, we should use types of mineral substances for this, but for the time being we are not using them. Lakes are grown in several points of the rivers.


The heights are again smoothed from the mountains down to the sea, and local peaks are made for peaks and volcanoes. After the creation of heights is completed, the program makes corrections to the amount of precipitation based on rain shadows and precipitation in mountain areas. Based on the height and precipitation, as well as the constraining effect of forests, temperatures are re-established, after which the program uses new values ​​to finalize the level of vegetation. For the ocean and its neighboring tiles, salinity values ​​are set.

Having defined all this, we can now find the boundaries of the final areas of the biomes to give them names and individualities. Here we also add geological and underground layers, although, as mentioned above, geological information should have been added earlier. Next is the final verification process for compliance with the parameters to make sure that we are not too deviated from what the player wants. Having finished with this, the program generates the initial populations of the animal world for each region and sets the variable weather.

The story itself begins from this moment. Then there are civilizations and caves. It is difficult to explain what happens next, but the basic idea is that a huge strategic game is simulated with zero players with fairly arbitrary move rules and poor AI (but with thousands of agents), on the basis of which history is recorded. Procedural generation of stories using the simulation logging is quite a working approach, but it also has its drawbacks. This is a large amount of work, it is necessary to perform post-processing and study in order to find good points that you want to emphasize, and if the dynamics and mechanisms are not enough, the result can be boring.

In DF, I like the most that the game does not contain any explicit system of hit points, everything related to the strength and damage of the character is part of a complex model of body parts. What are the advantages of such a system? If someone decides to create something similar, then what obstacles can it expect?

This model creates more detailed plot points, long-term consequences and provides greater communication systems. It is easy to overreact here, and we have indeed bent it in some places, at least at the current stage - some properties of materials are not used anywhere else. In addition, there are ways to mitigate the system, examples of which can be seen in games, where, for example, there is a common pool of exposure / energy / hit points, but specific wounds arise either as a result of critical hits, or as consequences of achieving zero or close to zero values ​​in the pool. The right choice of system depends on the game. We strive to use numbers as little as possible, because numbers usually do not fit well with stories.

What does the main game loop look like?

Take for example the gnome mode. It starts with checking ads and reading autosaves, etc. Most of the rest of the cycle does not occur in every measure. For example, after every hundred clock cycles, the program checks the assigned tasks and “strange moods”. Army moved around the world map. Through every hundred ticks, the program processes the tasks issued to the dwarves. This is a kind of invisible auction that is used to manage various conflicting priorities. Every ten clock cycles the seasons change, which affects the weather and the map (both locally and around the world), and also checks the development of plot elements (diplomats, sieges, etc.) and checks that the fort is still alive.

Then the program is taken for what is done in each measure. Fluids move and other map card information changes (however there are various optimizations so that each tile does not necessarily check each tile; in addition, there are flags that allow you to skip entire areas of the map if nothing happened in them.) The predators are updated and processed other "events" of the card, for example, active fires.

If the flag is set, then wounded / thirsty / hungry gnomes who cannot take care of themselves are updated. Dead gnomes “think” about their funeral rituals so that you can assign these tasks to others. Prisoners in cells and chains are periodically updating their thoughts and situations.

Then, if the creatures crossed the edges of the map, they are removed from it.


Every fifty bars is updated information of all taverns, temples, libraries, etc., depending on other bars. The supplies, also dependent on other cycles, work in a similar way. Similarly, the creation of tasks for storage. Despite the fact that this process is supplemented with various optimizations, it is still quite slow and at a certain point more than 50 thousand stones can cause problems.

Every thousand clock cycles, objects marked for deletion from the game are actually deleted, and the memory allocated for them is released. This happens more often with items once every fifty clock cycles, as is the building usage check (mainly updates for wells and some other flags that need to be checked often.)

Next, we perform another update that works in each measure. The shells released from the weapon are removed. Actions (from dancing and teaching martial arts to storytelling) are updated when necessary. Gnomes and other creatures make decisions and perform their instant actions (moving to a neighboring tile, working in a workshop, etc.) - the main part of their AI (except for the choice of tasks) is performed here.

Every hundred tacts spoil items. Each measure of growth of vegetation is performed (although there are many dependencies and flags here.) If necessary, the state of buildings is updated in each measure and trolleys are moved. Progress is being made on the transportation routes. The temperature is updated (there are many optimization flags here, but for now this is still a rather slow process.)

Finally, the camera following the creature that the player is watching is updated.

In Dwarf Fortress, a grid-based tile map is used to display the world — a simple and efficient way to present it. I noticed that there are many ways to draw tiles, and it depends on what the creature is doing or how it feels, how many elements are on the tile, is there anything above it, is the water flowing or is it buried under the stone? When DF decides how to display a tile, what does it do? How did you optimize this process?

This is just a character (byte) with a few more bytes of color, so the system is not expensive at all and we can simply replace the chosen solution before outputting to the screen, and not try to solve everything at the same time, and most of the tiles still have one earth / wall symbol. The program goes from the bottom up, in some sense using the “height” (creatures are above objects, objects are above the ground), changing the solution from time to time. Nevertheless, despite the presence of various flags and auxiliary arrays, much more can be done. There is a small array of possible units that can be displayed on each tile, so the program can implement frame-by-frame animation, allowing you to see each element on the tile, even when the game is paused.

Source: https://habr.com/ru/post/454912/


All Articles