Friday 24 January 2014

Data-driven class design

As if my normal posts weren't boring enough, this one is about programming. I'm a big fan of OOP (object-oriented programming) and have tried to apply it in my projects. But working with a legacy project like this I'm constantly fixing style mismatches with traditional C's linear programming style.

One of the basic ideas and building blocks in Kaduria is a data-type class that has a simple value stored (usually with accompanying enum list) and a verbose public interface. As an example there is a problem I'm working on right now. It's the mask value for mask map. In plain old C you would use enums like this:

if (mask_map->Get(x, y)==maskWall) Do_Something();

(Actually I'm using coordinate parameter for location, and x, y is used here for clarity.)

That maskWall is a "linear" or plain data. Using a datatype class things change a bit:

K_Mask_Type m=mask_map->Get(x, y);
if (m.Is_Wall()) Do_Something();

It's doing exactly the same thing, just with minor difference of using class and a getter routine. The m value is simply the mask value in the map, stored into K_Mask_Type as the only variable which then can point to the data associated to mask value. Is_Wall() can look something like this:

bool K_Mask_Type::Is_Wall()
{
  return mask_data[type].is_wall;
}

But it can also be a switch statement or any other way to handle data.

The problem with first way, old school C, is that you might want to add another mask type which is also a wall. When you do it you need to write changes in all places using that statement, for example like this:

int m=mask_map->Get(x, y);
if (m==maskWall || m==maskCorridor_Wall) Do_Something();

As you can guess, the great thing in class style is that you can add the new mask type, but Is_Wall() can include it and you don't have to search for those if statements in the source code.

It's not all good, because you can have legacy code that has both styles mixed and it can require more than just simple if statements to be changed. Also, the way the class itself is written can become complex as you add verbose functions for each of features. In this case the getter/setter syndrome is not actually that big problem, because that's the way it is supposed to work anyway. You can go wrong with class functions, too. But often they hold a logic which works through the project and when you add something it's less likely that something breaks elsewhere.

The verbosity of class functions is not something I would have thought means anything when I was still learning OOP, but it's of course a powerful way to write source code along with "class as type" classes. Often when people think of classes they are already thinking inheritance and other more advanced things that in my opinion are less important! In fact when you get to the next level you try to avoid inheritance at all costs, because it's a complex topic in which many things can go wrong, starting from the way you plan the class hierarchy. For some people it goes as far as inventing their own meta-language using templates.

I think many C++ books are to be blamed on how we see classes and immediately associate inheritance and "easy" re-use to them. Far more important is the data-driven idea: hide the raw data in the class and use (verbose) public interface to avoid refactoring through the source code when something changes. And as we know in case of roguelikes there always will be changes.

Wednesday 22 January 2014

The hatch

On halfway of nine selected todo items I ran into a problem. There are some door type (lid) objects on top of stairs or sewers. Those objects are created on the same map as other objects that don't move, including stairs. When you create a hatch lid on stairs and open it the stairs are not displayed. More than that it also affects to object interactions (collision detection etc.).

The layer system of maps is already complex, but I could solve this by creating another layer for door type objects. Or I could create a stacked object map with std::list for each tile. Whatever the solution were it would also include collision detection logic etc. In other words a lot of work.

I was thinking of this for a while and decided to skip it. Just skip it for now. Lids are needed for sewers and in rare cases for stairs. It's not an important feature right now and because it's pretty big change to the engine I want to make sure that other more basic features are done first.

Wednesday 15 January 2014

Update areas rewrite

There are rectangle areas dividing screen in parts and update each of them when their flag is set. At least with software mode of SDL this is a good idea, because it certainly does speed up the game compared to full screen update. Besides not only updates, the background graphics are also re-drawn when something happens in that area.

I've been refactoring those areas, because I needed to change message output to 5 lines and scrolling mode. The ancient nature of GUI was revealed when I noticed that almost all actual output to those areas were hard wired with another set of coordinates. So.. this is how it looks now:


Those purple dotted lines show the areas as debug method to actually see where they are. I already fixed the small map, but as you can see the gameview is in wrong place and stats also (and some areas itself), displaying only the last line: Water. This is a minor fix however. From now on the content coordinates are retrieved from the area data only and in future I can easily change the locations of areas if needed. One possible option I can think of already is switching the places of location (bottom line) and messages.

Saturday 11 January 2014

When it's done

The first item of 10 hour todo list was easy. Then I realized before doing anything else I have to fix the message routine, just like I did for Teemu. This time of course I can use new parts of Teemu's routine, but even then there are work to do with Kaduria's message system. Like with level generation I once thought it would be nice to create a complete engine for messages, but it became too complex and unwieldy for what it was supposed to do. So, rather than trying to make the system work in one way I'm using several different ways that all ultimately use the core routine.

People often ask when Kaduria is ready. It's getting annoying. For a long time I've planned to release the game without telling anyone. Just download the first version in the homepage of Kaduria and let it be there until someone finds it. Let people keep asking when it's released and all the time it's there.

Wednesday 8 January 2014

News

I have a new plan for Kaduria which is choose some items in todo-list and give them an id and estimated development time. Then follow that order and try to implement them. It should be easy and I already chose ten hours of programming for nine items.

Some items can't be implemented without knowing stuff related to them. It's the difficult part of game programming when you don't have a detailed plan or you don't yet know how stuff is going work with other stuff. It's also something I have to concentrate on better.

Those 9 items are about 20% of all unfinished items in the list, not counting level themes. The reason for that is that level themes are too big to be in todo list anyway, first they have to be broken into smaller pieces, because many level generation techniques are reused in other themes. That 20% is not accurate also because there are more "todos" written in the source code directly.

This new plan should be better than just looking at the source code puzzled what to do next.