Skip to content

GameFont

Nightinggale edited this page Oct 8, 2018 · 1 revision

What is GameFont?

The short answer is that it allows adding multi color sprites to text. They are used everywhere, like +2 food icon.

A more detailed answer starts with understanding how the game understands text. The game use Windows-1252 encoding. However it's actually just a subset of it because billboards and python only works with the characters where the 1252 value is the same as the unicode value. This means the row 8_ and 9_ aren't fully supported. Luckily odds are that we will never use those rarely used characters.

The game can represent text in two ways:

// 8 bit characters
char*
CvString
"text"


// 16 bit characters
wchar*
CvWString
L"text"

All of Windows-1252 bit in 8 bit characters. However the game has a concept called GameFont where sprites are added starting at address 8483 (decimal). This means to use GameFont characters, the string needs to use wide characters. All python strings are wide char.

How to use GameFont

Displaying GameFont is easy once you know how to do it.

// C++
szBuffer.append(CvWString::format(L" %c", iGameFont));
# python
(u" %c" %  (iGameFont))

Getting iGameFont is in most cases trivial. It's all about calling Cv*Info::getChar() (getMissionaryChar() for CvCivilizationInfo). All those functions are exposed to python and they are in the following classes

  • CvYieldInfo
  • CvSpecialBuildingInfo
  • CvFatherPointInfo
  • CvCivilizationInfo
  • CvBonusInfo

There are also some fixed symbols not related to xml. They are managed by the enum FontSymbols and are called like:

gDLL->getSymbolID(POWER_CHAR)
CyGame().getSymbolID(FontSymbols.POWER_CHAR)

Adding your own GameFont

Adding a new sprite

The sprites are stored in two files in Assets/Res/Fonts. They are called GameFont.tga and GameFont_75.tga. GameFont_75.tga is for font size 1 and 2 while GameFont.tga is for font size 3+ and city billboards.

Both files follows the same design. They are image files containing rows of blocks, each block being a character. The first 4 are the 8 bit characters and we should likely never have to touch those.

The rest (the majority) are what GameFonts are all about. Blocks of icons, which the exe will map into the font system. The files are horrible to edit by hand, meaning editing should be done by using the GameFontEditor tool.

Some not so obvious features: control+C and control+V works and can be used to move icons. Marker will give the character vertical offset, meaning lowering the marker value will make the icon appear lower relative to the surrounding text. Usually the default marker is the right one.

Adding or removing blocks still have to be done without the tool. It's open source and not updated since 2012, meaning it's free for anybody to add this feature. It has been the most requested feature for years. Clearing cells by backspace and moving more than one icon at a time would be useful too. You can encounter a case where you have to move all icons in a row 1 to the left or right.

Knowing the values of the icons

At game startup, the exe will read both files and map them into character IDs. It will call setChar/setMissionaryChar and assign values to the info classes. It will set FontSymbols to some value it decides. You can use everything without knowing what numbers it assigns to what and it's nice when it works as expected.

When it doesn't work, vanilla won't help you at all. We do however have a debug tool in WTP. Compile a Debug DLL (1-2 city radius doesn't matter). The Domestic Advisor will gain a new button to the right, which can be used to debug GameFont. It will show a table where ID and sprites are visible for all GameFonts. It also displays which xml entries use which GameFontID and button from those, if any.

python

While it can be used to detect GameFont IDs, the real use case for this is to check that an info class has the correct icon for both small and large GameFont.

Known issue: the GameFont table is big and lags a bit when opening.

City billboards

City billboards (the name sign in the main view) have a top row of icons. They are GameFont icons set by the exe calling

CvGameTextMgr::buildCityBillboardIconString()

It uses GameFont.tga.

Clicking on a row in the GameFont debug table will add a lot of GameFont characters below the city billboards, starting with the clicked character. It won't update them until after the Domestic Advisor is closed. Removing the billboard debug display is done by opening the Domestic Advisor and click on any button other than GameFont debug as this will remove the GameFont display from both DA and billboards.

billboard

Limitations/issues

Billboards use a different system internally and will only map up to POWER_CHAR, the last FontSymbols in vanilla. Where POWER_CHAR is placed depends on xml data and at the time of writing it gives 275 billboard characters. This means the number shouldn't be an issue, but because a lot of IDs are unused, we do have some icons after POWER_CHAR.

Billboard assigns IDs by counting blocks. Adding one in one row will cause an offset issue for the next row.

Non-billboard font reads blocks in chunks of 16. This means you can add blocks without hurting the next line right until all following lines shift 16 places.

Non-billboard font can skip blocks if empty, like skip a row if the first icon is empty or a chunk of 16 blocks if all are empty or something like that. Precise mechanics of this isn't fully understood.

Because of multiple ways of assigning, we are essentially dealing with 3 different lists of GameFont

  • GameFont_75
  • GameFont
  • GameFont for billboards

They can all 3 go out of sync and there is no real way of controlling it other than trial and error.

Exe hardcoding

[CHAR_BULLET] is hardcoded by the exe as in we can't change the GameFont ID once the exe assigns one itself. In fact we better make sure all vanilla FontSymbols stay where the exe wants them to be.

The exe will call setChar() at startup and it's followed by getChar() and for some mysterious reason if it doesn't read the same, it will crash. However once the exe is done with this (after xml reading, but still before the main menu), we are free to set the chars to whatever we want.

Adding your own getChar to a new info class

That or whatever else you want to use GameFont for. The exe won't do anything for you. This gives you the freedom to assign whatever ID you want. There are plenty of unused addresses, which can be used on city billboards and if you don't need that, then there is no known limitation to the number of GameFont IDs.

Just use some more clever setup, like assigning

// HARRY_CHAR is 0
gDLL->getSymbolID(HARRY_CHAR) + MAX_NUM_SYMBOLS + 10 + (index of xml file entry in question)

We can assign completely manually and set an int in xml for each instance of the info class, but auto assigning like this will adapt to changing lengths of rows/xml files, making it more maintainable. It would become even more maintainable if we figure out the padding approach and can set it to exploit this by always starting in a 16 icon chunk.