Go to file
Nicholas Hope db2d8c5e37 Added space to soldier 2022-12-16 16:11:21 -05:00
.vscode Not sure. Seems added by sphinx. 2022-08-13 15:36:48 -04:00
docs Added new pages and linked in 2022-08-15 09:05:18 -04:00
examples Added space to soldier 2022-12-16 16:11:21 -05:00
tests Added new type test 2022-10-02 12:13:37 -04:00
tfscript Changed some names, made "bind" a type instead 2022-12-11 20:29:08 -05:00
.gitignore added .venv 2022-08-13 12:12:36 -04:00
LICENSE Initial commit 2022-06-12 13:41:34 -04:00
README.md Added a lot of content 2022-08-12 19:57:34 -04:00
pylintrc Added pylintrc file for pylint 2022-06-15 09:28:45 -04:00
pyproject.toml added CLI identifier and setuptools 2022-08-06 22:13:30 -04:00
requirements.txt added for pytest integration 2022-08-13 12:12:36 -04:00
setup.cfg changed package target 2022-10-31 13:47:32 -04:00
setup.py Moved up to make installation more consistent 2022-10-31 11:45:09 -04:00


Hello and welcome to TFScript!

This is a little project I created over the summer of 2022 to aid those who might want to do complex things with tf scripting that they may not know is possible.

Basic overview

TFScript is meant to simplify the complexity of creating tf2 keybinds. While simple binds may be easy, more complex actions such as key combinations and toggles can be quite complex.

The basic process for creating a TFScript config file goes as such:

  1. Write your TFScript file according to the Syntax Guide
  2. Run TFScript <filename> to generate code and write to game files
  3. Play tf2 with your new binds!

A TFScript file is written in YAML and parsed by the program to generate the apporpriate code, so if you know the YAML syntax you can jump straight to the TFScript Syntax section, otherwise, read the YAML Syntax section below, then read the TFScript Syntax section.


YAML Syntax




TFScript Syntax

A TFScript file is structured like this:



where class1, class2, etc. refer to classes such as soldier, pyro, or any of the other lovable mercenaries.

key1 and key2 are, rather obviously, keys such as w or mouse1 to which you want actions bound, and type/fields is the data to be parsed by TFScript to generate a config file.



The full list of valid class names is as follows:

  • Scout
  • Soldier
  • Pyro
  • Demo
  • Heavy
  • Engi
  • Medic
  • Sniper
  • Spy
  • Default

As you may have noticed, there is a special class, "default", which specifies the default state of any and all keys. Any keybinds in this config are the defaults, and apply to all other classes unless specifically overwritten (that said, any classes which do have a different definition for that key will overwrite the previous "default" definition).

Names are not case sensitive, so "SoLDiER" will work just as well as "soldier".



If you are already familiar with TF2 scripting, every key that tf2 recognizes is also recognized by TFScript.

For the rest of us, the most relavent keys are:

  • A to Z
  • 0 to 9
  • space
  • tab
  • capslock
  • shift
  • ctrl
  • function
  • alt

For the remaining symbol characters (like "`", "[", or "\"), just press the key that it appears on, on your keyboard. Do not hold shift, alt, or any other control keys. This does limit the keys you can use, for example if you wanted to use the { character, you would be stuck with [. This is a limit imposed by TF2, but you can get around this using the double type, as explained later.



The moment we've all been waiting for, the types. These are the crux of any TFScript file, and the bulk of what's important. In fact, they're so important I'm just going to write the important bits right now and leave the rest to be completed by release 1.0.0




  command: <text>

impulse: <text>

Binds the associated key to the command provided by text.

Also has shortcuts for voice, build, and destroy


  impulse: voice medic




    <type and body>
    <a second type and body>
  condition: <key>
  type: <"held" or "toggle", default "held">
  cancel both: <true or false, default false>
  solo: <true or false, default false>

While the condition is satisfied, the associated key behaves as secondary, otherwise it behaves as primary. primary and secondary are both types defined above, like impulse or hold, and follow the exact same syntax.

If the type is "held", the condition is satified while the condition key is held down, but if the type is "toggle" then the condition will be satisfied when the condition button is pushed, and continue being satisfied until it is pushed again (so the condition key toggles the associated key between the secondary and primary behaviours).

If cancel both is true and either of primary or secondary are of the type hold, releasing the associated key will execute the release block of both keys or otherwise stop both actions from continuing. Otherwise the two keys will behave completely independently of each other.

If the condition key already has other keys that change with it, then the condition key will change all of them to their secondary action. However, if solo is set to true, it will only toggle the given key and not any of the others.


    impulse: voice medic
    impulse voice activate uber
  condition: mouse4


Some examples

For example, this config will bind "e" to call for medic, unless mouse4 is held, in which case it will call for an ÜberCharge:

alias call_for_medic "voicemenu 0 0"
alias call_for_uber  "voicemenu 1 6"
alias e_bind call_for_medic
bind e e_bind
alias +toggle_state "alias e_bind call_for_uber"
alias -toggle_state "alias e_bind call_for_medic"
bind mouse4 "+toggle_state"

There are some issues with this:

  • It is quite verbose, and if several of these exist the file can become difficult to traverse
  • There is a lack of clarity in the voicemenu command, only the bind name explains what it does
  • If either "mouse4" or "e" had a prior function, it has now been overwritten
  • The scope is dictated by what .cfg file this is located within, which can cause problems


The TFScript way of doing this is

        impulse: voice medic
        impulse: voice activate uber
      condition: mouse4

There are several benefits to this:

  • The indentation allows for easier scanning of the file
  • The voicemenu commands have been replaced with the clearer "voice" impulse
  • Since this is within the default section, it is clear that there are is no "prior function" to overwrite and this will apply to all classes unless specifically overwritten.


Known issues, problems, etc.

none everything is perfect and there are no problems it is all amazing