Team Fortress 2

Team Fortress 2

130 ratings
The ADVANCED War Paint Guide
By Renim ☕
This is the advanced War Paint guide, going over how War Paint mapping works and how you can visualize your own code tweaks.
THIS IS VERY LONG
3
5
2
2
6
2
3
2
   
Award
Favorite
Favorited
Unfavorite
Before We Start
This guide is written under the assumption you know the basics of how war paints are currently created, as I will not be going over the very basics of reskinning existing war paints in this guide. I will however be going over the basics of how war paints work on a coding level so you can understand the process of remapping an existing template to fit your needs.

Things you will need for this guide:
  • Notepad++ for .json file editing
  • Photoshop, or any texture/photo editing program that can allow you to offset your texture
  • VTFEdit to import your manually generated war paint layouts
  • A copy of proto_defs.vpd that is decompressed
  • A lot of patience - you might be individually mapping every texture of every weapon
And the "War Paint Master Folder" supplied here[drive.google.com] (150 Megabytes)

This includes everything you will need to map your own war paints, plus a few other useful items. The full list is as follows:
  • All color corrected "Groups" textures in PNG format (More info on what this is below)
  • All "Albedo" and "AO" textures for each war paintable items
  • .vmt files that enable albedo tinting on all war paint templates
  • .vmt files that disable albedo tinting on war paints that have it innately
  • And a "clean edges" version of the War Paint item, just to make your promos look slightly better.


While these guides aren't necessary to this guide, they are very important to war paint creation if you are just starting out:
How Exactly Do War Paints Work?
On a mechanical level, war paints work by applying a set of textures to a weapon model via a secondary texture called "p_*Weapon*_groups". This texture is located within the same folder as the weapons texture itself, and is different per weapon.

This texture is used by a file called "proto_defs.vpd" (located in the scripts folder in tf2s main directory) to apply your textures to a weapon model using a set of variables as dictated by an "operation_template", which also functions as the order of operation instructions for a war paint.

This is the basic description with no elaboration. But a bit further on I will be going over the concepts of a "Groups" texture, what proto_defs.vpd is, and the functional use of an "operation_template", as to not overwhelm you all at once with descriptions of each one of these concepts.
What are "Weapon Groups"?



A "Groups" texture is a red monochromatic image that allows you to map your textures onto a weapon. The different shades of red correlate to different parts of the model.

Here is a gif comparison between p_shotgun_groups and c_shotgun:



Green and blue spots will appear on some "Groups". Those are placed over specific parts of weapons (for example, the bullets of the Shortstop) so they do not get textures applied to them in game.

In your image editing software, open up the Groups texture of the weapon you would like to work with. Use a color picker tool in your software and select a solid area in the texture. Now view the RGB color information. The "Red" or "R" in the RGB value represents the ID of that area to be placed in the .json war paint code.



What is proto_defs?
proto_defs.vpd is the master file for features added in the Jungle Inferno major update. It controls the ConTracker contract layouts, Contract rewards, War Paints, and Mercenary Park Gift Shop rewards and prices.

The main reason this is compressed is due to how massive it is. When uncompressed the file is a whopping 26 million characters long. Due to that, opening the file on its own will be laggy and unresponsive, so it is recommended to copy all War Paint templates to their own file for future use.

There is a way you could go about decompressing this file, but it's highly unnecessary as there's already an uncompressed version of the file on GitHub[github.com]. It is a few years out of date; however, this shouldn't matter because all you need from it is a war paint template.

With this outdated proto_defs, you will not be getting any war paints past Scream Fortress 2018. But due to how war paints are currently implemented, you are only truly missing out on 2 unique war paint bases from past this era, those being Necromanced and Swashbuckled. You can easily recreate how those function with code tweaking, which will be explained later in this guide.

One thing should be noted about this file: You cannot modify this file directly and visualize your changes in-game. proto_defs.vpd is .sig locked, meaning if the signature check fails the entire game will crash until the signature check passes. The only way it passes is if it is unmodified.

While you cannot modify War Paint variables directly, you can supply pre-modified code with your submissions for Valve to implement in future updates. So long as you're not adding variables that aren't innately used on that War Paint base, it would be almost no effort on Valve's end to implement so long as your code is typo-free.
What is an "operation_template"?
"operation_template" is a variable that points to the order of operations instructions for a War Paint; it is how the War Paint properly constructs itself so the layering is correct in-game. Along with that, "operation_template" tells the War Paint exactly what variables are used for that War Paint template. If the variable is not included within "operation_template", then that function will just not work.

"operation_template" is located directly above the "paintkit_tool" section of variables.


There are many templates that "operation_template" can point to, most of which are labeled in the "tags" section in the header of every War Paint in proto_defs.vpd.

I will include every "operation_template" ID and name here so you can assign the correct Operation Templates yourself:

Used Operation Templates:
  • 929, yeti_multiply_skin_with_stickers (4 textures, 3 stickers, multiply layer applied to textures 3 and 4)
  • 918, custom_uv (reskinned weapon UVs)
  • 826, multiply_skin_with_stickers_team_color (4 textures, team colored, 2 stickers)
  • 825, one_sticker_three_texture_inner_wear_blood (3 textures with inner wear, 1 sticker)
  • 824, three_sticker_four_texture_inner_wear
  • 821, one_sticker_three_texture_inner_wear
  • 819, one_sticker_five_texture
  • 818, four_texture_over_albedo_team_colored (4 textures, team colored, bare metal exposed)
  • 817, four_texture_over_albedo (4 textures, bare metal exposed)
  • 815, two_texture_over_albedo (2 textures, bare metal exposed)
  • 814, multiply_four_texture (4 textures with 4 multiply layers applied to them)
  • 809, four_texture_team_color
  • 808, three_texture_team_color
  • 807, two_texture_team_color
  • 802, four_texture
  • 801, three_texture
  • 800, two_texture
  • 797, brown_ao (used by Mannana Peeled and Pina Polished for its custom blood texture)
  • 791, scratches (used by Operation Templates to load scratches into the gun)
  • 790, ao (used by the Operation Templates to load dirt, blood and the AO texture)

Unused Operation Templates:

  • 823, one_sticker_add_multiply_random_two_texture (2 textures, 1 sticker with a gradient mapped to it?)
  • 822, one_sticker_random_multiply_two_texture (2 textures, 1 sticker with 2 gradients mapped to it?)
  • 820, two_sticker_three_texture_inner_wear
  • 816, three_texture_over_albedo (3 textures, bare metal exposed)
  • 811, three_sticker_four_texture
  • 810, five_texture_team_color
  • 806, coffinnail (9 textures, team colored, 1 sticker, inner wear bound to textures 5, 7, and 9)
  • 805, five_sticker_five_texture (5 textures, 5 stickers, sticker 3 has 10 permutations)
  • 804, three_texture_with_overlay (3 textures, with a texture overlay bound to the first texture)
  • 803, five_texture
  • 796, _____ao_dirt_blood__scratches_____ (AO, dirt, blood, and scratches test)
  • 795, _____--_dirt_blood__scratches_____ (dirt, blood, and scratches test)
  • 794, _____--_----_-----__scratches_____ (scratches test)
  • 793, blank_ao (no AO texture loaded, used by previous 3 Operation Templates tho)
Master List of War Paint Variables
This will not be going over wear-related War Paint variables due to not being able to test how they work directly in the game. If we ever get War Paint tools I will update this guide to include a breakdown on how all of those work.

For this list, any instance of x means a number between 1 and 5, any instance of a y is a number between 1 and 10, and any instance of z is a number between 1 and 3.

  • weapon_albedo
  • weapon_wearblend
  • weapon_groups
  • texture_layer_x
  • texture_layer_x_blue
  • texture_layer_x_rotate
  • texture_layer_x_translate_u
  • texture_layer_x_translate_v
  • texture_layer_x_scale_uv
  • texture_layer_x_select_y
  • texture_layer_x_flip_u
  • texture_layer_x_flip_v
  • multiply_layer_x
  • multiply_layer_x_blue
  • multiply_layer_x_rotate
  • multiply_layer_x_translate_u
  • multiply_layer_x_translate_v
  • multiply_layer_x_scale_uv
  • inner_layer_x
  • albedo_select_y
  • sticker_z_base_z
  • sticker_z_weight_z
  • sticker_z_dest_tl
  • sticker_z_dest_tr
  • sticker_z_dest_bl
  • custom_painted

These are the current War Paint variables that are fully coded and are used on at least one War Paint template.

Along with those there are a handful of completely coded in and functional variables that are currently unused on any War Paint in-game, so how they fully function is currently unknown. Use these at your own risk.

  • albedo_layer_z
  • albedo_layer_z_scale_uv
  • texture_layer_y
  • texture_layer_y_blue
  • texture_layer_y_rotate
  • texture_layer_y_translate_u
  • texture_layer_y_translate_v
  • texture_layer_y_scale_uv
  • texture_overlay
  • texture_overlay_rotate
  • texture_overlay_translate_u
  • texture_overlay_translate_v
  • texture_overlay_scale_uv
  • sticker_x_base_y
  • sticker_x_weight_z
  • sticker_x_dest_tl
  • sticker_x_dest_tr
  • sticker_x_dest_bl
  • multiply_sticker_layer_z_base_z
  • multiply_sticker_layer_z_weight_z
  • multiply_sticker_layer_z_dest_tl
  • multiply_sticker_layer_z_dest_tr
  • multiply_sticker_layer_z_dest_bl
  • multiply_sticker_layer_z_rotate
  • multiply_sticker_layer_z_translate_u
  • multiply_sticker_layer_z_translate_v
  • multiply_sticker_layer_z_scale_uv

It is safe to assume, however, that if textures go up to 9, and stickers go up to 5 with 10 permutations per sticker, that you can safely go to those numbers on your own War Paints.
War Paint Variables (weapon_, texture_layer_x)
weapon_albedo

The variable "weapon_albedo" is the base texture of every weapon, being the texture of the weapon that gets revealed at higher wear levels. These typically load the textures of p_*weapon*_albedo.

weapon_wearblend

The variable "weapon_wearblend" is how the textures themselves wear down at higher wear levels, which is different per weapon.

weapon_groups

The variable "weapons_groups" is how each weapon is mapped. Most weapons in the "workshop" folder only have one "groups" texture, but all Valve-made weapons have anywhere between 1 and 6 to choose from as your baseline for texture mapping.


texture_layer_x

"texture_layer_x" is your texture, primarily set in what I refer to the "master" set of variables that are set for every weapon, instead of per weapon.
These "master" variables are all typically right after the header, and have the prefix "name": instead of "variable":.



The way this variable is set up, texture_layer_1 is always set to "yeti_fur" as an example.
I will go over what "inherit": means in the mapping section of this guide.

However, a few War Paints that do not have a texture_layer_1. This is because if texture_layer_1 is not bound, it instead defaults to the weapon albedo texture. The current War Paints that utilize this are Autumn mk.II, Spirit of Halloween, Anodized Aloha, Macabre Web Mk.II, saccharine striped, and Elfin Enamel.

There is one variant to texture_layer_x, which is texture_layer_x_blue. This variable sets a War Paint to have a team colored version of that texture if the War Paint template is set up to properly have team coloring. Even if only one texture is team colored, all texture_layer_x variables in the War Paint must have a texture_layer_x_blue variable alongside it.


texture_layer_x_rotate

Sets the rotation of that texture, typically set to "0 360", meaning it will be anywhere between 0 degrees and 360 degrees. This can also be set to a single number, as explained later.

For a few examples, here is a "master" variable, and a "normal" variable:



"texture_layer_x_translate_u"
"texture_layer_x_translate_v"




"texture_layer_x_translate_u" and "texture_layer_x_translate_v" are used to tell the texture where the looping point starts.
The first and second numbers in "string" (or "value" in this example) represent the range in which the looping start point can be randomly assigned. Therefore, if both numbers are the same, that is the only point at which the looping can start, meaning the looping is locked.
"texture_layer_x_translate_u" pushes the texture to the left, and "texture_layer_x_translate_v" pushes the texture upwards. A value of 0 means 0, and a value of 1 means the maximum resolution of that specific weapon's texture.
If your texture scale is less than 1.0, you must increase your maximum translate_u and translate_v so that if you multiply it by your texture scale, it equals 1 or greater.

War Paints are generated using an RNG seed. Seeds will move the texture left or up a random number of pixels. But if you wanted to lock your texture so it does not move left at all, you would do this:

"variable": "texture_layer_x_translate_u",
"string": "0 0"

"variable": "texture_layer_x_translate_v",
"string": "0 1"

This would lock the texture from moving horizontally, but not vertically.

texture_layer_x_scale_uv


The variable "texture_layer_x_scale_uv" scales your texture within the texture space. The higher the number, the smaller your texture will appear, and the texture will repeat itself (or "loop") to fill out the texture space.

If you're having a hard time grasping how texture_layer_x_scale_uv works, go to beta.loadout.tf, select any War Paintable item, and press Shift + W to open the War Paint Editor. Select any War Paint and mess around with the "scale u" and "scale v" options and see how the scaling works.

Although loadout.tf has individual U and V scaling options, the TF2 War Paint code does not. For it to be accurate to the game, you will need to make sure in the loadout.tf War Paint Variable Editor that both U and V are the same.

texture_layer_x_select_y



The variable "texture_layer_x_select_y" dictates where that specific texture is mapped to based on the shade of red from the Groups texture. Only textures 2 through 5 have this variable, as texture_layer_1 is by default always bound to the entire canvas.

texture_layer_x_flip_u
texture_layer_x_flip_v


When these variables are enabled on an operation_template, the texture associated will flip itself on either the horizontal or vertical axis based on the seed.

War Paint Variables (multiply_layer_x, inner_layer_x, albedo_select_y)
multiply_layer_x variables

The "multiply_layer_x" variable is similar to the "texture_layer_x" variable, but its use case is to multiply a second texture on top of another texture. This variable is used on War Paints such as Macaw Masked, Leopard Printed, Tiger Buffed, and Yeti Coated to give certain textures a randomized color.

Similar to the texture_layer_x set of variables, multiply_layer_x has a set of sub-variables that tell the texture what to do, which are:
  • multiply_layer_x_blue
  • multiply_layer_x_rotate
  • multiply_layer_x_translate_u
  • multiply_layer_x_translate_v
  • multiply_layer_x_scale_uv
These work similar to the texture_layer_x variants, but with a small caveat on multiply_layer_x_translate_u and multiply_layer_x_translate_v. If your texture scale is set to below "1.0" like all current gradients are, you will need to increase the maximum value of multiply_layer_x_translate_u and multiply_layer_x_translate_v to equal what "1" would be. For example on Yeti Coated:

025 x 40 = 1, so you must increase the maximum U and V values to 40 to compensate.

inner_layer_x

The "inner_layer_x" set of variables work similarly to the "texture_layer_x" variables, but only appear at higher wear levels underneath the texture of the same number. This is the variable that gives the custom wear that Park Pigmented, Mananna Peeled, Pina Polished, and Misfortunate reveal at higher wears.

These variables have no sub-variables, instead they use the exact same settings issued by texture_layer_x. For example, if texture_layer_1_rotate is set to "0 360", then "inner_layer_1"s rotation will also be set to "0 360".


albedo_select_y

albedo_select_y is a variable used in conjunction with inner_layer_x. albedo_select_y works similar to texture_layer_x_select_y, but instead of applying a texture to a specific part of a weapon, you are replacing the inner wear from "inner_layer_x" with the albedo texture of the weapon. It is currently only used to disable inner wear on the War Paint item.
War Paint Variables (Stickers, Miscellaneous)
sticker_z_base_z
This variable denotes what the file path for the sticker is. It works the same way as texture_layer_x.

sticker_z_weight_z
This variable denotes the rarity of a sticker, 0 being 0%, and 1 being 100%.


sticker_z_dest_tl
sticker_z_dest_tr
sticker_z_dest_bl


These 3 variables correlate to the top left, top right, and bottom left pixels of a sticker and where they are placed on the UV of a weapon.
Each variable uses 2 numbers to calculate the x and y coordinates of that pixel location.

It has a minimum value of 0, with a maximum value of 1, with a 0 being 0, and a 1 being a value of 2048.


I will go into depth on sticker mapping later in this guide.

custom_painted

This variable is only used on the Dragon's Fury War Paint. Instead of loading individual textures for a weapon, it loads a different UV that is applied to the model.

material_override
This is found in between the "data": and "variable": sections on each individual item within a War Paint. This enables a different VMT to be loaded instead of the default weapon VMT file. This is what gives certain War Paints the metallic sheen and glow in the dark properties.

can_apply_paintkit
This is found between the "data": and "variable": sections of the War Paint item. This is always set to false, so you cannot apply a War Paint to a War Paint.


has_team_textures
This is found directly before "paintkit_tool". This tells the war paint if it has team colored textures.


Not all War Paints have this variable, but any War Paint that was originally team colored that was modified by Valve changes this variable from "true" to "false".
Internal Names for All War Paints
Below are the internal names for each War Paint for the sake of searching for its template within proto_defs. Here they all are:
  • kit_wrappedreviver - Wrapped Reviver Mk.II
  • kit_carpetbomber - Carpet Bomber Mk.II
  • kit_maskedmender - Masked Mender Mk.II
  • kit_woodlandwarrior - Woodland Warrior Mk.II
  • kit_forestfire - Forest Fire Mk.II
  • kit_backwoodsboomstick - Backwoods Boomstick Mk.II
  • kit_woodsywidowmaker - Woodsy Widowmaker Mk.II
  • kit_nightowl - Night Owl Mk.II
  • kit_ironwood - Iron Wood Mk.II
  • kit_plaidpotshotter - Plaid Potshotter Mk.II
  • kit_bovineblazemaker - Bovine Blazemaker Mk.II
  • kit_civilservant - Civil Servant Mk.II
  • kit_smalltownbringdown - Smalltown Bringdown Mk.II
  • kit_civicduty - Civic Duty Mk.II
  • kit_deadreckoner - Dead Reckoner Mk.II
  • kit_autumn - Autumn Mk.II
  • kit_nutcracker - Nutcracker Mk.II
  • kit_macabreweb - Macabre Web Mk.II
  • workshop_equatorial_bloom - Bloom Buffed
  • workshop_bonus_ducks - Quack Canvassed
  • workshop_cashflow - Bank Rolled
  • workshop_triple_alliance - Merc Stained
  • workshop_head_count - Kill Covered
  • workshop_fiery_inferno - Fire Glazed
  • workshop_pizza_party - Pizza Polished
  • workshop_sponsored_scout - Bonk Varnished
  • workshop_rising_star - Star Crossed
  • workshop_lucky_camouflage - Clover Camo'd
  • workshop_defender_of_the_freedom - Freedom Wrapped
  • workshop_premium_cardboard - Cardboard Boxed
  • workshop_pyroland_potpourri - Dream piped
  • workshop_miami_element - Miami Element
  • workshop_neo_tokyo - Neo Tokyo
  • workshop_geometrical_teams - Geometrical Teams
  • workshop_bomber_soul - Bomber Soul
  • workshop_uranium - Uranium
  • workshop_cabin_fevered - Cabin Fevered
  • workshop_polar_surprise - Polar Surprised
  • workshop_hana - Hana
  • workshop_dovetailed - Dovetailed
  • workshop_cosmic_calamity - Cosmic Calamity
  • workshop_hazard_warning - Hazard Warning
  • workshop_mosaic - Mosaic
  • workshop_jazzy - Jazzy
  • workshop_alien_tech - Alien Tech
  • workshop_damascus_mahogany - Damascus and Mohogany
  • workshop_queen - Skull Study
  • workshop_haunted_ghosts - Haunted Ghosts
  • workshop_spectral_shimmered - Spectral Shimmered
  • workshop_spirit_of_halloween - Spirit of Halloween
  • workshop_horror_holiday - Horror Holiday
  • workshop_totally_boned - Totally Boned
  • workshop_electroshocked - Electroshocked
  • workshop_ghost_town - Ghost Town
  • workshop_tumor_toasted - Tumor Toasted
  • workshop_calavera_canvas - Calavera Canvas
  • workshop_snow_covered - Snow Covered
  • workshop_frost_ornamented - Frost Ornamented
  • workshop_smissmas_village - Smissmas Village
  • workshop_igloo - Igloo
  • workshop_seriously_snowed - Seriously Snowed
  • workshop_smissmas_camo - Smissmas Camo
  • workshop_sleighin_style - Sleighin' Style
  • workshop_alpine - Alpine
  • workshop_gift_wrapped - Gift Wrapped
  • workshop_winterland_wrapped - Winterland Wrapped
  • workshop_helldriver - Helldriver
  • workshop_organically_hellraised - Organ-ically Hellraised
  • workshop_spectrum_splattered - Spectrum Splattered
  • workshop_candy_coated - Candy Coated
  • workshop_pumpkin_pied - Pumpkin Pied
  • workshop_sweet_toothed - Sweet Toothed
  • workshop_crawlspace_critters - Crawlspace Critters
  • workshop_portal_plastered - Portal Plastered
  • workshop_death_deluxe - Death Deluxe
  • workshop_raving_dead - Raving Dead
  • workshop_eyestalker - Eyestalker
  • workshop_spiders_cluster - Spiders Cluster
  • workshop_gourdy_green - Gourdy Green
  • workshop_mummified_mimic - Mummified Mimic
  • workshop_spider_season - Spider Season
  • workshop_gingerbread_winner - Gingerbread Winner
  • workshop_saccharine_striped - Saccharine Striped
  • workshop_elfin_enamel - Elfin Enamel
  • workshop_peppermint_swirl - Peppermint Swirl
  • workshop_snow_globalization - Snow Globalization
  • workshop_gifting_manns_wrapping_paper - Gifting Mann's Wrapping Paper
  • workshop_snowflake_swirled - Snowflake Swirled
  • workshop_smissmas_spycrabs - Smissmas Spycrabs
  • workshop_frozen_aurora - Frozen Aurora
  • workshop_starlight_serenity - Starlight Serenity
  • workshop_frosty_delivery - Frosty Delivery
  • workshop_glacial_glazed - Glacial Glazed
  • workshop_cookie_fortress - Cookie Fortress
  • workshop_sarsparilla_sprayed - Sarsaparilla Sprayed
  • workshop_swashbuckled - Swashbuckled
  • workshop_skull_cracked - Skull Cracked
  • workshop_misfortunate - Misfortunate
  • workshop_neonween - Neon-ween
  • workshop_simple_spirits - Simple Spirits
  • workshop_broken_bones - Broken Bones
  • workshop_potent_poison - Potent Poison
  • workshop_searing_souls - Searing Souls
  • workshop_party_phantoms - Party Phantoms
  • workshop_polterguised - Polter-Guised
  • workshop_kiln_conquer - Kiln and Conquer
  • workshop_necromanced - Necromanced
  • yeti - Yeti Coated
  • yetipark - Park Pigmented
  • Mannanas - Mananna Peeled
  • tropical_bird - Macaw Masked
  • saxton - Sax Waxed
  • hawaiian_shirt - Anodized Aloha
  • island_plants - Bamboo Brushed
  • mannco_animal_trophy_stripe - Tiger Buffed
  • crocodile - Croc Dusted
  • mannapples - Piña Polished
  • mannco_animal_trophy_spot - Leopard Printed
  • dragons_fury_dragon - Dragon Slayer
  • stocking_stuffer2017 - Smissmas Sweater
Modifying War Paint Code - What You Need to Know
This is where everything discussed previously gets put into practice. I will be going over the creation of a few weapons on a War Paint in this section to give a visual example of how to manually map and generate your War Paints yourself for your workshop submissions.

Again, due to proto_defs.vpd being .sig locked, you cannot modify it directly to see your changes. The only way around this is to manually construct your own War Paint, weapon by weapon.

I will be using Valve-made textures and stickers for each section of this, and it will go over the following subjects:

  • Part 1 - What does "inherit" do?
  • Part 2 - Enabling or disabling albedo tinting
  • Part 3 - Changing texture scale
  • Part 4 - Basic remapping
  • Part 5 - Properly visualizing multiply layers
  • Part 6 - Changing sticker locations on War Paints
  • Part 7 - Rotation and U/V locking
  • Part 8 - Bare metal exposed on War Paints
  • Part 9 - Turning an unused operation_template into a War Paint template
  • Part 10 - What isn't possible with War Paints?
  • Part 11 - Stuff you can do without a custom operation_template
Part 1 - What Does "inherit" Do?
Within what I refer to as the "master" variables for a War Paint (the variables near the very top of every War Paint with the prefix of "name" instead of "variable"), certain variables have "inherit" directly underneath the variable. When "inherit" is set to false, that means that variable is set the same for each individual weapon.


This is primarily used for texture_layer_x and certain sub-variables to make sure that these things are consistent across every weapon.

If you want to, for instance, make it so "texture_layer_1_rotate" is different for different weapons, you would have to change " "inherit": false, " to " "inherit": true, ". Then you would have to manually add " "variable": texture_layer_1_rotate " to every single weapon, and set it to the appropriate angle.
You need to make sure to change "name" to "variable" and "value" to "string" for weapon variables!

Part 2 - Enabling or Disabling Albedo Tinting
To enable albedo tinting within the code, you must set a "material_override". The file location for all of these VMTs are models/paintkits/macaw/c_*Model Filename*. You have to set these for each individual weapon, right below "data": and right above "variable":


This can be set freely on any War Paint base, as it does not require a special operation_template to work.

To visualize how this looks before you modify the code, or just for your promos, the Master War Paint folder will have 2 sets of VMTs for every weapon: "albedo_tinted_weapons_for_war_paints" and "remove_albedo_tinting_on_macaw_reskins". These are used to visualize how every weapon will look with or without albedo tinting. You will have to decide on which looks best for your War Paint.

Here is a visual example of the difference between albedo tinting on and off on the Neo Tokyo Scattergun:

Part 3 - Changing Texture Scale (Easy)
There are 2 primary ways of visualizing War Paint texture scale. This section will go over the simpler of the two options to adjust texture scale.

Using beta.loadout.tf's War Paint variable editor (which you can enable by pressing Shift + W), you can mess with scale_u and scale_v to figure out exactly what scale you need for for your War Paint.

For a quick example I will visualize a scale change for the wood texture on the Bovine Blazemaker Mk.II Minigun:

After you get the scale you want, you will want to export this as a VTF, renaming the file to the correct filename for the weapon of your choice (for this example, it would be c_minigun) and then placing the newly created VTF in the folder for the weapon to see how it looks in-game. This will replace the default Minigun texture with a War Painted version of that weapon.

If you enjoy how the scale looks, great! You can go straight to editing the scale for the texture in your War Paint template you're supplying with your submission.

To do that, figure out what texture_layer_x your texture is bound to (for Bovine Blazemaker Mk.II, this is texture_layer_2). Then modify the texture_layer_x_scale_uv from its original scale, to the scale you want.

While this way of modifying texture scale is very easy, it has some drawbacks. Primarily, loadout.tf does not have a way of modifying WHERE textures are bound, so you're limited to already existing War Paint templates. Along with that, it tends to export textures in the wrong VTF format, forcing you to re-import the texture to work properly in-game. This might end up being fixed in a future update to loadout.tf, however.
Part 4 - Basic Remapping, Changing Texture Scale (Advanced)
In this section, I will be going over the very basics of remapping individual weapons and manually adjusting texture scale, along with how to calculate what scale your texture has been changed to.

Let's say that the War Paint template you're using as a baseline is mapped almost exactly how you wanted it to look. The only problem is that it looks a bit weird on a handful of weapons, and the texture scale is a bit too large across the board. Instead of switching War Paint bases, you can just modify the War Paint code itself to fix these issues!

For this example, I'll be using "Backwoods Boomstick Mk.II" as my War Paint base, and the SMG as the weapon being modified.

The first step would be to grab the code for the War Paint base you're using. If you know the textures that are used for the base, you can easily just search the filename (or refer to the internal names for all War Paints as written above) within proto_defs to find your template.

You will then want to copy this over to its own text document. If you're using Notepad++ (which I highly recommend), you can click the little "-" button directly above the "header" line to minimize the entire section of code, allowing you to easily copy-paste this into its own Notepad++ document.


Then, you will want to open up the "p_smg_groups" (or whatever groups you want to use), and "p_smg_ao" textures from the "Master War Paint Folder" into your image editing program of choice, and the textures you're using on your War Paint. Be sure to set the blending mode on p_smg_ao to "Multiply" to have the albedo or groups texture visible. It might also be worthwhile to enable an all-black alpha channel, that way you can see what you're working on.




While this isn't directly necessary for the guide, if you're having trouble figuring out where things are mapped using the "Groups" texture, you can temporarily change the individual shades of red to different colors so you can see where they are mapped. Keep in mind that you're selecting all of that same shade of red, because that color can be mapped to multiple sections. Save your color modified version of "Groups" as a .tga, then import it into VTFEdit and save it as "c_smg.vtf" and place it in the c_models folder for the SMG (materials/models/weapons/c_models/c_smg). You can see what the model looks like by visualizing any War Paint, selecting the SMG, then changing the War Paint to "None" afterwards. It should look roughly like this:


If the texture didn't update, open up the developer console, set sv_cheats to 1, then run the command "mat_reloadmaterial c_smg" to update the texture for the SMG.



Before you start getting into texture mapping itself, you might first want to check what scale you want for your textures.

Texture scale is based off of the number of times it tiles within a 2048x2048 space.

You can easily calculate your texture scale by dividing 2048 by one side of your current texture's resolution within the canvas. For example, a texture with the resolution of 512x512 would be a "4.0" scale, because 2048 divided by 512 equals 4. Note that any scale that isn't a factor of 2048 will have the resulting resolution rounded to the nearest whole pixel, which should be negligible.

Now that you have the scale you want for your textures, now you can get to mapping your weapon. For this example, my texture 1 has a scale of "3.0" and my texture 2 has a scale of "2.0".

Place your texture 1 and texture 2 within your photo editor of choice, with texture 2 being a higher layer than texture 1. Then on the groups layer use the Magic Wand tool to select the different shades of red you want texture 2 to be bound to. Invert your selection (ctrl+shift+i by default in Photoshop) then "delete" that layer selection from texture 2's layer. This will reveal your Texture 1. To get it in-game, follow the same guide as written above in the optional section of remapping.





This might take a few attempts to get just right, but once you have it done, you're now ready to modify your version of your War Paint template.

Within your War Paint template, you will want to search for the internal name for the weapon you're using ('smg' for the SMG in this example) and make sure that "weapon_groups", "texture_layer_1_scale_uv", and "texture_layer_2_uv" are set correctly. Then go back to your photo editor - You will want to hide the ao, texture 1, and texture 2 layer, leaving only the groups texture visible. Anything that texture 2 was overlapping, use the color picker tool on those shades of red and then update "texture_layer_2_select_y" with the updated shades of red. If there are too many "texture_layer_2_select_y"s for how many you ended up using, just set them to "0", as 0 is not used on any Groups texture.

Now, all you have to do is save it, and now your code is modified for the SMG. You can now include your new modified War Paint code with your Workshop submission. Be sure to save it as a .json file.

While this example was specific to the Backwoods Boomstick Mk.II SMG, this can translate over to all weapons and all War Paint templates. Keep in mind you may need to do more textures depending on what template you're using.
Part 5 - Properly Visualizing Multiply Layers
Let's say you're remapping a War Paint that utilizes a multiply layer (Macaw Masked, Leopard Printed, Tiger Buffed, or making something entirely new). You want to make sure that you are visualizing the multiply layer correctly for your newly remapped weapons.

To figure out how much of the multiply texture is applied over your texture, you need to check the scaling options within your War Paint template.

Within this example, I will be using Macaw Masked.
Its multiply layer has a scale of "0.1". If you think of "1.0" as "100%", this scale of "0.1" would then be considered 10%. Because "1.0" is equal to 2048, 10% of that would be 204.8. But after rounding to the nearest whole, it would be 205. Meaning an area of 205x205 pixels of the gradient will be stretched to fit the entire canvas, then multiplied over the texture as designated by "operation_template".

Be sure to do texture scale first, because if you don't, your gradient will not be applied properly. The gradient goes after the scaling operation.
Part 6 - Changing Sticker Locations on War Paints
Stickers on war paints seem mildly complex at a glance. Taking a quick peek at the code for Yeti Coated or Park Pigmented, you might see this random string of numbers and not really understand how exactly it works. It's not actually all that bad.

The variables ending in dest_tl, dest_tr, and dest_bl use a simple mathematical equation that translates to the x,y coordinates of that pixel location. That equation is x-axis divided by 2048, then y-axis divided by 2048. Please note, that the top left, top right, and bottom left are relative to the sticker orientation.


While the method of finding the coordinates for your sticker differ per program, in Photoshop if you press F8, wherever your cursor is placed tells you that pixel's x,y location. The only caveat here is most photo editors start the pixel grid at 0,0 while TF2 starts it at 1,1. So you need to add +1 to each of your coordinates; otherwise, it will be off by a pixel, though a 1 pixel difference is negligible.

For this example, I am going to be adding in a second eye sticker to the Yeti Coated Claidheamh Mor.

Because I am just going to be adding in a sticker to a pre-existing template, I am going to be grabbing the texture from beta.loadout.tf (specifically, the version that is before the AO texture application, so I can apply it afterwards to make the lighting right.)


Applying a sticker to the canvas is easy: You just need to make sure that you either have a background on the sticker, or put a background underneath the sticker so you can easily tell where the top left, top right, and bottom left coordinates are. This is easy with the Yeti Coated stickers because they already have a background.



Once you get the sticker location exactly where you want, now you have to modify the code for the sticker to the new location. Because you need to write down 3 sets of coordinates, its best to write them down in another text document before directly putting them into the War Paint template. We will need to convert both x and y locations using the math equation above.

It should look a little something like this:

Before you update your code, make sure that your sticker coordinates are accurate using beta.loadout.tf's War Paint variable editor. The War Paint editor rounds to the nearest hundredths place, but it should be accurate enough to make sure it's roughly in the right place.
Note: The War Paint variable editor on loadout.tf puts the sticker variables in the order of bottom left, top left, top right instead of top left, top right, bottom left. Make sure you're putting them in the correct order.

Then, after you are happy with your new sticker location, update your war paint code with your new top left, top right, and bottom left variables, submitting your new modified proto_defs with your Workshop Submission.
It's also a good idea to just include your raw coordinate locations in a separate notepad document with your submission, just in case Valve has any issues with your code.
Part 7 - Per weapon rotation and U/V locking
Rotation and U/V locking work relatively similarly.

Locking the rotation means that the texture will not leave the specified orientation as dictated by your War Paint template, and U/V locking refers to locking the texture from moving horizontally or vertically. This can be set universally using the "master variables", or set on a per-weapon basis.

Most War Paint templates set a universal rotation of "0 360" and a universal "translate_u" and "translate_v" of "0 1", meaning that it can rotate freely a full 360 degrees, and move left and up freely.
This is true unless the texture scale is less than 1.0. If your texture scale is less than 1.0, then you must increase the maximum u/v until when multiplied by the texture scale so it equals 1.0 or greater.

If you want to add a rotation lock, or a U/V lock to individual weapons of your War Paint, you're need to convert the "master variable" into a normal variable, and put that variable into every weapon on your War Paint template. For this example, I am going to be converting "texture_layer_1_rotate", "texture_layer_1_translate_u", and "texture_layer_1_translate_v" from universal variables to individual variables.

The first step is very easy; you will want to change "inherit" on "texture_layer_1_rotate", "texture_layer_1_translate_u", and "texture_layer_1_translate_v" from "false," to "true,".


You can then copy the master variables over to each individual weapon, changing "name" to "variable", "value" to "string", and removing the "inherit" line entirely. You now need to do this for every single weapon, placing them in the same order as they appear in the master variable section.


After you have placed the variable on every weapon, then you can start modifying it.
Rotation locking is very simple: Simply enter a single number between 0 and 360 corresponding to the single rotation angle you want for that texture. For example, if you want it to always be straight with no rotation whatsoever; you would enter "0", "90", "180", or "270" depending on what you need your texture to face, this will be different per weapon.

For example, both the Sniper Rifle and Pistol have the exact same texture rotation of "0":

To have them be consistent with each other, you would want to rotate the Sniper Rifle or Pistol to "90", instead of "0". This might take some time to fully figure out for each of the 45 War Paintable items (including the War Paint item itself).

Unfortunately, U/V locking is a tiny bit more complicated in nature due to how many factors are in play for it.

Let's say you want to make something similar to High Roller's, where the roulette wheel is locked from moving left/right from one section of a weapon, but not vertically.

The first thing you need to do is get the rotation locked and scaled correctly. For this example I am going to be using the Crusader's Crossbow, and locking the roulette wheel from horizontally out of of the center of the cylinder, but not stop it from scrolling vertically.

The first thing you need to do before trying to lock translate_u or translate_v in this situation is to make sure the scale is correct. In this case, a texture resolution of 506x506 or a scale of 4.047 would be the perfect size in this specific situation.
Once the scale is correct, then you need to tile your pattern until it fills the entire canvas. Do not worry if your texture goes beyond the edge of the canvas. So long as your texture loops seamlessly, it will not cause any issues in the actual game.

Now comes the offsetting. Depending on how your texture is oriented, you will need to offset your texture to the left, or up until the texture is exactly where you want it to be placed. This will be a lot of trial and error, but don't get too frustrated if it takes a while; this is a very tedious process. If you're having trouble trying to figure out exactly where your texture needs to line up, bring the "p_*weapon*_albedo" texture into your photo editor, underneath your AO and texture you are working with as a visual guide.

Now, once you have your texture offset exactly how you want it to be, you will then take the number of pixels you offset it to the left, or up, and then divide that by 2048.

In this example, I had to offset the texture to the left by 500 pixels. If I then divide that number by 2048, I get 0.2441 (rounded to the nearest ten thousandths). Because I had offset this to the left, "texture_layer_1_translate_u"s "string" goes from "0 1" to "0.2441 0.2441". Unlike rotation, you have to set this to two numbers, even if they're the same. Again, I must reiterate that you must make sure your scale is correct; if your scale is not set correctly then your u/v lock will not be correct. it's very scale dependent.




Be sure to make regular saves if you are doing every single weapon this way, and double check for typos periodically because you cannot import your code directly into the game to test it.
Part 8 - Bare Metal Exposed on War Paints
Within proto_defs, there are a handful of War Paint templates that have no "texture_layer_1" variable. Because "texture_layer_1" has that special property of defaulting to whatever is not used by texture_layer_2 onward, it will instead display whatever the "weapon_albedo" texture is set to.

You cannot just remove texture_layer_1 from whatever existing template you're using; you must use an operation template that has "over_albedo" within the name. You can find this in the list of all operation templates section.

If you are remapping a War Paint and want to have bare metal exposed, you are going to need to use the p_*weapon*_albedo texture along with the textures you are using for your War Paint template. This works the exact same was as manual remapping of the textures, outside of the lack of texture_layer_1.
Part 9 - Turning an Unused Operation Template into a War Paint Template
You should follow this section at your own risk. While some of the unused templates are probably totally okay to use, some of these have completely untested variables on them and might not work as expected.

Let's say you want to make a War Paint using one of the many unused operation templates. While time consuming to do, it's really not all that hard to convert.
For this example, I'm going to be converting 811, "three_sticker_four_texture" from an operation template into a War Paint template.

Starting off, you can copy over everything found within the "variables" section of the operation template. To easily select everything you can press the "-" button in Notepad++ that is located directly next to the word "variables". Include this line, along with the bracket at the very end. Copy this over to its own notepad document.

After this, take any pre-existing War Paint template and copy it in its entirety to its own notepad document. Which one doesn't matter, but in this example I am using Bovine Blazemaker Mk.II.

To make these easier to modify, you can quickly save them as .json files to add on the "-" buttons.

Copy over the variables from the unused operation template into your War Paint template, updating the defindex of "operation_template" to the index of the template you are using, along with updating any "inherit" settings as you need. This entire section will work similarly to how you added per-weapon rotation and u/v locking, but with every variable after "2_wearblend_adjust_gamma" being copied over per weapon. This will be relatively time consuming to do.

You can speed it up if you modify your copy of the master variables from the operation template, removing all variables up to "2_wearblend_adjust_gamma", but keep the "variables" line intact. You would then modify this set of variables to remove any lines with inherit set to "false", and then do the same thing that you did in Part 7 where you changed "name" to "variable", and "value" to "string." You will also want to add a single 'tab space' (by pressing tab) to the beginning of every line. With this, you now have a copy of the variables you can now copy into every weapon.


You can press the "-" button for that section, and then copy this entire set of variables to every single weapon very efficiently by doing the same to the weapons set of variables. For this section do not include the bracket at the very end.



Here is a gif showing how to copy the variables over for each weapon:


The only downside to this method is that you need to manually update "weapon_albedo", "weapon_wearblend", and "weapon_groups" for every weapon, along with your texture and sticker placements. But you should really only be doing this before you start modifying the code itself.
Part 10 - What isn't Possible with War Paints?
This section is for things that are currently not possible to do with War Paints, and things that I'm unsure of.

This will be updated as time goes on, so feel free to ask questions if you want to know what is and isn't possible!

Not possible:

Team colored stickers
Within proto_defs, there is absolutely no functionality for team colored stickers. There are plenty of War Paints that are both team colored and have stickers, but Valve did not program in a _blue variant to the sticker variables.

Not sure if it's possible:

Team colored inner wear
There are no War Paints internally that have both team colored textures and inner wear textures. As far as I'm aware, you can totally have team colored inner wear. But it's better to be safe than sorry and also include a team neutral inner wear option for your War Paints.
Part 11 - Stuff You Can Do Without a Custom Operation Template
This section will cover a few small things that you can do by modifying the War Paint template itself in some way that doesn't require its own operation template to be created. This will be updated over time to add in more things you can do as they're discovered.

Makeshift Inner Wear

Within the master variables, if you add in "inherit": false, to "weapon_albedo", you can set some sort of makeshift inner wear texture for your War Paint. The only downside is that you will only have a single inner wear texture to work with.

Note that you will have to remove the "weapon_albedo" code from each individual weapon after you do this.

Adding in Custom Blood

Similar to makeshift inner wear, you can do the same to the "texture_blood" variable, allowing you to have your own custom blood texture for your War Paint.
Bugs n' Glitches with War Paints
Tiger Buffed / Leopard Printed Broken Gradients

Tiger Buffed and Leopard Printed both utilize the exact same War Paint template and currently have a bug where it does not select anything past the top-left 1075x1075 area for its gradient mapping. For promotional materials, you should condense your gradient down to that location.

You can fix this yourself with your coding tweaks by changing multiply_layer_translate_u and multiply_layer_translate_v from a value of "0 20" to "0 40", making it select from the whole texture again.

Autumn Mk.II / Spirit of Halloween Broken Gradients

Similar to the Tiger Buffed/Leopard Printed bug, the gradient texture for Autumn Mk.II and Spirit of Halloween don't select from the entire canvas. With the texture scale being 0.2, if you want it to maintain the same texture scaling size, you need to change texture_layer_3_translate_u and texture_layer_3_translate_v from "0 1" to "0 5" for it to select from the whole canvas again. Alternatively, you can increase the texture scale from 0.2 to 1.0 or higher.

Albedo Tinted Bazaar Bargain

You might notice that when you apply Saccharine Striped or Elfin Enamel to the Bazaar Bargain, the scope breaks. Due to how the scope and the weapon themselves have two completely unique VMTs, when a "material_override" is enabled for the Bazaar Bargain to enable albedo tinting, it also breaks the scope. You cannot do anything to fix this to my knowledge, so if you're doing albedo tinting be sure to either disable it on the Bazaar Bargain or just let the scope be broken.




Flipped Eyes on Yeti Coated/Necromanced Ubersaw

More of an oversight if anything, but the eyes on the Yeti Coated/Necromanced Ubersaw are facing the wrong way. The easiest way to fix this is to swap the _l and _r stickers.
A Request to Valve
Valve,

I really love the concept of War Paints, but they were not handled very well. Forcing us creators to use only templates you created and not giving us any wiggle room with things like modifying variables or texture scale is really annoying. I don't expect the ability to make our own operation template, but I would really like to have the ability to change how my textures are scaled and bound without having to manually generate my own UVs for every individual weapon, because it's such a time sink.

Please, consider going back to something similar to paintkits_master but for War Paints. That way we can at least make basic modifications to our War Paints.

Thank you,

- Renim
38 Comments
Saffron Jun 9, 2023 @ 9:47am 
oh uh is it possible that you give me the link of "that" through dms?
Renim ☕  [author] Jun 9, 2023 @ 8:36am 
in-game without cheating however, you cannot. a few of us use a hack that allows us to visualize code
Renim ☕  [author] Jun 9, 2023 @ 8:34am 
the only way to do so would be to merge an existing war paint template with a stickered template, moving the mapping data from night owl mk.ii to like. yeti coated
Saffron Jun 9, 2023 @ 5:38am 
please correct me if im wrong xd
Saffron Jun 9, 2023 @ 5:37am 
i've read the guide and saw nothing on how to put stickers on a warpaint with no sticker base for example (night owl MKII), is it possible to somehow put stickers on a warpaint with not base sticker?
Zeugziumy Mar 30, 2023 @ 9:14am 
Sounds good, that would be very useful, thanks! I know loadout DOT tf exports the War Painted texture as VTF version 7.5 (or something), and I also know that the VTF version can be found via HxD. I'd have to check if it also uses VTF 7.5 on in-game generated war paints.

Something I just noticed is that, on mat_texture_list 1, it does show the amount of kb that the texture has. I tried out a Smissmass Sweater Black Box on Very High quality texture settings, and it shows:

Size(5,461Kb) Dimensions(2048x2048) Format(DXT5_RUNTIME)
1,365Kb @ lower mip

I guess we can use that for knowing the specific bytes date to extract from the memory dump. I perhaps should try again sometime.
Gadget Mar 30, 2023 @ 2:17am 
It's not only determined by the resolution but also the compression type, whether it has mipmaps (and how many) and whether it has additional resources. You can check out the C++ source code of vtflib on Github to get an idea. I've already used that to make a C# project that can read and write vtfs. I can also read all war paints, definitions and files from the game. Now the other major part that I have to do is generate the final vtf from all of that. I might continue working on it after the summer update is out. Until then I have other projects to work on. Trying to read it from a memory dump is an interesting idea, though.
Zeugziumy Mar 29, 2023 @ 6:26pm 
From my initial tests, it seems the VTF filesize is determined by the resolution size of a texture, but I'm not 100% sure. Knowing this, it may be possible to extract .vtf files with predetermined byte sizes. "mat_texture_list 1" DOES show the resolution size of a war painted weapon, meaning it might be possible to make the script copy specific amount of bytes to recreate the .vtf file.

It would create a bunch of corrupted .vtf files, though along the list, the war painted texture should be there. Using something like the VTF Shells Extension to view VTF thumbnails inside the File Explorer would help this a ton, as it wouldn't require to view each .vtf one by one.

The .vmt files for certain war paints are stored in the game's .vpk, so the material files are not a problem.
Zeugziumy Mar 29, 2023 @ 6:18pm 
I think the idea of making a script to find files using the VTF header might be possible, because I was able to get VTFs out of the process' fulldump, proper VTFs, actually. Like a small Counter Strike icon that is probably hardcoded into the engine, and a small server browser icon.

It would extract basically everything, but along the lines, the war painted texture should be alongside everything else. Viewing a War Paint from the little viewer inside the Inventory system, then doing the fulldump, then closing the game and running the script to analyze the fulldump file.

Process Explorer can create a fulldump. I know that I used a similar thing to find portraits in Heroes of the Storm, something that was largely unknown before. Basically a cygwin bat script that would look up for the DDS header on a cache folder that had extensionless files.

The only thing here is that the script would require to know how much of the VTF's file size to recreate as a new file.
Renim ☕  [author] Mar 29, 2023 @ 6:11pm 
The only way to do so would be to either manually construct it, or use loadout.tfs war paint thing sadly