PostApocalypse DevDiary 2: Of Saves and Trading

by admin

This is part 2 of an on-going series. Start here if you don’t know what’s going on.

Hello readers!

Last week I spent most of my time working on a school assignment, but I still managed to find some time to cram in some new features into this fancy indie game I’m making.

But most of all, I spent time playing one of my favorite indie games of all time: Mount & Blade: Warband. So I want to start this blogpost with a critique of that game.

What’s so bad about MB:W?

When looking for comments about it online, you’ll find a lot of people talking about the first person combat, and not a helluva lot of talk about the third-person world overview. Even though you’ll be spending close to 70% of your time in it!

The way it works is that you get an iconic representation of your party: a horse with a dude on it with a number representing its movement speed. All the other parties get the same treatment, so you can start fights by colliding with them.

However, that is just one way to get rich. You can also try your hand at trading, where you buy goods for cheap in a village or town and sell them in another town where the demand for that good is high. Of course you’ll have to factor in that some goods will lose value as they get eaten or start to rot, so to get rich you have to focus on the unspoilables like salt, velvet, tools and furs. But how do you know if the price you’re getting for an item is amazingly cheap or horrifyingly expensive?

You don’t. And it’s very problematic. In fact, I started a spreadsheet just to see what the average price of items was:

What is this, EVE online?

After a while, you start seeing patterns. For instance, if you see grain at <10 denars, you should always buy it. That's because can sell it to most towns and villages for 20-30 denars. But then get another problem: the game tells what for, but doesn't tell how much paid in first place! So spreadsheet also has a column selling price see that grain is quick profit, it's never much. However, if iron <70, ~300 A huge profit! And would be nice actually told about these things. If let discover workings of local economy, help make profit without bloodshed. But enough this hugely popular successful built by two people an apartment. Let's talk new features PostApocalypse!

Saving game state

This proved to be quite the headache. How do you store game state in a file? Well, remember how I talked about MVC last week? That model is very, very handy when you want store only the state of the game and not any additional fluff. You simply store the variables in the Data modules, because you don’t have to care about the rest!

But what should the save file structure look like? My first thought was to use an .ini like structure. And you should always start small before investing a lot of time an energy into a new system, so I started with saving and restoring the camera’s position and rotation:

[Camera]
Position=Vec3(123.5, 23.4, 677.0)
Orientation=Quat(0.0, 0.0, 0.0, 1.0)

This looks fine, but it has a problem. These guys can explain it much better: Engine Tech: Concurrent World Editing. They talk about the problems they had with their save file system and how they resolved it. The gist of it is that when you store floats as strings, you tend to lose precision real fast. Did you know that the floating point number “0.2” cannot be accurately represented in the floats we use in our floating point hardware? It’s true and it’s not a conspiracy! It’s just how the math works out.

The result of this is that it’s a bad idea to store floats as strings. So I looked at the article I linked earlier to see how they fixed it. Well, here’s what they do:

; position
    46.103531 : 42386a04
    108.079681 : 42d828cc
    8.694907 : 410b1e57

They store the float as both a string representation and they store it as a hexadecimal integer! That’s brilliant, because you can always read the hex correctly and convert it to a float.

But as I tried to implement this myself, I realized: why don’t I just make a binary file in the first place?

The tool I'm using here is called 010 Editor. It's awesome.

That probably looks like a big ol’ pile of gobbledygook to you. As well it should! But after reverse-engineering a great many stupid file formats, I know what makes a great file format.

The savefile

The most important thing of any binary format is the header. This helps identify the type of file you’re dealing with, but also the type of data. I use a PNG-like block structure, where each block has its own header.

This is the structure used for that:

	struct SaveSectionHeader
{
	char name[4];
	dword version;
	dword length_in_bytes;
};

Keeping the header separate like that allows you to load the entire header in one go, without those pesky conditionals. The name variable is used to identify the block. Right now I have only two blocks: “PASV” (PostApocalypse SaVe file) and “CAMR” (Camera). The first block allows you to quickly identify that yes, this is a PostApocalypse save file and it also tells you how many blocks are going to follow.

The camera block simply has its data (position and rotation) dumped into the block as bytes.

And that’s it. :) Surprisingly easy to work with, reliable and extensible! The version variable might seem like overkill, but it will vital in allowing backwards-compatibility. Hopefully, by the time this game reaches completion, I will still be able to convert and load this savefile.

The Graphics User Interface

UI is one of those things that nobody ever enjoys doing, but is vital to what separates a mediocre game from a great game. Mount & Blade: Warband, that game I use to steal… I mean get inspired by? It has a terrible user interface. It tells you either too much:

Why not just tell me: 108 prisoners. Sure would save screen estate.

Or too little:

How many days can I survive on these food supplies? How much does each horse increase my movement speed? What the hell is encumbrance even for?

So I set out to improve upon that by designing a great UI from the start and integrating it with the rest of the game.

Firs things first: that silly font rendering I had had to go. Obviously it was Windows-specific and it would be a pain to port to other platforms. But I’ve used FreeType in the past and I could never get it to render quite right. Also, I don’t want to spend all of my time designing a system I don’t really care for, so let’s take an existing package.

I don’t want to use CEGUI, because last time I used it I produced code looking like this:

That RockScroll image on the right side really tells you all you need to know about the GUI code.

But I didn’t want to let my prejudices get in the way of progress, so I gave it a try anyway. And then I found out that you can’t really control anything. Don’t want to use XML to load scheme files? Too bad, the scheme loader uses private, not protected members. Want to use your own font render? Yeah, no, this one just works for us you know?

So I decided to look at alternatives. And I ended up with libRocket, the cute little engine that could.

Even its logo is adorable

Now I should explain my thought process regarding it:

  • Stage 1: Dismissal – “Using HTML and CSS to render UI’s? Come on, that’s overkill! Besides, I’ll bet it’s really slow.”
  • Stage 2: Testing the waters – “I bet the source is a real mess. No, actually it’s not. And I get to specify my own renderer? Sweet!”
  • Stage 3: Acceptance – “Listeners are awesome! Input is awesome! I love this library!”

That’s not to say I haven’t had problems with it. For instance, what if you want to know whether input should be handled by the game or by the UI? I still haven’t figured out a reliable way, but here’s what I came up with:

	void InterfaceManager::Update()
{
	m_Context->Update();

	Rocket::Core::Element* hover = m_Context->GetHoverElement();

	Rocket::Core::String tag_handled;
	if (hover)
	{
		tag_handled = hover->GetTagName();
	}
	else
	{
		tag_handled = m_Context->GetFocusElement()->GetTagName();
	}

	m_HandledByGUI = true;
	if (tag_handled == "#root")
	{
		m_HandledByGUI = false;
	}
}

It seems kind of… clunky, to be honest. And then there’s the div hell web developers should be familiar with:

I just want that little X to the right of that dummy text. WHY MUST THIS BE SO DIFFICULT?

But overall: I really like it! Using HTML/CSS for UI makes a lot more sense than trying to get the same results using relative and absolute offsets like in CEGUI.

Next week

I’m hoping I can finally start getting some gameplay next week. I have the basic systems set up, now it’s a matter of getting more content.

And that may involve… me learning GIMP and Blender. D:

See you next time!