Skip to content
Xsear edited this page Aug 27, 2023 · 2 revisions

This document details some notes on how warpaints and colors work in the game.

Understanding color values

In the SDB, colors are often stored as uint32.

We understand the game to use 2 different ways to store colors in the SDB.

argb-color

In the SDB table dbvisualrecords::WarpaintPalette, you find colors stored as uint in for example the color1_highlight field. An example value is 4294967295. These fields are understood to be represent a 32bit ARGB value using 1 byte per color, such that the first 8 bits represent the Alpha value, the next 8 bits the Red value, then 8 bits Green, and finally 8 bits Blue, in order high to low bits.

light-dark-color

In the SDB table dbcharacter::Monster, there is a hair_color field with a uint value. An example value is 3461939200. This 32 bit value represents two 16 bit colors. The first 16 bits are "light" and the last 16 bits are "dark", in order of high to low bits. Each 16 bit color value uses 5 bits for red, 6 bits for green and 5 bits for blue, in order of high to low bits. The light and dark colors are blended together for the final result.

Retrieving Warpaint Palette Colors from the SDB

Warpaints Palettes, in dbvisualrecords::WarpaintPalette, contain a total of 7 colors in 14 fields. From reading the previous section, perhaps you can now infer that for example color1_highlight and color1_shadow, each contain an argb-color and together these two values can blend into a single light-dark-color.

That is how the colors should be sent down to the client from the server.

Here is some sample code to perform the conversion

function ffColorCompressAndCombine(argb_light, argb_dark) {

  function compress_8bit_to_5bit(value) {
    return Math.floor( ( value * 32 ) / 256 )
  }

  function compress_8bit_to_6bit(value) {
    return Math.floor( ( value * 64 ) / 256 )
  }

  function compress(rgb) {
    const input_red = (rgb & 0xFF0000) >> 16
    const input_green = (rgb & 0x00FF00) >> 8
    const input_blue = (rgb & 0x0000FF)

    const compressed_red = compress_8bit_to_5bit(input_red)
    const compressed_green = compress_8bit_to_6bit(input_green)
    const compressed_blue = compress_8bit_to_5bit(input_blue)

    // RRRRR GGGGGG BBBBB
    const result = 0x0000 | (compressed_red << 11) | (compressed_green << 5) | compressed_blue

    return result
  }

  // Skip alpha
  const light = 0x0000 | (argb_light & 0x00FFFFFF)
  const dark = 0x0000 | (argb_dark & 0x00FFFFFF)

  // Compress
  const compressed_light = compress(light)
  const compressed_dark = compress(dark)

  // Combine
  const combined = (0x0000 | ( compressed_light << 16 ) | compressed_dark) >>> 0

  return combined
}

Warpaint Palette Application

Chassis

We have 7 colors for each palette, and we know the Chassis/Battleframe uses an array of 7 colors in the network views.

Digging into the UI we can find the following names for the Chassis/Battleframe, and assume they are in the appropriate order:

ARMOR_COLOR_1
ARMOR_COLOR_2
ARMOR_COLOR_3
BODYSUIT_COLOR_1
BODYSUIT_COLOR_2
GLOW_COLOR_1
GLOW_COLOR_2

Applying Warpaint Palettes to a Monster/NPC

In the SDB table dbcharacter::Monster, we can find the following fields:

  • fullbody_warpaint_palette_id
  • armor_warpaint_palette_id
  • bodysuit_warpaint_palette_id
  • glow_warpaint_palette_id

We must also note the field chassis_id (Battleframe), and the associated table dbitems::Battleframe which has the following fields:

  • default_fullbody_palette_id
  • default_armor_palette_id
  • default_glow_palette_id
  • default_bodysuit_palette_id

Given what we see, the following approach has thus far given the most satisfying results:

  1. Retrieve the 4 palettes and all of their colors, prioritising the Monster table if they are set (not 0), and retrieve the default from Battleframe otherwise.
  2. Select one of the four palettes to use all of its 7 colors as the base palette, in the following priority order: fullbody -> armor -> bodysuit -> glow
  3. If armor is set, then override the colors 1-3 with those of that palette.
  4. If bodysuit is set, then override the colors 4-5 with those of that palette.
  5. If glow is set, then override the colors 6-7 with those of the that palette.

Warpaint Patterns

Usage

Patterns in dbvisualrecords::CziPattern have a one byte type_flags column. This is likely equivalent to slot_mask, found in the UI MainUI\Sinvironment\garage\BattleframeGarage.lua That would mean that the flags indicate which things that the palette can apply to:

2^0: Armor,
2^1: Bodysuit,
2^2: Weapon1,
2^3: Weapon2,

Thus, type_flags == 15 would imply that the palette can be applied to all of these slots.

Clone this wiki locally