Left 4 Dead 2

Left 4 Dead 2

32 ratings
Material Proxies for Modders : Part 3 : Programming [Abandonned]
By Ellie
This is the third part of the huge guide about material proxies that aims to let the Source Modding community know everything I've learned about material proxies and stuff. This part aims to present tools that are way more complex and powerful. To understand this part and use it, you need to have knowledge of programming.
   
Award
Favorite
Favorited
Unfavorite
Introduction
Hello and welcome to the third part of the huge guide about Material Proxies.
This part is mostly there for legacy, in this part, I'll explain in detail how to hard code with material proxies. It's possible to build entire programms using material proxies, that will be executed and that are able to change almost any parameter of the VMT depending on stuff.

A lot of stuff can be done knowing ONLY the "Input Proxies" and the first section of the guide (If ($x > $y) {$a = $b} else {$a = $c})

Basically, here are a breif summary of what the possible inputs, operators and outputs are :

Inputs (raw) :
  • Speed of the player you're controlling*
  • Speed of the model the material is applied to*
  • Angle of view between the player and the center of the model (Player view)*
  • Distance between the player and the center of the collision model*
  • Random number (different methods)
  • Time
  • Fact that the material is an entity or not*
  • Position along the X axis where the player is in the map
  • Fact that a survivor is covered in boomer bile*
  • Fact that an infected is on fire*
  • Number of kills with a melee weapons (only from 0 to 5)*
  • Team the player is in (infected / survivor / spectator)
  • Object ID*
(Things with a * will crash the game if used on a static object in the map)

Operators :
  • Anything "condition" related operator (Equals / Bigger Than / Smaller Than) as well as (AND / OR / NOR etc.)
  • Anything "time" related ("for the first time" / "since x seconds" / "for x seconds" etc.)
  • While loops
  • Force increment / decrement
Outputs :
  • Any VMT parameter that's an Integer (INT) or a String (STR), except the ones related to rimlight and phongexponent (possibilty to bypass through model stacking)
    This includes change of texture (with $frame of $detailframe) as well as bumpmaps, model (with the use of $alpha or $frame combined with $translucent and an all-black diffuse alpha) but with Glow Outline problem ($alpha taken into account)

This guide will TRY to teach you all that. Keep in mind that it's incredibly complex, but incredibly powerful. As I said, you need basics in programming to understand how that stuff works, you won't be able to learn anything if you are not able to write basic code in a least one programming language :P. As I said, the content of this guide is more for legacy, to "document" everything I ever did.

Are you ready ? Let's go :D
Input Proxies
The very first thing you need to know is how to store the "Inputs" we can work on in variables in a VMT Code.

To do so, you need to use the corresponding elements that you can find here.

To create an input proxy, you need to define a custom variable in order to store the value you'll get "somewhere" and you also need the desirated proxy, to retrieve the value you want.

Here's an example :
$prox "0" "Proxies" { PlayerProximity { scale "1" resultVar "$prox" } }
You can see with this bit of code that I declared a custom variable, called $prox, and that I added a proxy to retrieve the distance between the player and the object the material is applied to. At this point then, the variable $prox will constantly change (unless the player is static) and store the current distance between the player and the entity.

There is a lot of input proxies that grants you the possibility to do a lot of things. You can check the next section to see the full list for L4D2.
List of input proxies [1/2]
Here's the full list of useful input proxies you can use in L4D2, with examples and a description of "what" does every proxy do. Proxies code samples do not include the variables definition part and just show the raw proxy ;)

You can also find the Material Proxies List here.


Description : Continuously emits random numbers between user defined minVal and maxVal
Usage : Anything related to random generation
Notable particularities : The values that are stored in resultVar are not related to the model ID or any other thing, therefore, this proxy allows you to draw different random numbers for each VMT you use on a same model.
Notable glitches : Using this proxy, not matter how you buffer your code, can lead to "flickering" textures the first time the material is loaded in a map, until you look away from it.

UniformNoise { minVal "0" maxVal "100" resultVar "$rand" }
Examples of mods that use this input proxy :

Click here     Click here



Description : Returns a random number associated with the entity it's applied to (from 0.00001 to 0.999999). A embed multiplication (scale) is avaliable.
Usage :
Random number generation with a shared value between VMTs
Determination of the "Entity Type" the material is applied to
Notable particularities :
The value will be shared between the VMTs that are applied to a same model. It it therefore very useful to apply random skins that consist of multiple VMTs.
Will return 0.000000 if used on an object witch position is not determined server side and that is not static on a map (prop static).
Notable glitches : Will crash the game if used on a static prop (an object part of a map that cannot move in any way)
EntityRandom { scale "100" resultVar "$randone" }
Examples of mods that use this input proxy :

Click here     Click here



Description : Generates a sin(x) wave with the possibility to set the top peak value (sinemax), the bottom peak value (sinemin) and the time btween two high/low peaks (sineperiod)
Usage :
Anything related to blinking (lights / LEDs etc.) or light modulation
Cheap buffer for a control condition related programming block
Notable particularities : Since it's a sin(x) wave, the values it returns are not equally distributed along the curve. Values at the peak "stays" longer than values between the center of the poles. This means it can't be used to draw random numbers unless the result has to be very uneven.

Sine { sinemin "-10" sinemax "10" sineperiod "5" resultVar "$sinewave" }
Examples of mods that use this input proxy :

Click here     Click here



Description : Generates a timer (auto-increment function), rate as well as offset can be specified. The "rate" is the time it takes for the function to increment 1 unit. So if you put 0.02 in rate, it will take 50 seconds for the timer to go from 0 to 1, then 50sec from 1 to 2 etc.
Usage :
Anything time related
Generation of "looping" values (for RNG mostly).
Notable particularities : The timer is stored server-side. Joining a game mid map will result in the timer to not start at 0.

LinearRamp { rate "$timerate" initialValue "$one" resultVar "$ramp" }
Examples of mods that use this input proxy :

Click here     Click here


Description : Returns the speed of the local player (the character you're playing) in "units per second".
Usage :
Anything "speed" related
Can be used to differanciate the state of the player (walking / running / crouching / falling)
Can be used to differanciate "static" and "non-static" objects
Notable particularity : Crashes the game if used on objects that are static on a map

PlayerSpeed { scale "1" resultVar "$playerspeed" }
Example of mod that use this input proxy :

Click here (Anti-cheat that turns the toolbox invisible at noclip speed)
List of input proxies [2/2]

Description : Returns the distance in units between the local player and the "object origin" the material is applied to.
Usage : Anything distance related
Used as a "trigger" to artificially make the player "pick" items on the map when he's close to them
Used to differentiate world model and first person model (since first person model will never have values over 30/40 units, no matter the FOV)
Notable glitches : Crashes the game if used on objects that are static on a map

PlayerProximity { scale "1" resultVar "$playerprox" }
Examples of mods that use this input proxy :

Click here    Click here (Pure RNG)


Description : Returns a value between -1 and 1 corresponding to the angle of view between the player and the center of the entity the material is applied to (-1 = full looking away ; 0 = looking at a right angle ; 1 = full looking at it)
Usage : Anything view related
Used to know if the player is looking or not at the object, can be used for "spooky" effecty or to know when the player picked or looked at something
Notable glitches : Crashes the game if used on objects that are static on a map

Playerview { scale "100" resultVar "$view" }
Example of mods that use this input proxy :

Click here    Click here (Pure RNG)


Description : Returns the player position (x/y/z coordinates) related to the map origin
Usage :
Knowing if the player has moved a bit or not
Knowing if the player has spawned in the map
Notable particularity : Needs to be stored in a string variable to be used ( [] )
Notable glitches : As far as I know, it's only possible to compare the position on the x axis, using LessOrEqual with $pos[1] doesn't seem to work at all.
PlayerPosition { scale "1" resultVar "$pos" } // $pos "[0 0 0]"
Example of mods that use this input proxy :

Click here    Click here (Real DMG)


Description : Returns a number associated with the team the player is in : 0 = survivor ; 1 = infected ; 2 = spectator
Usage : To display something depending on the Team the player is in
PlayerTeam { team "3" // No idea what this does resultVar "$team" }
Example of mods that use this input proxy :

Click here    Click here
(Used in both cases to prevent scary stuff to happen if you play as Infected)
See also "hulkwallglow.vmt" in L4D2 files.


Description : Returns a number between 0 and 1 associated with the number of hits dealt with a melee weapon (0 hits = 0 ; 2 hits = 0.4 ; 5 hits = 1)
Usage : Only on melee weapon, to know how many infected (between 0 and 5) were killed with the specific melee weapon
Notable particularity : Value is shared between the world and the view model.
BloodyHands { resultVar "$hits" }
Example of mod that use this input proxy :

Click here


Description : Returns a number between 0 and 1 that depends if the survivor having the material is Boomed.
Usage : Only on survivors, to make stuff depend on the fact that a survivor is (or was) boomed.
IT { resultVar "$boomed" }
See "survivors_it_shared.vmt" in game files for an example


Description : Used on Miniguns, returns a number between 0 and 1 depending on the MininiGun heat (0 = cold, 1 = overheat)
Usage : Only on Miniguns to know its heat value.
Heat { resultVar "$heat" }
See "w_minigun.vmt" in game files for an example
List of math proxies
Here are some math proxies that are used to transform variables along the code : Addition / Subtraction / Multiplication / Division / Absolute Value

You can also find the Material Proxies List here.


Description : Allows to perform basic math operations on variables
IMPORTANT : To avoid problems, always make sure that the value set as resultVar cannot change anywhere else in the code ; otherwise, create a variable only to store the result.
Add { srcVar1 "$one" srcVar2 "$two" resultVar "$three" }
Subtract { srcVar1 "$ten" srcVar2 "$four" resultVar "$six" }
Multiply { srcVar1 "$three" srcVar2 "$four" resultVar "$twelve" }
Divide { srcVar1 "$fifty" srcVar2 "$five" resultVar "$ten" }

Description : Returns the absolute value of a number (= force it to be "positive")
Abs { srcVar1 "$minusten" resultVar "$ten" }

Description : Clamps a value between two boundaries. It "forces" the variable to have a value of "at least" the min and "at maximum" the max.
Clamp { min "0" max "1" srcVar1 "$detailblendfactor" resultVar "$detailblendfactor" }
The code above prevents the detail blend factor (detail global transparancy) to reach unlogical values (like, more opaque than 100%).


Description : Copies the value of the variable in srcVar1 to the variable in resultVar
Equals { srcVar1 "$frame" resultVar "$bumpframe" }
The code above is an proxy to add at the end of a code to make the frame of the diffuse match the frame of the bumpmap.
List of output variables
Here are some of the major outputs that are useful to change with proxies. Most of them are just VMT parameters but I thought it could be intresting to have a small list of what the Proxies can change :



$frame : Frame of a VTF, used to change the "texture" of the material. It can also change a selfillum mask but only if it's contained in the alpha channel of the $basetexture. It's although NOT possible to change things such as phong mask, ao mask or envmap mask. To change things like this, the only solution is Model RNG.
$frame
There are also other varaibles for things other than the $basetexture :
$bumpframe // For $bumpmap $detailframe // For $detail $texture2frame // For $texture2 (UnlitTwoTexture)

$alpha : Display of a VTF, used to change the visibility of the material (base of Model RNG).
$alpha // 0 = invisible, 1 = visible
If it doesn't work (it sometimes does depending on the object), it's possible to make something invisible by changing the $frame of the object, where frame 0 has a full-white alpha and frame 1 has a full-black alpha, is $translucent or $alphatest is set to 1. See the Pizza RNG Mod for an example.


$selfillumtint : Intensity of the Glow defined by the diffuse alpha or a $selfillummask
$selfillumtint // [0 0 0] = no glow, [1 1 1]= max glow
You need to inject the values separately into that one, see cara_95sedan_glass_alarm.vmt in the game files for an example.


$color : Color of the material "under" the $basetexture (used to color things with different colors)
$color // [0 0 0] = black, [1 1 1]= white
You need to inject the values separately into that one, see the Color RNG Rickenbacker and the Part 2 of the Guide on Color RNG for more info.


TextureTransform is a proxy that allows to modify the $basetexturetransform (and related) value of a VMT, resulting in a texture rotation, translation or scaling. It it used to make smooth animations that can also work atop of animation frames.
To use it, you always need to set all the parameters, as seen in the example below (explainations below the example) :

To use this proxy, you'll always need these variables (they can have another name) :
$center : The "center" of the material, scaling and rotation will take this point as an origin. Default is .5 .5 (witch means half width half height of the material)
$angle : The angle of rotation of the material in degrees, relative to the $center.
$scale : The scaling of the material (x and y axis) related to $center. Warning, the HIGHER the number, the small it is (2 = 2 times smaller, 0.5 = 2 times bigger) ; scaling can be set independantly for x and y axis.
$translate : Translation of the material. Number between 0 and 1 in translation %. 0% (0) = 100% (1). Setting this value to .5 will operate a 50% translation, the "center" will now be at the sides.
$basetexturetransform : Tells the proxy "what" to modify. You can also use $bumptransform for $bumpmap, $detailtexturetransform for $detail and $texture2transform for $texture2.
$angle "0" $translate "[0.0 0.0]" $center "[0.46533203125 0.4619140625]" $scale "[1 1]" Proxies { LinearRamp { "rate" "-0.00833333333" "initialValue" "0" "resultVar" "$angle" } TextureTransform { "translateVar" "$translate" "rotateVar" "$angle" "centerVar" "$center" "scaleVar" "$scale" "resultVar" "$basetexturetransform" } }
The above code is the VMT Proxies code the the "Hours" Clock Arm in the mod I did :
1 : $scale and $translate are set, even though I don't use them, because the Proxy always requires all the values.
2 : The LinearRamp is used to gradually increase the $angle, resulting in making the texture rotate.
3 : The 0.00833333333 in the Linear Ramp is the time it has to take for the Ramp to reach 1. The hour arm has to complete a rotation in 12 hours, witch is 43200 seconds. It means it has to move 1° every 43200/360° = 120 seconds so it has to move of 1/120th of degree every second, : 1/120 = 0.00833333333
4 : The -0.00833333333 is because it has to rotate clockwise. Not putting the - would result in the arm to rotate counterclockwise.
5 : The $center is custom defined. It is (x axis) at 953 pixels on a 2048x2048 texture. Pure center (0.5) would be at 1024/2048. Therefore, the $center has to be at 953/2048 = 0.46533203125 on the x axis.
I have done a few mods that use the TextureTransform proxy to animate things, here are some examples for you to look at :

Translation :
Tablet Medkit (on ECG)
Hatsune Miku Tunnel of Love (on music notes)

Rotation :
Black Hawk (on rear rotor)
C-17 Globemaster (on engine fans)
Animated Clocks (on clock arms)

Scaling :
Tablet Medkit (on Heart)
Global Radar(s)[www.gamemaps.com] (on HUD Icons)
IF $X > $Y... ELSE...


The base of every programming language are conditions. For example, if you want to change the texture of your model when the player speed is over 200 units per second, you need an "if" for the "when" part of the sentence :
"If the player speed is over 200, set frame to 0, else, set frame to 1"

Conditions like this are written this way in "Proxies Language"

LessOrEqual { srcVar1 "$x" // if $x srcVar2 "$y" // > $y resultVar "$a" // { $a = greaterVar "$b" // $b } LessEqualVar "$c" // else { $a = $c } }

We use the "LessOrEqual" proxies. This proxy compares srcVar1 to srcVar2. If srcVar1 is bigger than srcVar2, it copies greaterVar in resultVar, otherwise, if srcVar1 is less or equal to srcVar2, it copies LessEqualVar in resultVar.

The order you put the things in is not relevant here, I myself write it that way :

LessOrEqual { LessEqualVar "$c" // else { $a = $c } greaterVar "$b" // $b } srcVar1 "$x" // if $x srcVar2 "$y" // > $y resultVar "$a" // { $a = }

Now that you know how to write a proper condition. Time for a practical example (taken from a Pure RNG code) :

$skincount "13" // Number of different skins we have $rand "0" // Used to store random number $zero "0" "Proxies" { EntityRandom { scale "$skincount" resultVar "$rand" } LessOrEqual { LessEqualVar "$frame" greaterVar "$rand" srcVar1 "$rand" srcVar2 "$zero" resultVar "$frame" } }

This code shows how to change the frame N° of an object by a random number, only if it's an entity
In this code, I defined 3 custom variables.
$skincount is a constant, it's the number of skins I have (13 frames in my VTF)
$rand is where I store the random number that I input with EntityRandom
$zero is simply 0 (a constant) that I use in my condition

Then, I have my input proxy, that stores a random number associated with the ID (between 0.000 and 1.0000) of the object the material is applied to into $rand, after it has multiplied it by $skincount (so I get a number between 0.00000 and 13.0000).

Then, I have the condition, that says :

If ($rand > $zero) { $frame = $rand } else { $rand = $rand }

Witch actually is the same as

If ($rand > $zero) { $frame = $rand }

And with words : $frame = $rand only if $rand > 0

This bit of code allows to resolve a "glitch" that is that the skin that is carried by the survivors always is the first frame with Real RNG. When an object is carried by a survivor, it is not an entity, and therefore always returns 0. Since the code says "do not change the frame if $rand = 0" (witch means : do not change the frame if it's a non-entity (= carried by survivor), it will result in the material carried by the survivor to take the last frame the player has "seen" in the world as the reference frame to display)

It's possible to do infinite variations of what you would like. The best and most interesting way to code using this is to modify the FRAME ($frame) of the texture (= allow an entire new texture) based on Input Proxies. Here's another example, that changes the $frame (frame) of an object depending on the player speed :

$speed "0" // Define a variable to store speed $speedlimitone "100" // Speed limit 1 $speedlimittwo "200" // Speed limit 2 $zero "0" $one "1" $two "2" "Proxies" { PlayerSpeed // Input Proxy { scale "1" resultVar "$speed" } LessOrEqual // Check with first limit { LessEqualVar "$zero" greaterVar "$one" srcVar1 "$speed" srcVar2 "$speedlimitone" resultVar "$frame" } LessOrEqual // Check if second limit { LessEqualVar "$frame" // IMPORTANT, READ ORANGE BELOW greaterVar "$two" srcVar1 "$speed" srcVar2 "$speedlimittwo" resultVar "$frame" } }

What the code does is the following :
1 : Input Proxy to store the player speed
2 : Condition : "if $speed > $speedlimitone, store $zero into $frame, otherwise store $one into $frame
3 : Condition : "if $speed > $speedlimittwo, store $two into $frame, [color=#ff8d00]otherwise store $frame into $frame

This will result in $frame to be set to 0 if the player speed is below 100 units per second.
This will result in $frame to be set to 1 if the player speed is above 100 units per second but below 200 units per second.
This will result in $frame to be set to 2 if the player speed is above 200 units per second.

The part in [color=#ff8d00]orange above is important : We have to tell the code to insert $frame into $frame (a.k.a do nothing) if the $speed is below $speedlimittwo in order not to loose what we did in the previous proxy. If we had put something else than LessEqualVar "$frame", the first condition (first LessOrEqual) would have been useless.
The order of operations is important. After the first "if...else...", we can only put "if...."s.

At that point, you can already do a LOT of stuff. Everything below is bonus ; You should be able to make a gun change its texture if you fall ; make a weapon glow if you are close to it (or use it in first person) etc.

IF $X == $Y... ELSE...


It's also possible to write an "==" condition, with however more procies

Here's an example of == operator in "Proxies Language"

Subtract { srcVar1 "$x" srcVar2 "$y" resultVar "$xminusy" } Abs { srcVar1 "$xminusy" resultVar "$xminusy" } LessOrEqual { LessEqualVar "$one" greaterVar "$zero" srcVar1 "$xminusy" srcVar2 "$tolerance" resultVar "$xequalsy" }

The code above sets $xequalsy to 1 if $x==$y and to 0 if not.
If the absolute value of the difference between 2 numbers is bigger than 0, it means that the 2 numbers are different. That's what we ask the code to check here.

What's IMPORTANT is that you have to define $tolerance at 0 in your variables (not shown above since it's only the proxies). What the code does is that it checks if there's a maximum difference of $tolerance between $x and $y.

That means that you can also change $tolerance to things different than 0 to create a condition that would say : IF $X has a max difference of [number] with $Y... ELSE...
8 Comments
MLUI Jan 20, 2022 @ 7:47pm 
@Elieen Can the dismemberment effect be done with this?
Soren MC Jan 15, 2022 @ 1:59pm 
When using the: "AnimatedEntityTexture" proxy, how do i control it from the enitity? what functions must i run
K"ashimura♦ Oct 31, 2021 @ 6:09pm 
@Ellie ok, thx
Ellie  [author] Oct 31, 2021 @ 9:44am 
@K"ashimura♦ : Sadly, no as far as I know
K"ashimura♦ Oct 27, 2021 @ 1:24am 
Hi, i have a question.If i don’t know the variables in the proxy, can i output to the console to view this variable?
Kyle Jul 10, 2020 @ 11:44am 
@Ellie : Sure add a friend
Ellie  [author] Jul 10, 2020 @ 11:36am 
@Dr_Fox96🦊 : Just ask or add me as a friend ;)
Kyle Jul 7, 2020 @ 5:49pm 
hello Ellie :steamhappy: . I have a question and since you made the radar for the pills, the bombs and the special zombies, I would like to know it makes me curious and intrigued, I admire your mods Greetings :fhappy: . DrFox96