Category Archives: Games

Simple Ultima-Style Dialogue Engine in C#

The Ultima series is one of the most influential RPG series of all time. It is known for open worlds, intricate plots, ethical choices as opposed to “just kill the bad guy”, and… dialogue. The dialogue of the Ultima series went from being simple one-liners to complex dialogue trees with scripted side-effects.

Ultima 4-6, as well as the two Worlds of Ultima games (which used the Ultima 6 engine), used a simple keyword-based dialogue engine.

In these games, conversing with NPCs (people) involved typing in a number of common keywords such as “name” or “job”, and entering new keywords based on their responses in order to develop the conversation. Only the first four characters were taken into consideration, so “batt” and “battle” would yield the same result. “Bye” or an empty input ends the conversation, and any unrecognised keyword results in a fixed default response.

In Ultima 4, conversations were restricted to “name”, “job”, “health”, as well as two other NPC-specific keywords. For each NPC, one keyword would also trigger a question, to which you had to answer “yes” or “no”, and the NPC would respond differently based on your answer. You can view transcripts for or interact with almost all Ultima 4 dialogues on my oldest website, Dino’s Ultima Page, to get an idea how this works.

Later games improved this dialogue engine by highlighting keywords, adding more NPC-specific keywords, allowing multiple keywords to point to the same response, and causing side effects such as the NPC giving you an item.

If we focus on the core aspects of the dialogue engine, it is really simple to build something similar in just about any programming language you like. In C#, we could use a dictionary to hold the input keywords and the matching responses:

            var dialogue = new Dictionary<string, string>()
            {
                ["name"] = "My name is Tom.",
                ["job"] = "I chase Jerry.",
                ["heal"] = "I am hungry!",
                ["jerr"] = "Jerry the mouse!",
                ["hung"] = "I want to eat Jerry!",
                ["bye"] = "Goodbye!",
                ["default"] = "What do you mean?"
            };

We then loop until the conversation is over:

            string input = null;
            bool done = false;

            while (!done)
            {
                // the rest of the code goes here
            }

We accept input, and then process it to make it really easy to just index the dictionary later:

                Console.Write("You say: ");
                input = Console.ReadLine().Trim().ToLowerInvariant();
                if (input.Length > 4)
                    input = input.Substring(0, 4);

Whitespace around the input is trimmed off, and the input is converted to lowercase to match how we are storing the keywords in the dictionary’s keys. If the input is longer than 4 characters, we truncate it to the first four characters.

                if (input == string.Empty)
                    input = "bye";

                if (input == "bye")
                    done = true;

An empty input or “bye” will break out of the loop, ending the conversation.

                if (dialogue.ContainsKey(input))
                    Console.WriteLine(dialogue[input]);
                else
                    Console.WriteLine(dialogue["default"]);

The above code is the heart of the dialogue engine. It simply checks whether the input matches a known keyword. If it does, it returns the corresponding response. If not, it returns the “default” response. Note that this “default” response could not otherwise be obtained by normal means (for example, typing “default” as input) since the input is always being truncated to a maximum of four characters.

As you can see, it takes very little to add a really simple dialogue engine to your game. This might not have all the features that the Ultima games had, but serves as an illustration on how to get started.

The source code for this article is in the UltimaStyleDialogue folder at the Gigi Labs BitBucket repository.

Announcing Ultima 1 Revenge

I am currently working on an engine port of Ultima 1: The First Age of Darkness, called Ultima 1 Revenge. This means I am reverse engineering the game files and building a new game engine for it, using C++ and SDL2.

Ultima 1: The First Age of Darkness was one of the first open-world Computer Role Playing Games (CRPGs). Originally released in 1981 and remade for the PC in 1986, Ultima 1 was followed by a series of games that lasted almost 30 years, generated a cult following, inspired countless other RPGs, and pushed the boundaries of technology.

Ultima 1 is a fairly weird game, featuring an unusual combination of medieval fantasy and space travel. The world of Sosaria is being ravaged by the monsters of the evil wizard Mondain. Before you can face him in battle, you have to complete dungeoneering quests in the service of the lords of the land, become a space ace, free a princess, and travel back in time using a time machine.

The 1986 PC remake, on which the Ultima 1 Revenge project is based, is very old technology, by today’s standards. Still, it provides a vast array of learning areas. The game’s graphics are made up of three tilesets (CGA, EGA, and Tandy 1000), giving a choice for the differently powered machines of the time. The game world is stored in a small map file, where each four bits is an index into the tileset you’re using. Space travel is a combination of top-down 2D and first-person views. The dungeons are simple 3D-like line drawings, randomly generated based on a seed stored in the savegame file (so they remain consistent for each playthrough, but change if you start a new game). The different parts of the game run in different executables, and a special savegame file is used to pipe the player state from one to the other. Savegames mostly use 16-bit numbers, with the least significant byte stored first. Decoding the game files is an ongoing effort that powers tools such as the online map viewer I built in 2015, and the engine itself.

Today, I have released a demo of the engine. So if you own a copy of Ultima 1 (if you don’t, you can grab a copy from GOG), grab it from the downloads page, set the path to your original Ultima 1 folder in the settings file, and take a tour of Sosaria!

Ultima 1 Reverse Engineering: Decoding Savegame Files

This article was originally posted at Programmer’s Ranch on 29th August 2013. The version migrated here is mostly the same except for minor updates and a brand new screenshot of the latest GOG installer for Ultima 1.

It’s no secret that I’m a long-time fan of the Ultima series of games. My most significant contribution to the Ultima fan community was running Dino’s Ultima Page, a news and information hub for the community, for over a decade.

Ultima insipired such a great following that hundreds of fan-made remakes, tools and other projects appeared over the years. Many remakes were intended to be mods of games such as Dungeon Siege or Neverwinter Nights. Among other things, talented developers found ways to understand the data files of the games (including the graphics, savegames, maps, etc). This resulted in several useful tools (savegame editors, map viewers, etc), but more importantly, it allowed people to create new game engines using the original data files, with the intention of allowing the games to be played on modern operating systems. The first of these was Exult (an Ultima 7 engine), but over the years, projects appeared for most Ultimas – such as Pentagram for Ultima 8, or Nuvie for Ultima 6.

This article describes the process of reverse engineering, i.e. how to try to make sense of data files given the data files and nothing else. We will be looking at the savegame format of Ultima 1. Although Ultima 1 is not free, you can get the entire first Ultima trilogy from Good Old Games for a few bucks. Ultima 1 is a great choice to start reverse engineering because it’s relatively simple – the savegame file is only 820 bytes long and is uncompressed. This made sense for me as when I started this project I was still a budding programmer, and it also made sense because there was very little knowledge about the U1 formats around, whereas the later games had been studied in depth. Reverse engineering is a bit of an advanced topic so feel free to skip it if you feel lost, but it’s also a very interesting topic.

So first thing you need to do is install Ultima 1. If you got it off Good Old Games, it conveniently comes with DOSBox, allowing you to play it under modern operating systems:

u1revenge-install-u1-gog

After launching the game, you will find yourself in the main menu.

u1revenge-mainmenu

Press ‘a’ to go through the character creation process.

u1revenge-chargen-6

Once you have selected your attributes and saved your character, you find yourself back in the main menu. In the Ultima 1 folder, you should notice a new file called PLAYER1.U1:

u1revenge-mainmenu-savefile

That’s the savegame file we’ll be messing around with. You can use a hex editor to take a glimpse of its contents. Personally I like XVI32 because it’s pretty lightweight and even allows you to edit hex entries.

u1revenge-xvi32

This might look like gibberish, but you can already notice a few things. The first 14 bytes in the savegame file are reserved for the player’s name. Knowing the stats you chose during character creation (strength 32, agility 32, stamina 15, charisma 12, wisdom 11 and intelligence 13), you can also spot them in hex on the second line (20, 20, 0F, 0C, 0B, 0D). Windows Calculator has a Programmer mode that is quite useful for switching between decimal and hex:

u1revenge-wincalc

A good way of decoding more parts of the savegame file is interacting with the game itself. Start the game. The world view looks like this:

u1revenge-startworld

Keep a copy of your savegame file at this point. If you move around a bit and save the game, you should at the very least observe changes in your character’s X- and Y-coordinates:

u1revenge-u1moved

You can manually check which bytes in the savegame file changed. I found it more useful to write a hex diff tool that actually highlights the bytes that changed:

u1revenge-hexcompare

As you can see, it’s not so simple: there are many things that might change, including food. However, you can choose your moves carefully (e.g. 2 steps west, 3 steps north) so that you can then spot which bytes have changed that much and determine which are the world coordinates.

Another way of learning more about the savegame file is by actually tampering with it. Using XVI32, I changed the byte after ’96’ from 00 to 13:

u1revenge-tampering

After running the game, note how the Hits shot up from 150 to 5014:

u1revenge-tampering-result

That makes a bit of sense: the 96 (hex) we saw earlier corresponds to 150 in decimal – the original value of Hits. But why did it become 5014 when we tweaked the byte after it?

It’s because DOS games like this stored values as 16-bit integers in little endian format (i.e. the bigger byte is the second one). So if we have the value we tweaked, i.e. 96 13, that’s actually (13 * 100) + 96 (all hex), which results in 5014 (decimal).

Isn’t that neat? Reverse engineering requires a lot of time and patience, but it’s a bit like fitting together the pieces of a jigsaw puzzle. After a while you might end up understanding a good chunk of the data files:

u1revenge-savegame-file-format

Once you understand the data files (which also includes map and graphics files), you can then proceed to write all sorts of tools and stuff. I had called this project U1Revenge (Ultima 1 Reverse Engineering Effort) and wrote a map viewer and was working on an engine for it. Although I stopped working on it, I did release a couple of demos, the latest of which you can grab from the project page.

u1revenge-engine

Reverse engineering is certainly not a new art. The book Masters of Doom describes how fans of DOOM would hack the game’s map files to create their own level editors. Many games have similarly been studied, and a wealth of knowledge is available today. Reverse engineering is not just an achievement; it is a glimpse of history, and helps to understand how games were created even before we were born. The following links provide further reading:

Rediscovering Sensible World of Soccer

Back in the days when I actually had time to play computer games, I was a fan of three main genres: RPG, strategy, and FPS. The sports genre never really captured my interest, with maybe a couple of notable exceptions. One of these was Sensible World of Soccer.

swos-goal

This week I treated myself to playing Sensible World of Soccer ’96/97, which I had bought off GOG.com. Although this is not the classic I grew up playing (which was Sensible World of Soccer ’95/’96 European Championship Edition), it’s practically identical to it: the only noticeable difference is that some players have moved between teams.

Sensible World of Soccer (SWOS) is a game where you can both manage a football team and play football. It offers a great deal of flexibility. If you just want to play football, you can choose all sorts of friendly matches or tournaments to play. On the other hand, if you just want to manage your team, you can do that, and see only the game results. You can both manage the team and control it while playing football games. And you can even do neither: watch a game played by computer-controlled teams.

 

swos-menu

The management aspect is menu-driven. When you start a career (which lasts a maximum of four seasons), you are given control of every aspect of your team, including organising and training your team, buying and selling players, keeping track of the match schedule, watching the club’s profit/loss, goal statistics, and even watching the progress of various tournaments in the world.

swos-team

Managing your team alone is complex enough to give the game a great deal of replayability. Each player has a position in which he’s comfortable (e.g. defence, attack, etc). Each player also has a financial value and a set of top skills (e.g. speed, shot power, ball control, etc) denoted by the yellow letters in the screenshot above. It’s not all as easy as it looks, however. A player may thrive or stagnate depending on his position, and his value may change accordingly. Financial value also does not always accurately reflect a player’s skill. So trying out different players in different position is key to forming a functional team.

swos-strategy

Other than that, before and during each match, you can refine your team’s formation, allowing you to try different strategies to adapt to demanding situations.

swos-goal3

The gameplay itself is incredible fun. You control one player at a time while all the rest move automatically. This is where the player skills pose a challenge: depending on whether your player is good at speed, ball control, passing, or whatever, you may opt to pass the ball, dribble past your opponents, or shoot the ball into the goal. Controlling the ball when shooting is a skill in itself; you usually use the arrows to indicate a direction (e.g. top-right), but you can actually use different key combinations after shooting to give the ball a curved effect or elevation, allowing you to score some pretty spectacular goals.

swos-goal2

Once you go beyond the basic skills, you can get creative and have loads of fun, scoring in incredible ways and posing different challenges. For example, instead of just shooting into the goal, you can cross to another player and score with a tackle or header. You can score from a distance, or pass your way around the goalkeeper, and just run in with the ball. Or my personal favourite: get the goalkeeper and defenders to follow you, run the train around for a bit, and then just deposit the ball at the back of the net.

swos-beating

You might want to start with a pretty good team, such as Manchester United or Bayern Munich. But as you get more familiar with the game, it’s a fun challenge to start with a crappy team and buy better players. That allows you to pretty much dominate a league, and give your opponents a beating. For example, the above 12-0 in a typical 3-minute (real time) career game is a pretty nice feat, and a challenge to achieve.

SWOS is a simple game but gives you a world of opportunities to try out. Career games last 3 minutes of real time, but friendlies may be 3, 5, 7 or 10 minutes (you choose the setting). This means you can play the game for just a few minutes, or spend several hours at a time.

If you’re not put off by the dated graphics, and love some genuinely fantastic gameplay, give this game a go.

Running Games Requiring a CD in DOSBox

Back in 2009, I had written DOSBox for Dummies, a short and simple article explaining how to get old games running in DOSBox, and how to write a batch file so you don’t have to do this every time. A typical batch file might look something like this:

dosbox -c "mount c C:\prophet" -c "C:" -c "prophet"

That will often be good enough. But if your game needs to access resources on CD, such as Ravenloft: Stone Prophet in this case, then that’s not quite going to work:

dosbox-no-cd

To allow the game to access the CD, you’ll need an extra command to mount the CD drive:

-c "mount d D:\ -t cdrom"

The whole thing looks something like this:

dosbox -c "mount c C:\prophet" -c "mount d D:\ -t cdrom" -c "C:" -c "prophet"

That’s much better:

dosbox-ravenloft-stone-prophet

If you don’t want to pop in your CD every time you want to play, just copy the CD onto your hard disk, and mount that folder instead:

dosbox -c "mount c C:\prophet" -c "mount d C:\STONE_V1.0 -t cdrom" -c "C:" -c "prophet"

That works just as well.