DEV Community

dalerank
dalerank

Posted on

How to build a mastaba

The game "Pharaoh," released back in 1999, was one of the first games to offer step-by-step building of structures, which also required various resources. Off the top of my head, I can recall the Settlers series, Majesty, and perhaps a couple more. After "Caesar III," where the primary resource for building was coins, this was truly astonishing and innovative. It was a particular pleasure to watch the city come to life during the construction of monuments. I remember just building the minimum necessary infrastructure for a monument and simply observing as architects complained about the lack of materials, slaves ran back and forth between farms and construction sites, and traders periodically sold bricks. The rest of the city, of course, lived its own life. You could even forget about certain parts of the city for a while, and the game would continue. This is when you realize why the game remains one of the best city-building games: the distinguishing feature of the series is its "balance," a balance perfected down to the smallest detail.

Image description

Revisiting this part of the game, I can't stop marveling at how it was all implemented on those hardware resources, which were, I must note, very limited. Not everyone had 64MB of RAM back then. One of the innovations of the monuments was that they were composite buildings; individual parts could be replaced with others, which essentially allowed for creating buildings with different appearances from the same set of textures. Nowadays, it seems like this approach is present in every game, but in '99, only a few games could boast such a mechanic.

At first, I tried to reconstruct the original drawing algorithm but quickly realized that not only could I not handle so many 'if' statements, but the compiler couldn't either. So, I had to improvise.
In the original game, players gain access to the first monument in the fifth mission of the story campaign, when the map of Egypt and traders become available. The construction of a mastaba is closely tied to teaching the basic rules of trading. Compared to the previous installment of the series, city modeling didn't change significantly. The primary condition for the development of houses remains the same: it depends on the attractiveness of the surrounding land and the availability of goods from the nearest market. If you have residents, you can develop production, build new resource-gathering buildings, and so on.

Image description

The number of people in houses depends on the current level of the house, and each level requires a new type of "resource" for maintenance. Moreover, these resources don't necessarily have to be products or goods; accessibility to temples and services like pharmacies is also required to maintain the house's level. While a single market can suffice for the initial levels of houses covering several dozen homes, it becomes noticeable after the mid-game that the number of buildings a market can support is carefully balanced. Even though reviews never explicitly mentioned it, players figured out that one market can support a maximum of 4 mansions at the highest level. Interestingly, even if their working areas overlap, having two markets doesn't support more than that number.
The hierarchy of needs itself remained unchanged from the previous game in the series. It fit so well with the entire setting of the series that it remained unchanged up to "Emperor: Rise of the Kingdom."

How the built in 1999

The first monument available to the player is the mastaba. In the original, it looks like this at the beginning of construction (screenshot from the original game)

Image description

And like this upon completion (screenshot from the original game)

Image description

I had to tinker with the algorithm for building the mastaba. At first, I tried to restore the original one, but in the end, I gave up and made it simpler. The original algorithm draws the entire mastaba in one pass on the screen, caching identical images and redrawing residents and buildings that are overlapped by the monument during rendering. It turns out to be unnecessarily complex (don't ask how I managed to restore it from the binary, I definitely gained a couple of gray hairs), below is the code for rendering the mastaba itself.
But then I looked at the resulting code and realized that a month would pass, and all of this would be forgotten because it's too complex. So I started digging through the internet in search of something simpler to understand. The solution came together after reading this article, which describes quite well the principles of rendering composite buildings in isometric view. This eventually led to breaking down the mastaba into its component parts and drawing them based on the general rules of isometric perspective.

Image description

On one hand, this greatly simplified the rendering logic and completely eliminated the need for overdraw of overlapping parts because each part is considered an independent building and is drawn based on general rules. On the other hand, now the mastaba consists of three types of buildings: slanted wall, entrance, and solid wall. The peculiarity of this implementation is the necessity to recalculate the rotation for each type depending on the map's rotation and the rotation of the mastaba itself.

Gathering bricks

The textures for the mastaba parts are divided into segments of the same size, from which the entire building is assembled. The size of the mastaba can vary from 2x3 to any reasonable size for a specific map, with the one in the pictures above being 2x5. In the resources, this data is split into two files, mastaba.sg3 — this is the description (sg3 stands for Sierra Graphics V3). The texture compression format, developed by Sierra Entertainment employees in 1988 and used in most studio games, but not well-known outside the studio. It provides good results when packing textures with RGB data in 16-bit, all pixels with a value of 0xf81f are interpreted as transparent, this is done with the expectation of subsequent RLE packing, which can compress such sequences of identical pixels into a couple of bytes. You can read more about the format here.

The texture data is stored in .555 files. Most images are located in the .555 file with the same name as the .sg3 file. This was done with the idea of possible patches, to have the ability to provide users with partial changes. The internet was not particularly fast back then, so saving even 0.5Mb, which the sg3 file occupies, was a significant argument for such an architecture.

Image description

The graphic data can be packed in various ways in the .555 files, depending on the type of texture:

  • Uncompressed — such images are stored "as is" by rows, from top to bottom, left to right in each row. Thus, if the image has dimensions of 20x30 pixels, it means that the data for this image consists of 20 * 30 = 600 unsigned 16-bit integers representing the colors of each pixel.
  • Compressed — transparent pixels for these images are encoded using run-length encoding. This format has remained since Caesar II and was used less and less over time. The data is processed byte by byte as follows:
  • Isometric — textures with a width divisible by 30 pixels and consist of two parts:
    • "Base" part: rhomboid base of the tile, stored in uncompressed form because there should be no transparent pixels. The dimensions of the base texture are determined by the game to which the SG file belongs: Caesar 3, Pharaoh, Zeus use tiles of size 58x30
    • "Upper" part: the remaining pixels that are not part of the base, and since they can contain transparent areas, they are stored in compressed form.

But that's not all, if you look at an isometric tile, you will notice that half of the space is not used, these are transparent pixels and they can be excluded from the encoding by simply skipping these areas. Thus, only significant pixels are recorded, and the texture size becomes even smaller. For example, with such a packing algorithm, a texture of size 10x6 out of 60 pixels is packed into 36.

Image description

Based on this data, which is read, unpacked, and correctly assembled, full-fledged textures are obtained. The storage format was primarily designed for the Windows operating system family and its graphics stack, allowing for quick blitting (overlaying) of textures without transparent pixels.

Image description

Preparing the construction site.

The construction process is divided into 8 parts: two site levelings and 6 stages of laying stones. To build a mastaba, stone is needed, and for laying one segment, 400 stones are required, which must be delivered from the warehouse to the construction site. After that, the bricklayers begin laying the stones (screenshot from the open-source version).

Image description

The calculation of the required texture turned out to be quite simple; edge textures are used along the edges of the site, while the internal tiles are filled with seamless stone tiles. For animating the monument construction process in the game, separate types of buildings and inhabitants with corresponding sets of animations were added, which was quite bold for games of that time and resource-intensive. The number of animations doubled compared to the previous game in the series, and the texture volume increased accordingly

Image description

Fixing rendering bugs

Due to the new approach to rendering the mastaba, incorrect rendering of individual parts occurred. I had to add a bit more to the renderer to teach the engine to render the top parts of textures. For this, the original texture is divided into two parts: the base and the upper part. Then, when overlaid on top of each other, they will form a cohesive texture, free from rendering order errors in isometric view. All bases can be rendered in the first pass, eliminating overlap with the figures of inhabitants, and then overlay the upper part to hide the inhabitants who ended up behind the building. As a result, we get a normal view of the buildings. (screenshot from the open-source version)

Image description

A little bit of magic with C++, wait for a few in-game years, and the construction is completed. Almost.... I still need to tweak the texture indices a bit when changing the city's orientation.

Image description

A little bit of magic with C++, wait for a few in-game years, and the construction is completed. Almost.... I still need to tweak the texture indices a bit when changing the city's orientation.

Image description

Code and logic reuse.

Also in Pharaoh, specifically for monuments, a dynamic resource model appeared (as far as I understood from the available source code) — the monument was essentially a warehouse, with set rules for goods. If in Caesar the space for resources was reserved at the warehouse immediately, here the bricks appeared on the construction site only when they were physically delivered on a stretcher. On one hand, this allowed players to abuse the game with save-loads; for example, if you catch the moment when the porter unloads the goods, reloading the save could allow you to get that item again. On the other hand, it added new connections to the simulation and made it more complex.

Image description

Where to see and try

The project lives here (https://github.com/dalerank/Akhenaten). The original render has been restored and partially rewritten; I am restoring the game mechanics, buildings, and inhabitants as I get to them. Missions are restored up to 5; to play, you will need resources from the Steam/GOG/Roger versions. Or join the Discord (https://discord.gg/HS4njmBvpb) channel where I post updates on the development progress.

Top comments (0)