Skip to content

Adding or using Custom sprites

Loobinex edited this page Apr 9, 2024 · 32 revisions

Introduction

It's possible for users to add custom sprites (animations made from pictures) to KeeperFX. Requires version 0.5.0 or newer.

Overview

Dungeon Keeper and KeeperFX use sprites from .dat files for all things in game, like Creatures, Objects, Traps and Projectiles. These are PNG images for every frame of animation, and when available, for each view direction too. The extracted images can be found here.

The custom sprites do not need to be merged into these .dat files, but can be bundled in .zip files, along with a json file that describe how these .png files make up the sprites.

Creating a Custom Sprite

Exporting the PNG files

Sprites in Dungeon Keeper can be as small as 32x32 (wooden barrel) or up to 128x128 pixels large (Hero Gate, big lairs).
Aim at roughly this target resolution when exporting your individual images.

PNGs will automatically be converted to Dungeon Keeper's limited palette.
Try to stay within Dungeon Keeper's limited range of colours. Otherwise, smooth gradients may turn into harsh cut-offs, or rogue pixels may stick out.
If there are issues, programs such as GIMP let you convert and clean up images manually. To import the DK palette into GIMP, use this file. For Photoshop use this file

You should have images for both perspectives (top down and first person).
When not possessing a creature, players look down at the game world at a ~45 degree angle. Top down sprites need to be displayed at this angle too, the exact value is (360 * 266 / 2048). (This is easier by making a 3D model and rendering it from different cameras.)

Simple sprites like the barrel have only 1 rotation.
Their image will act as a billboard and always face the player.

But optionally, sprites can have multiple rotations. They change their image as you rotate your view, faking a 3D appearance.
You'll need 5 rotations: a view from the Front, Right Front, Right, Right Back, Back.
The naming convention is "front", "rf", "right", "rb", "back".
The left side is mirrored automatically.

For animations, the number of frames should be somewhere between 4 (basic lairs) to 12 (Dungeon Heart) or even 16 (spinning spell books and door keys).

Finding or Creating a Model

3D modelling programs like Blender can be used to take a 3D model to then render into images.

Dungeon Keeper's limitations make it simpler than it would be for a modern game:

Details (and small mistakes) will get lost when rendering at such small resolutions.
Texturing is very simple and flat colours will often do the trick.
Simple shaders (e.g. Blender's Toon BSDF, Glossy BSDF) fit better than modern realistic standards.
Light bounces should be set to (near) zero to emulate 3D rendering from 1997.

If you can't make one yourself, many websites such as https://opengameart.org/ offer models for free.

In Blender:

Change "Output Properties -> Output" to define the folder your rendered images will go in.
Keep 8-bit color depth checked.

Check "Render Properties -> Film -> Transparent" to export the sprite with no background.

In the Compositing tab, round transparent pixels. That way, pixels with at least 50% transparency are removed.
Dungeon Keeper sprites are sharp, with distinct outlines. Consider lowering "Film -> Pixel Filter -> Width" to limit blurring further.

Blender template:

Download an example file here

It has 10 cameras set up: 1 for each angle, 5 for each perspective.

I use the Multiple Camera Render plugin to select them all and render all 10 at the same time.

The Pixelate node group in the Compositing tab does 2 things:

  1. Limit the number of colours slightly (to 256) - for more noticeable bands of colours, and easier conversion later.
    Change the first Value in the node group to tweak this.
  2. Round transparent pixels

Creating the JSON file

A JSON file needs to be created to explain to the game how the different png images come together to form a sprite. The easiest way would be to take an existing JSON file from another sprite and modify it for your custom sprite. A json file can be made with any text editor, but using an application that can validate them is recommended, as it's easy to miss a bracket or comma and have the entire file not work.

Enter all the filenames of the images making up the sprite into the json file, following this format:

[ // opening square bracket, for the entire file, needs closing up.
{ // opening curly bracket for each sprite you put in the zip. A comma after each one except the final one.
  "name": "CUSTOM_SPRITE_NAME", //The name given here will be used to assign the sprite to an object. May only use capitals.
  "rotatable": false,  //Set to either true or false, when set to false the first direction is the only one to ever be shown.
  "fp_offset_x": 24, // set these values to find the exact point where the sprite is set the object. Usually you want the bottom middle of the image.
  "fp_offset_y": 95, // The offset can be set per sprite, but also per file. This is at the "file" level.
  "td_offset_x": 23,
  "td_offset_y": 83,
  "fp_shadow_offset": 0, // Offset from bottom line of a sprite how shadow should be drawn for FP view
  "td_shadow_offset": 0, // Offset from bottom line of a sprite how shadow should be drawn for TD view
  "fp": [ // After the configuration settings, add all the directions of first person sprites. The 'fp' name is required, and the square bracket needs closing and a comma.
    [ // Square bracket for each direction. Front, Front Right, Right, Back Right, and Back in that order. A comma after each one except the last one. Non-rotatable sprites need only one direction.
      { // The list of the files for this direction, each file is an animation frame. If there's just one, the object does not animate. Again close each curly bracket with a comma except the last one.
        "file": "banner3fpfront0001.png",
        "offset_x": 23, // Per sprite offset would override fp_offset_x
        "offset_y": 95 // Per sprite offset would override fp_offset_x
      },
      {
        "file": "banner3fpfront0002.png",
        "frame_flags": 1 // No shadow for this particular image
      },
      {
        "file": "banner3fpfront0003.png"
      }
    ] //final direction, so no comma.
  ], // next view is coming, so you need a comma.
  "td": [ //The top down files added the same way as the fist person ones, but with unique file names.
    [
      {
        "file": "banner3tdfront0001.png"
      },
      {
        "file": "banner3tdfront0002.png"
      },
      {
        "file": "banner3tdfront0003.png"
      }
    ]
  ] //closing the final view.
} // End of the sprite. Add a comma here if a second one follows.
] // Closing the file

The json file gets zipped in with the image files, and now the sprite file is ready to be used. Be sure to not use compression when zipping, since that adds load times to maps to decompress the zip.

Json parts

Per animation options:

  • name - how this animation would be referenced in other files
  • rotatable means object could be rotated and have multiple views. Each rotation angle should have same number of frames

There are following json options could be applied either to each individual image in animation sequence or for all FP or for all TD of animation They are listed as <per frame> then <First Person per file> then <Top Down per file>

  • frame_w fp_frame_w td_frame_w - width of sprite image (default for whole image size)

  • frame_h fp_frame_h td_frame_h - height of sprite image (default for whole image size)

  • offset_w fp_offset_w td_offset_w - width offset (defaults to 0)

  • offset_h fp_offset_h td_offset_h- height offset (defaults to 0)

  • offset_x fp_offset_x td_offset_x - x offset (defaults to 0)

  • offset_y fp_offset_y td_offset_y - y offset (defaults to 1 - height)

  • shadow_offset fp_shadow_offset td_shadow_offset - distance between bottom line of a sprite and shadow (defaults to y offset)

  • frame_flags fp_frame_flags td_frame_flags - Flags.

    • 1 - means no shadows

Using a Custom sprite on a map

When using a Custom sprite, first be sure it is installed properly. Do this by placing the zip file with the sprite in the \fxdata folder to use it game wide, in the CONFIGS_LOCATION as specified in the campaign/mappack config file to use it campaign/mappack wide, or name the zip file e.g. map00440.zip and place it with the other files of map 440 to use it on that map only. When the sprite is available for KeeperFX, the next step is to configure an object to use the new sprite instead of the default sprite.

Modifying an Object for a campaign or mappack

Objects in KeeperFX by default use sprites from the .dat file, defined on the AnimationID field for every object in objects.cfg. The default objects.cfg file can be found in the \fxdata folder, but the campaign or map pack you want to use it on may have a custom 'object.cfg' in the folder defined on the CONFIGS_LOCATION of the campaign config file. When creating a pack or campaign be sure to make such a custom object.cfg file which holds just those objects that are new or changed compared to the defaults.

The objects.cfg file - like all .cfg bundled with KeeperFX can be edited with any text editor, like notepad++.

When for example you want to make the placeholder statue look like a banner, look for the object in object.cfg:

[object132]
Name = STATUE5
Genre = DECORATION
AnimationID = 958

Then update the AnimationID with the name of the Custom Sprite. If this name is not known to you, open the zip file of the custom sprite, open the json file inside with a text editor, and take the value after "name". It should look something like this:

[object132]
Name = STATUE5
Genre = DECORATION
AnimationID = BANNER

When the sprite is rotatable, you could also set the wind direction of the object. Possible values here are: NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST and NORTHWEST. Add this direction with a colon attached to the AnimationID. This would look like this:

[object132]
Name = STATUE5
Genre = DECORATION
AnimationID = BANNER:SOUTHWEST

Changing an object with the Level Script on a single map

Using the SET_OBJECT_CONFIGURATION script command it is possible to change the AnimationID as well. You can make this conditional on map progress. Define the AnimationID as described in the previous chapter.

An example level script:

REM We should have banners facing south
SET_OBJECT_CONFIGURATION(STATUE5, AnimationID, BANNER:SOUTH)
REM If the heroes are defeated, the banner should face north.
IF(PLAYER1,DUNGEON_DESTROYED >= 1)
  SET_OBJECT_CONFIGURATION(STATUE5, AnimationID, BANNER:NORTH)
ENDIF

You can change between different Custom and default sprites, and wind directions as often as you want, provided you remain within the regular script limits.

It is also possible to bundle a map specific objects.cfg, for a map with number 2345, the file would be named map02345.objects.cfg and should be bundled with the other level files that start with 'map02345'. Here too be sure to only include the difference compared to the bundled objects.cfg

Bundled custom sprites

name file thumbnail rotatable animated
BANNER
BANNER_NR
banner.zip banner thumbnail 5 sides
No
No
DRUID### druid.zip banner thumbnail 5 sides Yes
FERN
FERN_SMALL
FERN_BROWN
FERN_BROWN_SMALL
fern.zip fern thumbnail No No
GIMLY
GIMLY_NR
gimly.zip gimly thumbnail 5 sides
No
No
GOLDEN_ARMOR
GOLDEN_ARMOR_NR
goldenarmor.zip goldenarmor thumbnail 5 sides
No
No
MUSHROOM_GREEN_LUM
MUSHROOM_RED_LUM
MUSHROOM_YELLOW_LUM
mushrooms.zip mushroom green thumbnail No No
TMAGE### time_mage.zip banner thumbnail 5 sides Yes
TRAP_GEM_RED
TRAP_GEM_GREEN
TRAP_GEM_BLUE
TRAP_GEM_YELLOW
gemtraps.zip gemtrap thumbnail No Yes
TRAP_GREEN
TRAP_PEACH
TRAP_YELLOW
TRAP_WHITE
TRAP_GREENPEACH
trapcolors.zip trapcolor thumbnail
trapcolor thumbnail
No Yes
VINE_TORCH torches.zip vinetorch thumbnail No No
WHITE_FLAG whiteflag.zip whiteflag thumbnail No Yes
WIND_BANNER
WIND_BANNER_NR
windbanner.zip windbanner thumbnail 5 sides
No
Yes