Following my Blood Omen 2 animation, then Voyage (which I didn't think to announce on this site at its release), I've posted my first sizeable project made with Krita.

And for the sake of thoroughness, here is my previous project, Voyage:

Animation: Blood Omen 2

Following over two years of work, I am now releasing this animation based on the cinematic introduction of Blood Omen 2's first major antagonist, Faustus.

Aside from creating an artistic tribute to a game I greatly value, my hope is to catch the attention of companies specialised in 2D animation, who I invite to reach out through my contact form.

Software: Clip Studio Paint EX, OpenToonz and Tahoma2D for animation; Ravioli Game Tools for resource file extraction; Audacity for audio manipulation; Blender for video editing.

Aside from sounds taken from the game's files, additional sound effects are from Alan Dalcastagne's media library.

Translating Doom 3’s PDAs

While doing some tests with Doom 3's .pda files, I noticed that no matter what language I set the game to, the emails in Doomguy's PDA were always in French, even though menus were affected by the language settings. How is the language for PDAs decided? The answer is complicated, but the solution to my problem was rather simple.

Before I go on, it's important to note that I did install Doom 3 in French.

.pda files

These files hold the data for the PDAs the player will find throughout the game. They can contain several types of elements, such as PDA definitions (which specify information like the name and post of the PDA's owner) and email definitions (which specify a date, a sender, a recipient, and obviously the email's message), among other things.

Language management

.gui, .map and .def files will often reference variables with the format #str_01234 ; these are defined in files named english.lang, french.lang, etc. that link each variable to an explicit string in the target language.

The issue

Unlike the files mentioned above, .pda files do not allow for the use of "#str_" variables.

In Doom 3, resource archives (pak000.pk4, pak001.pk4...) are read in alphanumeric order. If pak003.pk4 and pak005.pk4 contain a file of the same name, pak005.pk4's version will take priority and the other will be ignored. The choice of which file to use depends exclusively on this system, and does not take the game's language into account.

If Doom 3 is installed in French, for example, the installer will add two .pk4 archives called zpak000.pk4 and zpak003.pk4. Naturally, these archives will be read last and the (French) PDA files held within will take priority over the (English) files held in the default archives.

In the vanilla engine, this precludes the possibility to set PDA text according to the chosen language. Fortunately, the changes required to support it are quite simple.

Say we want to start with emails' text property, which is the main message of the email.

In neo/framework/DeclPDA.cpp, find the idDeclEmail::Parse() function. As the name implies, PDA emails are interpreted here.

Find the section that deals with the text property:

if ( !token.Icmp( "text") ) {
    src.ReadToken( &token );
    if ( token != "{" ) {
        src.Warning( "Email decl '%s' had a parse error", GetName() );
        return false;
    while ( src.ReadToken( &token ) && token != "}" ) {
        text += token;

In the while loop, all we need to do is check if the text is a string variable; if so, we can easily retrieve its value:

while ( src.ReadToken( &token ) && token != "}" ) {
    if ( idStr::Cmpn( token, STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ) {
        text += common->GetLanguageDict()->GetString( token );
        text += token;

STRTABLE_ID is the string "#str_" and STRTABLE_ID_LENGTH is its length, 5 characters.

Once compiled, this code will allow us to translate PDA text for all supported languages, the same way this is done for map and gui files.