Team Fortress 2

Team Fortress 2

84 ratings
An All-Inclusive TF2 Scripting Guide
This guide will teach you everything need to know about scripting. Although this guide will not supply any information to give any significant advantage in gameplay, it allows for players to have the game work for them however they please (to a certain extent, obviously). This guide includes information such as what scripting/a config is, the basics of creating one, what is possible from these configs, debugging, and more.
What is a Config?
A config, or script, is essentially a series of console commands that are set to run either automatically, or when a key is pressed. They can be used to run console commands from in game (toggling viewmodel; setting the FoV), and can also be used for basic ingame things to be run at the same time (crouch, jump, and fire at the same time; automatically set down a building).

These scripts can be accessed from:
C:\Program Files (x86)\Steam\steamapps\common\Team Fortress 2\tf\cfg

By default, there is an autoexec.cfg, and a .cfg file(if you don't have these class .cfg's, we'll get into making them soon) for each class located in there, along with a few other files that we won't mess with. The autoexec.cfg is ran every time you start the game, and the [class].cfgs are ran whenever you switch to that respective class.

For editing these scripts, I recommend Notepad++, however, Notepad will do the trick.

In case you don't already have the [class].cfg files, you can create them. Be sure to include the following classes in your /cfg/ folder, and make sure they're saved as .cfg files, and not as .txt files.

  • scout.cfg
  • soldier.cfg
  • pyro.cfg
  • demoman.cfg
  • heavyweapons.cfg
  • engineer.cfg
  • medic.cfg
  • sniper.cfg
  • spy.cfg

Now let's jump in to some of the basic commands.
The bind, alias, and exec commands.
Although they wouldn't have any purpose without the actual commands that we'll get into later today, these are the three most important and common commands when scripting.

The most common command in scripting, or just the Console Command in general, is the bind command, which is used to assign commands to keys on your keyboard, or buttons on your mouse. The syntax for the bind is listed below, as well as an example:

bind "[key/button goes here]" "[command goes here]" bind "mouse3" "explode"

Note how the second and third blanks are wrapped in quotes. Although it isn't necessary, it's generally a good idea so you can read your config later, and to ensure that Source doesn't have any hiccups.

You can also bind more than one command to a key, each command seperate by a semicolon, as the example below shows:

bind "rctrl" "taunt; wait 500; explode"

There is also a bindtoggle command which toggles between the values specified in the command. It accomplishes what the toggle "command" does, but specified in the command itself, not the command being run by the command. An example is shown below, along with a variant that accomplishes the exact same thing:

bindtoggle "b" "cl_showfps 0 1" bind "b" "toggle cl_showfps 0 1"

Where toggle makes a difference from bindtoggle shines in the alias command. The alias command stores one or more commands with a keyword. Aliases are forgotten whenever the game restarts (hence the need for a config; binds are not forgotten, obviously). Two examples are shown below:

alias "test" "echo 'YO WHADDUP HOMIE'" alias "chug" "slot3; taunt"

From there, both test and chug are treated as commands in scripts, and can be used in binds and configs. The reason for using aliases instead of just binding two commands to a key are so you can have cleaner "code", so you can reassign values, and so you can use the + and - modifiers, which we'll get into later.

Notice how 'YO WHADDUP HOMIE' is encased in single quotes. These are necessary if you need to use nested quotes for whatever reason, particularly here, since the whole phrase is echoed instead of just the

The last of these big three is the exec command. This command is used to call other .cfg files and execute them. Consider the following example:

exec ChocolateCupcakes.cfg

If you were to run that command, it would run all of the commands in the "ChocolateCupcakes.cfg", assuming it exists.

What I generally like to do is to have a "shared" config file that is used by all classes. Go ahead and create two new .cfg files (for this tutorial they will be called "shared" and "waitChecker"), and name them whatever you want. Well focus on the first one for now and push 'waitChecker' off until later today. Now put the following at the start of each of your [class].cfg's:

exec shared.cfg
The wait and incrementvar commands.
Now that you know the three basic commands in scripting, another important command to know is called the wait command. This is used to wait a certain number of the server's frames (Valve, and most other servers servers have 66 frames per second, if I recall correctly) before executing a different command. An example is shown below:

alias "oldJoke" "taunt; wait 330; explode"

DISCLAIMER: THE WAIT COMMAND CAN BE DISABLED ON SOME SERVERS. THIS CAN CAUSE SOME SCRIPTS TO NOT WORK, AND CAN CRASH YOUR GAME IF THE COMMAND IS INVOLVED IN ANY LOOPS. (Loops will be gone over later this tutorial). As a preventative measure for our game not to crash, we put the following in our waitChecker.cfg(taken straight from the TF2 Wiki, be sure to modify [counter-scripts go here] with a not working or fixed version of an existing script):

alias waitTester "alias waitTest waitPositive; wait; waitTest" alias wait "alias waitTest waitNegative" alias waitPositive "echo Wait is enabled on this server." alias waitNegative "echo Wait is DISABLED on this server!; [counter-scripts go here]" waitTester

In case you're wondering how this works, aliases can not be made of an already existing command (i.e. trying 'alias "quit" "echo HI! :)"' will give you an error). In servers with the wait command disabled, it is treated like an unknown command, therefore an alias can be made for it. The tester aliases "wait" to use "waitNegative", but can only be succesfully executed if the wait command is disabled.

Once you have that saved, put "exec waitChecker.cfg" at the END of each of your [class].cfg's. The reason waitChecker and shared are different files, and that shared goes at the start and waitChecker goes at the end is because you want the inner commands of the [class].cfg should overwrite the shared.cfg, but the waitChecker.cfg should overwrite the [class] commands. Here's something that might help explain:

exec shared.cfg //All of the binds mentioned from the this file are set //STUFFS GO HERE (Overwrites any keys used in the shared.cfg) exec waitChecker.cfg //Overwrites all of the commands if specified

Another useful command is the incrementvar command, which functions similarly to the toggle command. What it does is add or a subtract a certain value in a command based on a range. The syntax, followed by an example, are shown below:

incrementvar [first range] [second range] [increases value by this amount each time the command is run] alias "increaseFOV" "incrementvar fov_desired 75 90 1

A good thing to note about this command is that you can also use negative values in the third blank to decrease a certain value.
The + and - modifiers.
The default commands simply execute and finish once you press a button, whether it is quickly tapped or held down. What if you want something to execute as long as you have the button pressed? Then simply use + and - modifiers at the start of your alias. + is used while the button is being pressed, and - is used when the button releases. An example of some already existing + and - commands are shown below:

+ command
- command
Moves forward on key press.
Uses Secondary attack on key press.

Also, if a bind with more than one command has a +/- command AND a regular command, the regular command will be executed when the button is pressed, and when it's released

You can create your own aliases using + and -. A thing to note is that if a + alias exists, a - must exist or else the command will keep running constantly. Unlike regular aliases, aliases with + or - can be executed from the console. Here is a crouchjump script using + and -:

alias "+duckJump" "+jump; +duck" alias "-duckJump" "-jump; -duck" bind "space" "+duckJump"

There are a couple things to note here. For starters combining wait and +/- commands can give you some funny results. Take this example:

alias "+waitFire" "wait 500; +attack" alias "-waitFire" "-attack"

The above script won't work as intended if -waitFire is executed before the wait timer is up. However, +attack still gets executed anyway.

Also, for +attack and +attack2 specifically, spectator controls break whenever you include these in other aliases, for whatever reason, but work fine whenever they're bound on their own. Just be sure to include spec_next and spec_prev in these aliases.
The Remaining Commands
Alright, now you know all the important commands. That's great n' all, but it's kinda' useless without the rest of the commands, don't you think? Well, here are some commands to get you started. This will be a brief list of commands, there are many other commands that will not be listed. Here's a graph showing a few different commands (Any command with a + has a - counterpart that you should also keep in mind, and the quotes in these commands should be ignored if they are already inside quotes):

echo "(text)"
Prints a string of text to the console.
say "(text)"
Prints a string of text in the chatbox.
r_drawviewmodel (0, 1)
Sets viewmodel visibility off and on.
fov_desired (75-90)
Sets the field of view to different values.
viewmodel_fov (1-∞, recommended 54-90)
Makes the viewmodel appear farther away the higher the value.
Uses primary attack.
Uses secondary attack.
Erases any prior text written in the console

A list of useful commands can be found on this Wiki page.[]

Alternatively this is a list of all commands used in TF2 (Beware, a lot of commands).
Debugging Tips
So I'm going to assume that you've been writing a few scripts here and there, and more than likely, you've ran into an error of some sort. You read the script over and over and over again, but you can't seem to find out what's wrong. This is mostly because your brain tries to fill in certain things for you if they belong there. Now if you want an easier way, add an echo to each of your commands, each saying something different. Consider this viewmodel hiding script:

r_drawviewmodel 1 alias "weaponHide1" "slot1; r_drawviewmodel 0; echo I am weapon 1!" alias "waeponHide2" "slot2; r_drawviewmodel 0; echo I am weapon 2!" alias "weaponHide3" "slot3; r_drawviewmodel 1; echo I am weapon 3!" alias "autohideOn" "r_drawviewmodel 0; slot1; bind 1 weaponHide1; bind 2 weaponHide2; bind 3 weaponHide3; bind i autohideOff; echo I turned on!" alias "autohideOff" "r_drawviewmodel 1; bind 1 slot1; bind 2 slot2; bind 3 slot3; bind i autohideOn; echo I turned off!" bind "i" "autohideOn"

Now if you were to go ahead and try this, everything would print to the console properly EXCEPT for "I am weapon 2!" Now we can inspect something in that area. If you notice, you can find that there is a typo "waeponHide2" instead of "weaponHide2". If there is a typo inside the command itself (i.e. r_drawveiwmodel 0), it would read "unknown command" in the console. The echo trick is simply used to detect typos in the name of the alias.
Some Advanced Tricks
So you understand almost everything about scripts. The only things we need to get out of the way are very subtle, and use what we have already learned. The first technique we will be using is looping. Loops are done two lines of commands, both involving a starter command, a wait command, and a line that executes the other line. Here is an example of a simple loop:

alias "firstloop" "echo I am first loop; wait 66; secondloop" alias "secondloop" "echo I am second loop; wait 66; firstloop"

As mentioned before, if the wait command is not enabled/used in a loop, your game WILL crash. These accidental loops can also be used when exec'ing cfg files (i.e. execing autoexec.cfg from scout.cfg, where autoexec.cfg exec's scout.cfg).

Another trick is to toggle a series of aliases from another alias. A previously mentioned example that has this is the following viewmodel hiding script:

r_drawviewmodel 1 alias "weaponHide1" "slot1; r_drawviewmodel 0" alias "weaponHide2" "slot2; r_drawviewmodel 0" alias "weaponHide3" "slot3; r_drawviewmodel 1" alias "autohideOn" "r_drawviewmodel 0; slot1; bind 1 weaponHide1; bind 2 weaponHide2; bind 3 weaponHide3; bind i autohideOff" alias "autohideOff" "r_drawviewmodel 1; bind 1 slot1; bind 2 slot2; bind 3 slot3; bind i autohideOn" bind "i" "autohideOn"

When you press i, you bind 1, 2, and 3 to weaponHide1, 2, 3, and you rebind i to undo the script. This comes in handy incase you don't always want to use a script.

Sometimes, you might want one script to change an already existing script to prevent bugs. Consider the following example, along with the viewmodel hiding script above:

alias "lowFov" "r_drawviewmodel 0; fov_desired 75" //Personally, I would change the alias "highFov" "r_drawviewmodel 1; fov_desired 90" // viewmodel_fov instead of turning off alias "+zoom" "lowFov" // viewmodels, but this is an example. alias "-zoom" "highFov" bind "ctrl" "+zoom"

These scripts together have a few conflicts. Let's say you pull out your rocket launcher (viewmodel turns off) and you zoom in. Once you zoom out, your viewmodel turns back on without you wanting it to. To fix this, we can adjust the viewmodel hiding script to set the zoom command to be different if it's on, like the following:

alias "autohideOn" "...; alias highFov fov_desired 90"

Typically you want some sort of hierarchy in your scripts. Typically +/- commands are overruled by regular commands, but sometimes you want to enable one script before you enable another script, unless you want to specify different rules in both of the scripts similar to how it's done above.

You can also use +/- commands to alter how regular commands work, similar to how pressing CTRL+C copies, and pressing C simply types 'c'. Here's a good example:

alias "+callCommand2" "alias call2 voicemenu 1 6" alias "-callCommand2" "alias call2 voicemenu 1 1" bind "q" "call2" bind "e" "+callCommand2"

Another trick similiar to looping is putting a sequence of aliases that can be executed using more than one key. Consider the following example, full of echo commands:

alias "nextLine" "1" alias "1" "echo I am #1!; alias nextLine 2" alias "2" "echo I am #2!; alias nextLine 3" alias "3" "echo I am #3!; alias nextLine 1" bind "b" "nextLine" bind "m" "nextLine"

The following shows that you can advance the order of the echo commands by either pressing "b" or "m". Note that the first line must be there because aliases are forgotten whenever the game restarts, as I mentioned earlier. Although if you want, you can put the starting alias in your autoexec.cfg, and have the order continue if you happen to disconnect and join another game, if that really matters to you.
F.A.Q. and Conclusion.

Q. Is this legal?
A. Yes. I don't think Valve would have integrated this feature if they didn't want people using it. However, some servers have scripting against their rules.

Q. Can I use scripts in competitive games, and is the wait command enabled there?
A. From what I've seen scripts are allowed (as long as they're not exploits), and the wait command being allowed depends upon the league I'm assuming.

Q. What kind of stuff should I put in my shared.cfg?
A. I rebind any keys that have been modified by my [class].cfg, along with some silly chat binds and any other feature that can be used by any class(Keep in mind, if a majority of your classes use a script except for one, you're better off just re-aliasing the script for that specific class).

Q. Look at my script and try to find the errors!
A. That's not a question.

Q. *sigh* Fine, can you look at my script for errors?
A. Please look at Debugging Tips before consulting me. If the problem persists, feel free to add me.

Q. What should I name my aliases n' such?
A. From what I've seen, the convention is to use acronyms (which I personally hate). I always follow the Java convention of naming variables (aliases in this case) with first letter is lowercase, and the rest of the words are capitalized.

Q. I have another question. Will you answer it?
A. Yes I will. If it's common enough might even include it in this F.A.Q.

Well, by the time you finished reading this long guide, you should know just about everything you need to know when making scripts. Thanks for reading my guide!

Also I'd like to mention that this guide is subject to change, depending on the kind of suggestions I get.

< >
shinomni Apr 28 @ 2:09am 
thank you so much this thing taught me cgf.txt doesn't mean it is a CGF File.
and now i can use them
P-z Sep 25, 2017 @ 11:38am 
If nobody knows, i can just bind it to another .cfg file, because it's over 15 commands for a single key anyways. I still would like to know if there's a solution that I just didn't see.
P-z Sep 24, 2017 @ 5:06pm 
how do i use bind in an alias to bind more than one command?

alias "2keys" "bind q say hi say there"

i'm pretty sure if i put a semicolon in between the two say commands it will bind "say hi" and execute "say there". you can't use two bind commands because one will overwrite the other. how do i get around this while still using aliases?
SmeagolPlaysGames May 29, 2017 @ 3:25pm 
1-800-EMCEESTANKY  [author] May 29, 2017 @ 3:19pm 
smeagol, you're looking at something like:

alias "cfg1" "exec gfx.cfg; bind x cfg2"
alias "cfg2" "exec settings.cfg; bind x cfg1"
SmeagolPlaysGames May 28, 2017 @ 12:04pm 
Is it possible for me to use a script to toggle entire configs? I specifically want to toggle two max-fps configs, which are named "gfx.cfg" and "settings.cfg". Thanks in advance.
Vad Feb 13, 2017 @ 12:55pm 
looking to have two chat binds at once, like if i press a button it will trigger a loop of chat binds, like the lmaobox binds, it doesnt have to be infinite but i just want to have a double bind
Anyar Jan 6, 2017 @ 10:46am 
1-800-EMCEESTANKY  [author] Sep 18, 2016 @ 3:25pm 
also, to anybody that wants help, add me instead of commenting, as i don't look at this thread, and being able to talk to people is easier
1-800-EMCEESTANKY  [author] Sep 18, 2016 @ 3:24pm 
@lslnger this is how i'd go about it, can't confirm if it works, hmu with any problems

alias "startTaunt" "taunt_by_name Taunt: The Proletariat Posedown; wait 66; endTaunt"
alias "endTaunt" "wait 66; startTaunt"
alias "resetTaunt" "alias startTaunt taunt_by_name Taunt: The Proletariat Posedown; wait 66; endTaunt"

alias "+repeatTaunt" "resetTaunt; startTaunt"
alias "-repeatTaunt" "alias startTaunt echo THIS TAUNT IS DONE"