Half-Life Lambda logo Half-Life Materials System


Half-Life, from Valve Software, is a first-person shooter based on a heavily modified version of the Quake engine. One of the many improvements to the engine was a primitive "materials" system: certain textures were assigned to a material type (e.g. dirt, metal, glass), and then the engine would treat these surfaces specially with added effects (different footsteps, sounds when shot, etc).

Several tutorials exist online for how to use the system, but I found them not comprehensive enough, and also there were many untested edge cases that could arise in the system. This page contains details on how to use the system, descriptions of the quirks and how mappers can use them. Finally there is a gallery of all available "materials" in the base Half-Life game, the textures that map to the materials, and example sounds for each material type.


Contents


Basic Description

The system works by using a single file, "valve/sound/materials.txt" located within the main Half-Life install folder. Within the file is a series of lines that assign texture names to the first letter of their materials type, for example:

// Half-Life Texture Types.  Modify this file only if texture names are changed!
//  'M' metal, 'V' ventillation, 'D' dirt, 'S' slosh liquid 
//  'T' tile, 'G' grate (Concrete is the default), 'W' wood, 'P' computer, 'Y' glass

V SILO2_COR
D OUT_GRVL1
M SILO2_P2

would cause any surface with SILO_COR2 to use "V"ent sound effects, OUT_GRVL1 is "D"irt and SILO2_P2 is "M"etal. Any texture NOT covered by this file receives the default "concrete" sounds instead. (This includes the tool textures like NULL, CLIP, AAATRIGGER etc).

Unfortunately, this file is global, and applies to ALL texture names used across ALL maps for a given mod. It also cannot be overridden with custom, map-specific entries.

There are a few ways around this problem:

Back to Top

Quirks

Some quirks of the system can be exploited by mappers to make adding new textures easier. Note that all HL textures have a 15-character name limit, so when using one of these methods, be sure the new texture name still fits.

12-Character Limit

The header of materials.txt includes this information:
// NOTE: only the first 12 characters of the texture name are used

The comment is interesting for mappers, because it means that it is possible to add many more custom textures without having to overwrite existing ones: by using an existing 12-character material in the list, and using that name as a prefix for your new texture, the first 12 characters will match.

For example, "CRETE2_FLR03" is listed as a "Grate" type texture that is 12 characters long. If you create a custom texture, called CRETE2_FLR03_NU, it will still sound like a Grate because the first 12 characters match. However, "ELEV1_FLR" is also listed, but cannot be used for this purpose: if you create "ELEV1_FLR_MyFlr", only the first 8 characters match.

For this reason 12+ character names in the materials file are "special" and will be highlighted in the Gallery below. This quirk is actually used by some stock HL textures as well to provide material sounds to several textures with one entry, e.g. DRKMTLT_WALL)

Prefix Characters

When the engine compares materials.txt to a map texture, it first removes "texture prefix" characters from the map texture. Specifically, it removes a leading +0 or -0 (or any other digit or letter), then can also remove one of !, ~, {, or space, before comparing. In practice, this means that an entry like
G CHAIN_LINK
will match all of the following texture names:
+0CHAIN_LINK
-2CHAIN_LINK
+A~CHAIN_LINK
~CHAIN_LINK
+1{CHAIN_LINK
Like the 12-character limit (above), this can be exploited to add new textures without replacing existing ones, by prepending an unused prefix. For example, to add another wood texture, you could name your texture "-1OUT_WD". This matches OUT_WD from materials.txt but leaves OUT_WD from halflife.wad available as well.

Some prefixes have special meaning, so it is probably best to avoid ! ("liquid"), -0 ("randomly tiled") and { ("semi-transparent").

Back to Top

Technical Details

The material handling code (which types are available, and what do they do?) is entirely hard-coded in a couple of places in the Half-Life DLL files. Mod makers can change this to add new material types or alter the sounds played, but without recompiling the only control is provided through materials.txt. Here are relevant technical details based on code from the Half-Life SDK, which covers basic Half-Life and HLDM (as well as Ricochet and Deathmatch Classic). Other mods may have their own rules.

Footsteps

The first part of material handling changes the player footstep sound based on the material they are on. This is stored in Single-Player Source, "pm_shared/pm_shared.c" (the "player movement" shared code). Arrays of 512 entries store mappings of 12-character texture names to a texture type, based on this list:

#define CHAR_TEX_CONCRETE  'C'                     // texture types
#define CHAR_TEX_METAL          'M'
#define CHAR_TEX_DIRT           'D'
#define CHAR_TEX_VENT           'V'
#define CHAR_TEX_GRATE          'G'
#define CHAR_TEX_TILE           'T'
#define CHAR_TEX_SLOSH          'S'
#define CHAR_TEX_WOOD           'W'
#define CHAR_TEX_COMPUTER       'P'
#define CHAR_TEX_GLASS          'Y'
#define CHAR_TEX_FLESH          'F'

When choosing a footstep sound to play, the texture under the player is checked against the list to find a type. Player footsteps alternate left/right and play different sounds for each foot. The material sound to use is determined in PM_UpdateStepSound() (line 491), rules being:

The actual play routine is at line 286, PM_PlayStepSound(). The determined step type is combined with the left/right foot choice and used to play alternating steps. One interesting trivia is that "Tile" steps have an extra sample for the right foot.

Weapon Noises

Part of the system code is visible in the Half-Life SDK, Single-Player Source, file "sound.cpp", beginning from line 1468. The code demonstrates loading of materials.txt and then using the values to return sounds or effects based on the material hit. File loading code begins at TEXTURETYPE_Init() (line 1535), where up to 512 textures are read from materials.txt and stored. Note string termination at the 13th char, and later "strnicmp" in TEXTURETYPE_Find() limited to a 12-character match.

TEXTURETYPE_PlaySound (line 1622) returns the specific sounds to play based on the texture type hit. When performing world-texture lookup, leading -0, +A etc are stripped, then leading special characters {, ~, ! and space are removed. Assuming a successful lookup, this code block determines the actual sound effects played:

   switch (chTextureType)
        {
        default:
        case CHAR_TEX_CONCRETE: fvol = 0.9;     fvolbar = 0.6;
                rgsz[0] = "player/pl_step1.wav";
                rgsz[1] = "player/pl_step2.wav";
                cnt = 2;
                break;
        case CHAR_TEX_METAL: fvol = 0.9; fvolbar = 0.3;
                rgsz[0] = "player/pl_metal1.wav";
                rgsz[1] = "player/pl_metal2.wav";
                cnt = 2;
                break;
        case CHAR_TEX_DIRT:     fvol = 0.9; fvolbar = 0.1;
                rgsz[0] = "player/pl_dirt1.wav";
                rgsz[1] = "player/pl_dirt2.wav";
                rgsz[2] = "player/pl_dirt3.wav";
                cnt = 3;
                break;
        case CHAR_TEX_VENT:     fvol = 0.5; fvolbar = 0.3;
                rgsz[0] = "player/pl_duct1.wav";
                rgsz[1] = "player/pl_duct1.wav";
                cnt = 2;
                break;
        case CHAR_TEX_GRATE: fvol = 0.9; fvolbar = 0.5;
                rgsz[0] = "player/pl_grate1.wav";
                rgsz[1] = "player/pl_grate4.wav";
                cnt = 2;
                break;
        case CHAR_TEX_TILE:     fvol = 0.8; fvolbar = 0.2;
                rgsz[0] = "player/pl_tile1.wav";
                rgsz[1] = "player/pl_tile3.wav";
                rgsz[2] = "player/pl_tile2.wav";
                rgsz[3] = "player/pl_tile4.wav";
                cnt = 4;
                break;
        case CHAR_TEX_SLOSH: fvol = 0.9; fvolbar = 0.0;
                rgsz[0] = "player/pl_slosh1.wav";
                rgsz[1] = "player/pl_slosh3.wav";
                rgsz[2] = "player/pl_slosh2.wav";
                rgsz[3] = "player/pl_slosh4.wav";
                cnt = 4;
                break;
        case CHAR_TEX_WOOD: fvol = 0.9; fvolbar = 0.2;
                rgsz[0] = "debris/wood1.wav";
                rgsz[1] = "debris/wood2.wav";
                rgsz[2] = "debris/wood3.wav";
                cnt = 3;
                break;
        case CHAR_TEX_GLASS:
        case CHAR_TEX_COMPUTER:
                fvol = 0.8; fvolbar = 0.2;
                rgsz[0] = "debris/glass1.wav";
                rgsz[1] = "debris/glass2.wav";
                rgsz[2] = "debris/glass3.wav";
                cnt = 3;
                break;
        case CHAR_TEX_FLESH:
                if (iBulletType == BULLET_PLAYER_CROWBAR)
                        return 0.0; // crowbar already makes this sound
                fvol = 1.0;     fvolbar = 0.2;
                rgsz[0] = "weapons/bullet_hit1.wav";
                rgsz[1] = "weapons/bullet_hit2.wav";
                fattn = 1.0;
                cnt = 2;
                break;
        }

        // did we hit a breakable?

        if (pEntity && FClassnameIs(pEntity->pev, "func_breakable"))
        {
                // drop volumes, the object will already play a damaged sound
                fvol /= 1.5;
                fvolbar /= 2.0; 
        }
        else if (chTextureType == CHAR_TEX_COMPUTER)
        {
                // play random spark if computer

                if ( ptr->flFraction != 1.0 && RANDOM_LONG(0,1))
                {
                        UTIL_Sparks( ptr->vecEndPos );

                        float flVolume = RANDOM_FLOAT ( 0.7 , 1.0 );//random volume range
                        switch ( RANDOM_LONG(0,1) )
                        {
                                case 0: UTIL_EmitAmbientSound(ENT(0), ptr->vecEndPos, "buttons/spark5.wav",
                                    flVolume, ATTN_NORM, 0, 100); break;
                                case 1: UTIL_EmitAmbientSound(ENT(0), ptr->vecEndPos, "buttons/spark6.wav",
                                    flVolume, ATTN_NORM, 0, 100); break;
                                // case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark5.wav", flVolume, ATTN_NORM); break;
                                // case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark6.wav", flVolume, ATTN_NORM); break;
                        }
                }
        }

        // play material hit sound
        UTIL_EmitAmbientSound(ENT(0), ptr->vecEndPos, rgsz[RANDOM_LONG(0,cnt-1)], fvol, fattn, 0, 96 + RANDOM_LONG(0,0xf));
        //EMIT_SOUND_DYN( ENT(m_pPlayer->pev), CHAN_WEAPON, rgsz[RANDOM_LONG(0,cnt-1)], fvol, ATTN_NORM, 0, 96 + RANDOM_LONG(0,0xf));
                        
        return fvolbar;
}
A few interesting things to note here:

Back to Top


Materials Gallery

The following sections describe each material type in Half-Life, as well as the textures from the base four WAD files (halflife.wad, liquids.wad, xeno.wad, decals.wad) that match those material. Each section also includes short audio clips of the associated footsteps and weapon sounds.

Concrete

Type C
Sample footsteps
Sample hit sounds
Notes "Concrete" is the default sound and is used when a texture does not match any defined in materials.txt. It IS possible to assign a texture to concrete using material type "C" but this has no different effect than just leaving it undefined.

Back to Top

Flesh

Type F
Sample footsteps
Sample hit sounds
Notes No "flesh" materials are listed in materials.txt, so this material is unavailable to mappers. The hit sound does not play when using the crowbar.

Back to Top

Dirt

Type D
Sample footsteps
Sample hit sounds
Notes There are no 12-character Dirt texture names in materials.txt.

Back to Top

Grate

Type G
Sample footsteps
Sample hit sounds
Notes n/a

Back to Top

Metal

Type G
Sample footsteps
Sample hit sounds
Notes n/a

Back to Top

Computer / Electrical

Type P
Sample footsteps
Sample hit sounds
Sample spark sounds
Notes Computer material is exactly like Glass, except that it has a random chance to create a spark when hit. The step sound is Concrete.

Back to Top

Slosh Liquid

Type S
Sample footsteps
Sample hit sounds
Notes Slosh sounds are also used when a player is standing in a liquid (brush entity with contents other than "air") up to foot height. If the water is knee-height, "wade" sounds are played instead. Note that textures beginning with ! are listed here; however, the engine converts these to world-water, so they actually have no sound in-game. There are no 12-character Slosh texture names in materials.txt.

Back to Top

Tile

Type S
Sample footsteps
Sample hit sounds
Notes Tile sounds have an extra footstep noise (five, instead of four) - the "squeaky" rubber-on-tile sound.

Back to Top

Ventilation

Type V
Sample footsteps
Sample hit sounds
Notes There are no 12-character Vent texture names in materials.txt. Vent materials have only one "hit" sound.

Back to Top

Wood

Type V
Sample footsteps
Sample hit sounds
Notes The step sound is Concrete.

Back to Top

Glass

Type Y
Sample footsteps
Sample hit sounds
Notes The step sound is Concrete.

Back to Top


Return to the index.