Difference between revisions of "TASing"

From P2SR Wiki

m (Can't Even moved page TASsing to TASing over redirect: Incorrect spelling of TASing (it was spelt TASsing, which is wrong))
m (typo)
 
(35 intermediate revisions by 5 users not shown)
Line 1: Line 1:
{{P2_Title|TASsing}}
+
{{P2_Title|Portal 2 TAS-ing}}
This wiki page intends to introduce beginners to Portal 2 TASsing by  
+
= Introduction =
explaining how to play one in-game and how to write one.
+
This article serves as a complete documentation of the Portal 2 TASing environment implemented into [[SAR]] and the <code>.p2tas</code> scripting language used by the TAS playback. It also intends to be a guide for people interested in Portal 2 TASing.
It also serves as documentation for the TAS tools.
+
== Setup ==
== Introduction ==
+
* Install [[SAR]] if you don't already have it.
=== Setup ===
+
* Create a new folder called <code>tas</code> in your <code>Portal 2</code> directory. This is where all your TAS scripts will live.
* Install  [https://github.com/p2sr/SourceAutoRecord/releases SAR] if you do not have it already.
+
** The script files will need to have a <code>.p2tas</code> extension and can be played using the command <code>sar_tas_play <filename></code>. For example, if your script file is <code>Portal 2/tas/mytas.p2tas</code>, then to play it in-game you would use the command <code>sar_tas_play mytas</code>.
* Create a new folder called <code>tas</code> in your <code>Portal 2</code> directory. This is where all your tas scripts will live.
 
** The script files will need to have a <code>.p2tas</code> extension and can be played using the command <code>sar_tas_play <filename></code>. So for example if your script file is <code>Portal 2/tas/mytas.p2tas</code>, then to play it in-game you would use the command <code>sar_tas_play mytas</code>.
 
  
==== Visual Studio Code ====
+
== Visual Studio Code ==
Thanks to the diligent coding of RainbowwPhoenixx, Soni, and Cqnd, there is a custom language file that can be used with [https://code.visualstudio.com/download Visual Studio Code] (a.k.a. VS Code) to greatly assist in editing <code>.p2tas</code> files. Some major features of this extension include:
+
<pre style="color:red">The VS Code extension currently does not support the playback of coop TASes, so you will need to bind a key to 'sar_tas_replay' and start playback using 'sar_tas_play <filenameblue> <filenameorange>'</pre>
 +
Thanks to the diligent coding of RainbowwPhoenixx, Soni, mlugg, and Cqnd, there is a custom language file that can be used with [https://code.visualstudio.com/download Visual Studio Code] (a.k.a. VS Code) to greatly assist in editing <code>.p2tas</code> files. Some major features of this extension include:
 
* Integration with SAR's TAS server for TAS playback control within VS Code.
 
* Integration with SAR's TAS server for TAS playback control within VS Code.
 
* Syntax hightlighting to make your scripts easier to read
 
* Syntax hightlighting to make your scripts easier to read
Line 18: Line 17:
 
* And more!
 
* And more!
  
==== Setting up Visual Studio Code with p2tas-lang ====
+
=== Setting up Visual Studio Code with p2tas-lang ===
 
Once you have installed [https://code.visualstudio.com/download Visual Studio Code], you will want to install the <code>p2tas-lang</code> extension in order to use all of its features.
 
Once you have installed [https://code.visualstudio.com/download Visual Studio Code], you will want to install the <code>p2tas-lang</code> extension in order to use all of its features.
 
# On the left side of the main window of VS Code, there is an extensions menu that you can open (you can also hit ctrl+shift+x).
 
# On the left side of the main window of VS Code, there is an extensions menu that you can open (you can also hit ctrl+shift+x).
Line 27: Line 26:
 
Note: sometimes there may be beta features for <code>p2tas-lang</code> that are still in development, and will therefore not yet available through the extensions marketplace. If that is the case, you may be able to find a <code>.vsix</code> file from one of the extension developers and install that manually. '''Make sure you only manually install extensions from trusted sources!'''
 
Note: sometimes there may be beta features for <code>p2tas-lang</code> that are still in development, and will therefore not yet available through the extensions marketplace. If that is the case, you may be able to find a <code>.vsix</code> file from one of the extension developers and install that manually. '''Make sure you only manually install extensions from trusted sources!'''
  
=== The .p2tas file format ===
+
= The .p2tas file format =
<code>.p2tas</code> is somewhat similar to <code>.srctas</code>.
+
The <code>.p2tas</code> format roughly follows the idea of <code>.srctas</code>, however, while <code>.srctas</code> focuses more on data-based representation of the script, <code>.p2tas</code> deviates into more declarative syntax.
  
==== Version indicator ====
+
== Header ==
 +
Every <code>.p2tas</code> script needs to start with a header, which will give TAS player essential info for script playback.
 +
 
 +
=== Version indicator ===
 
At the beginning of every script, you have to include a <code>version <number></code> command. This number lets the TAS tools know which version of the tools to use in order to maintain compatibility with older scripts (this mostly affects the <code>strafe</code> tool).
 
At the beginning of every script, you have to include a <code>version <number></code> command. This number lets the TAS tools know which version of the tools to use in order to maintain compatibility with older scripts (this mostly affects the <code>strafe</code> tool).
  
The latest version number as of June 1st, 2022 is <code>3</code>.
+
The latest version number is {{TASVER}}.
 
 
==== Start commands ====
 
After the <code>version</code> command, you have to include a <code>start</code> command. It can have five parameters: <code>now</code>, <code>save</code>, <code>map</code>, <code>cm</code> or <code>next</code>.
 
  
* <code>start now</code>: Starts playing the TAS immediately, or as soon as a session is started if you are not in one (i.e. in the main menu).
+
=== Start commands ===
* <code>start save <save_name></code>: Loads the given save and then immediately starts playing the TAS.
+
After the <code>version</code> command, you have to include a <code>start</code> command. It can have five parameters:
* <code>start map <map_name></code>: Loads the given map and then immediately starts playing the TAS.
+
* <code>now</code>: Starts playing the TAS immediately, or as soon as a session is started if you are not in one (i.e. in the main menu).
 +
* <code>save <save_name></code>: Loads the given save and then immediately starts playing the TAS.
 +
* <code>map <map_name></code>: Loads the given map and then immediately starts playing the TAS.
 
** Internally, SAR uses <code>changelevel</code> to go to the map if the map is different than the current session, and <code>restart_level</code> if the map is the same as the current session. This has the potential to cause slight inconsistencies in how the map is loaded, so when making TASes it is common practice to ignore any outcomes that happen when you start your TAS from a different map, as they could be different from when you start the TAS on the same map.
 
** Internally, SAR uses <code>changelevel</code> to go to the map if the map is different than the current session, and <code>restart_level</code> if the map is the same as the current session. This has the potential to cause slight inconsistencies in how the map is loaded, so when making TASes it is common practice to ignore any outcomes that happen when you start your TAS from a different map, as they could be different from when you start the TAS on the same map.
* <code>start cm <map_name></code>: Starts playing the TAS after loading into the given map, but in Challenge Mode.
+
* <code>cm <map_name></code>: Starts playing the TAS after loading into the given map, but in Challenge Mode.
 
** The discrepancy listed above with <code>start map</code> also applies to <code>start cm</code>.
 
** The discrepancy listed above with <code>start map</code> also applies to <code>start cm</code>.
* <code>start next <start type></code>: Emulates one of the above start types, but waits until the next session load to start TAS playback. If left blank, SAR presumes a start type of <code>now</code>.
+
* <code>next <start type></code>: Emulates one of the above start types, but waits until the next session load to start TAS playback. If left blank, SAR presumes a start type of <code>now</code>.
 
** SAR will wait until the next session load to start TAS playback, but in the case of <code>save</code>, <code>map</code>, and <code>cm</code>, SAR will immediately load the save/map requested. This means that SAR will load the save/map, wait until the next load, and ''then'' start TAS playback.
 
** SAR will wait until the next session load to start TAS playback, but in the case of <code>save</code>, <code>map</code>, and <code>cm</code>, SAR will immediately load the save/map requested. This means that SAR will load the save/map, wait until the next load, and ''then'' start TAS playback.
 
** Due to a bug in how SAR handles TAS playback, when SAR is waiting for the next session load to start TAS playback, even though no TAS is playing, SAR is still in kind of a "half-TAS" state, and thus will make the game ignore your inputs. Due to this, the only currently useful implementation of <code>start next</code> is running <code>start next save <save_name></code> with a save made just before a level transition. Starting your TASes this way results in level loading and game playback that is theoretically identical to the conditions you would find yourself in during a regular fullgame speedrun, making it the most theoretically legitimate way of making a TAS.
 
** Due to a bug in how SAR handles TAS playback, when SAR is waiting for the next session load to start TAS playback, even though no TAS is playing, SAR is still in kind of a "half-TAS" state, and thus will make the game ignore your inputs. Due to this, the only currently useful implementation of <code>start next</code> is running <code>start next save <save_name></code> with a save made just before a level transition. Starting your TASes this way results in level loading and game playback that is theoretically identical to the conditions you would find yourself in during a regular fullgame speedrun, making it the most theoretically legitimate way of making a TAS.
  
==== Examples: ====
+
=== RNG manipulation ===
 +
Since SAR version 1.12.7-pre6, it is possible to save some of the RNG-dependent elements in current session (gel spread, bridge bump camera shifting etc.) using command <code>sar_rng_save</code>. Then, an optional <code>rngmanip <filepath></code> header line can be used to automatically inject these values before TAS starts playing. The path is relative to your main "Portal 2" directory.
 +
 
 +
In the future, this feature will be capable of recording and recreating all of the game's RNG, allowing us much greater playback consistency during the TASing process.
 +
 
 +
=== Header examples ===
 +
Below you can see some examples of how a header file may look like in your script.
 +
 
 
Loads Triple Laser and immediately starts the TAS:
 
Loads Triple Laser and immediately starts the TAS:
<pre>
+
{{TASBLOCK|version {{TASVER}}
version 2
 
 
start map sp_a2_triple_laser
 
start map sp_a2_triple_laser
</pre>
+
}}
 
 
  
 
Immediately loads the save <code>tas_sp_a2_bts3</code> and starts the TAS:
 
Immediately loads the save <code>tas_sp_a2_bts3</code> and starts the TAS:
<pre>
+
{{TASBLOCK|version {{TASVER}}
version 2
 
 
start save tas_sp_a2_bts3
 
start save tas_sp_a2_bts3
</pre>
+
}}
 
 
  
 
Loads the save <code>future_starter_pre</code>, waits until the next session load, then starts the TAS:
 
Loads the save <code>future_starter_pre</code>, waits until the next session load, then starts the TAS:
 
In this example, the save <code>future_starter_pre</code> was made on the last tick of a TAS for the map Cube Momentum, meaning that it loads into a save on Cube Momentum, then a tick later the level transition completes, and then the TAS starts playback at the beginning of Future Starter. The end result looks very similar to <code>start map sp_a1_intro6</code> (Future Starter), but is arguably "more legitimate".
 
In this example, the save <code>future_starter_pre</code> was made on the last tick of a TAS for the map Cube Momentum, meaning that it loads into a save on Cube Momentum, then a tick later the level transition completes, and then the TAS starts playback at the beginning of Future Starter. The end result looks very similar to <code>start map sp_a1_intro6</code> (Future Starter), but is arguably "more legitimate".
<pre>
+
{{TASBLOCK|version {{TASVER}}
version 2
 
 
start next save future_starter_pre
 
start next save future_starter_pre
</pre>
+
}}
 +
 
  
==== Comments ====
+
== Comments ==
 
Comments are sections that will be ignored and can be expressed in 2 ways:
 
Comments are sections that will be ignored and can be expressed in 2 ways:
* Single line comment with <code>//</code>
+
* Single line comments with <code>//</code>
* Multiline comment by opening with <code>/*</code> and closing with <code>*/</code>
+
* Multi-line comments starting with <code>/*</code> and ending with <code>*/</code>
 
They are useful for adding notes in your script or to temporarily prevent a line from being executed.
 
They are useful for adding notes in your script or to temporarily prevent a line from being executed.
  
Every other line that isn’t <code>version</code>, <code>start</code>, a repeat block (explained later) or a comment is considered to be a framebulk.
 
  
== The framebulk ==
+
== Framebulk ==
Framebulks store the inputs and commands that should be executed by a virtual controller at a given tick. They’re formatted like this:
+
Framebulks store the inputs and commands that should be executed by a virtual controller at a given tick. Every line that isn’t a header line, a repeat block (explained later) or a comment is considered to be a framebulk.
<pre>tick>movement|angles|buttons|commands|tools</pre>
+
They’re formatted like this:
 +
'''{{TASBLOCK|tick>movement{{!}}angles{{!}}buttons{{!}}commands{{!}}tools}}'''
 
Below is an explanation of each field in a framebulk:
 
Below is an explanation of each field in a framebulk:
 
=== Tick ===
 
=== Tick ===
Line 104: Line 108:
 
Additionally, by putting a number after an uppercase button, you can specify a number of ticks after which it will be released automatically.
 
Additionally, by putting a number after an uppercase button, you can specify a number of ticks after which it will be released automatically.
  
''Example'': <code>dU1J</code> as buttons field will release duck button, press the use key for 1 tick and press jump input. All other inputs will remain at their previous state.
+
''Examples'':
 +
 
 +
* <code>D</code> will press the duck input. <code>d</code> will release the duck input.
 +
* <code>D8</code> will press and hold the duck input for 8 ticks, then release it.
 +
* <code>dU1J</code> will release the duck input, press the use input for 1 tick, and press the jump input. All other inputs will remain at their previous state.
 +
 
 
=== Commands ===
 
=== Commands ===
A string representing a console command or a set of console commands separated by semicolons. Everything typed here will be executed in the console.
+
A string representing a console command or multiple console commands separated by semicolons. Everything typed here will be executed in the console at the given tick.
  
 
''Example'': <code>say I hate this game; quit</code> will print "''I hate this game''" in game’s chat and turn the game off.
 
''Example'': <code>say I hate this game; quit</code> will print "''I hate this game''" in game’s chat and turn the game off.
Line 114: Line 123:
  
  
Everything connected together gives us an example of a complete framebulk:
+
Everything connected together gives us an example of a fully populated framebulk:
<pre>
+
{{TASBLOCK|20>0 1{{!}}5 0{{!}}dU1J{{!}}say I hate this game; quit{{!}}strafe max}}
20>0 1|5 0|dU1J|say I hate this game; quit|strafe max
+
 
</pre>
 
  
 
== Preserving rules ==
 
== Preserving rules ==
TAS script player will keep the previously set state of a virtual controller input if no information about it was given for current tick. This allows you to skip certain parts of a framebulk or even entire framebulks if you want specific input to continue for a specific amount of ticks.
+
The TAS script player will keep the previously set state of a virtual controller input if no information about it was given for current tick. This allows you to skip certain parts of a framebulk or even entire framebulks if you want specific input to continue for a specific amount of ticks.
  
  
Line 126: Line 134:
  
 
''Example 1'': There are no framebulks for ticks 11 to 19, the player will just continue going forward until the tick 20.
 
''Example 1'': There are no framebulks for ticks 11 to 19, the player will just continue going forward until the tick 20.
<pre>
+
{{TASBLOCK|...
...
+
10>0 1{{!}}0 0{{!}}jduzbo{{!}}{{!}}
10>0 1|0 0|jduzbo||
+
20>0 0{{!}}0 0{{!}}jduzbo{{!}}{{!}}
20>0 0|0 0|jduzbo||
 
 
...
 
...
</pre>
+
}}
  
 
''Example 2'': Framebulk 15 is missing information about button states. Jump will be held for that tick.
 
''Example 2'': Framebulk 15 is missing information about button states. Jump will be held for that tick.
<pre>
+
{{TASBLOCK|...
...
+
14>0 0{{!}}0 0{{!}}Jduzbo{{!}}{{!}}
14>0 0|0 0|Jduzbo||
+
15>0 0{{!}}0 0{{!}}{{!}}{{!}}
15>0 0|0 0|||
 
 
...
 
...
</pre>
+
}}
  
 
''Example 3'': Framebulk 21 has only movement field and 22 has only button field with lowercase d. Player will move for 11 ticks while trying to crouch, stop moving at tick 21 and stop crouching at tick 22.
 
''Example 3'': Framebulk 21 has only movement field and 22 has only button field with lowercase d. Player will move for 11 ticks while trying to crouch, stop moving at tick 21 and stop crouching at tick 22.
<pre>
+
{{TASBLOCK|...
...
+
10>0 1{{!}}{{!}}D
10>0 1||D
 
 
21>0 0
 
21>0 0
22>||d
+
22>{{!}}{{!}}d
 
...
 
...
</pre>
+
}}
  
 
Notice that, even though fields are not mandatory, you have to use the separator <code>|</code> characters to reach the correct field. Button field is the third one, so even if you don’t use movement and analog fields, you have to put two separators before it.
 
Notice that, even though fields are not mandatory, you have to use the separator <code>|</code> characters to reach the correct field. Button field is the third one, so even if you don’t use movement and analog fields, you have to put two separators before it.
Line 154: Line 159:
 
Additionally, commands and tools fields are not preserved. In other words, they won't be repeatedly executed every tick until the field is “cleaned up”. They will execute only once in a tick specified by a framebulk.
 
Additionally, commands and tools fields are not preserved. In other words, they won't be repeatedly executed every tick until the field is “cleaned up”. They will execute only once in a tick specified by a framebulk.
  
== Automation tools ==
 
Automation tools are controlled through the tools field, which works in a similar way to the commands field.
 
  
Tools work by modifying the view/movement analog and button fields in the framebulk every tick. The order in which the tools are evaluated is defined by the order they appear in the framebulk. For instance, if the tools field is <code>setang 10 20; strafe max</code>, the <code>setang</code> tool will be run before the <code>strafe</code> tool, which means the autostrafer is able to "predict" the angle change caused by the setang (which is important since angle changing is the first thing to happen in a tick). In general, angle-changing tools (<code>setang</code>, <code>autoaim</code>) should be placed before movement tools (<code>strafe</code>, <code>absmov</code>, <code>decel</code>) for the tools to work accurately.
+
== Tools bulk ==
 +
When TASing, you'll find yourself commonly writing framebulks that only contain information in the tools field. For instance:
 +
{{TASBLOCK|+7>{{!}}{{!}}{{!}}{{!}}strafe vec max 0deg}}
  
Here are the tools that are currently implemented:
+
To improve readability of such bulks, SAR version 1.12.7-pre4 introduces 'tools bulks', which only contain the tools field:
=== Strafe tool ===
+
'''{{TASBLOCK|tick>>tools}}'''
 +
 
 +
The previous example as a tool bulk would be:
 +
{{TASBLOCK|+7>>strafe vec max 0deg}}
 +
 
 +
 
 +
== Loops ==
 +
By putting a part of your script in a <code>repeat</code> block, you can repeat it X amount of times.
 +
 
 +
''Example'': This block will spam the use key ten times. The indentation is not required.
 +
{{TASBLOCK|repeat 10
 +
    +2>{{!}}{{!}}U1
 +
end
 +
}}
 +
These blocks can be nested.
 +
Also, keep in mind that both <code>repeat</code> and <code>end</code> keywords are case-sensitive (they have to be lower-case to work).
 +
 
 +
= Automation tools =
 +
Automation tools are controlled through the tools field in a framebulk, which works in a similar way to the commands field. They work by modifying the view/movement analog and button fields in the framebulk every tick.
 +
 
 +
== Tools execution order ==
 +
Since version 3, tools have static execution order: <code>check</code>, <code>setang</code>, <code>autoaim</code>, <code>autojump</code>, <code>absmov</code>, <code>strafe</code> and <code>decel</code>. For instance, if the tools field is <code>strafe max; setang 10 20</code>, the <code>setang</code> tool will be run before the <code>strafe</code> tool, which means the autostrafer is able to "predict" the angle change caused by the setang (which is important since angle changing is the first thing to happen in a tick). In general, angle-changing tools (<code>setang</code>, <code>autoaim</code>) are executed before the movement tools (<code>strafe</code>, <code>absmov</code>, <code>decel</code>) so they can work accurately.
 +
Before version 3, the order in which the tools were evaluated was determined by the order in which they were enabled in the script.
 +
 
 +
== Strafe tool ==
 
The <code>strafe</code> tool will adjust player input to get a different kind of strafing depending on parameters.
 
The <code>strafe</code> tool will adjust player input to get a different kind of strafing depending on parameters.
  
Line 187: Line 216:
 
''Other'':
 
''Other'':
 
* <code>nopitchlock</code> - Make the autostrafer not clamp the pitch. The autostrafer will always clamp your pitch angle (up and down) between -30 and 30 when midair, as it gives the fastest possible acceleration (forward movement is being scaled by a cosine of that angle while being airborne). This argument will tell the autostrafer that you wish to enable sub-optimal strafing (this is useful when you need to hit a shot while strafing for example).
 
* <code>nopitchlock</code> - Make the autostrafer not clamp the pitch. The autostrafer will always clamp your pitch angle (up and down) between -30 and 30 when midair, as it gives the fastest possible acceleration (forward movement is being scaled by a cosine of that angle while being airborne). This argument will tell the autostrafer that you wish to enable sub-optimal strafing (this is useful when you need to hit a shot while strafing for example).
 +
* <code>letspeedlock</code> - By default, autostrafer will attempt to not deviate too much right or left when the velocity is higher than or equal 300 ups, to avoid speedlock and keep acceleration at its maximum. Sometimes that behaviour is not wanted, in which case this parameter should be used.
 
'''''Examples''''':
 
'''''Examples''''':
 
* <code>strafe 299.999ups left veccam</code> - strafes left while facing current view angle and trying to reach the velocity of 299.999ups (the highest velocity you can get without being affected by the limited air control).
 
* <code>strafe 299.999ups left veccam</code> - strafes left while facing current view angle and trying to reach the velocity of 299.999ups (the highest velocity you can get without being affected by the limited air control).
 
* <code>strafe vec max forward</code> - strafes with maximum acceleration on a straight line without changing view angles.
 
* <code>strafe vec max forward</code> - strafes with maximum acceleration on a straight line without changing view angles.
=== Autojump tool ===
+
 
 +
 
 +
== Autojump tool ==
 
Autojump tool will change the jump button state depending on whether the player is grounded or not, resulting in automatically jumping on the earliest contact with a ground.
 
Autojump tool will change the jump button state depending on whether the player is grounded or not, resulting in automatically jumping on the earliest contact with a ground.
  
'''''Syntax''''': <code>autojump [on]</code>
+
'''''Syntax''''': <code>autojump [on|ducked]</code>
  
Anything other than <code>on</code> will disable the autojump.
+
Anything other than <code>on</code> or <code>ducked</code> will disable the autojump.
  
 
'''''Examples''''':
 
'''''Examples''''':
 
* <code>autojump on</code> - enables autojump.
 
* <code>autojump on</code> - enables autojump.
 +
* <code>autojump ducked</code> - enables autojump while also ducking. Ducking slightly increases your jump height.
 
* <code>autojump off</code> - disables autojump.
 
* <code>autojump off</code> - disables autojump.
=== Absolute movement tool ===
+
 
 +
== Absolute movement tool ==
 
Absolute movement tool will generate movement values depending on the absolute move direction you provide in degrees.
 
Absolute movement tool will generate movement values depending on the absolute move direction you provide in degrees.
  
Line 208: Line 242:
  
 
'''''Examples''''':
 
'''''Examples''''':
* <code>absmov 90</code> - enables absolute movement towards positive Y.
+
* <code>absmov 90deg</code> - enables absolute movement towards positive Y.
* <code>absmov 90 0.5</code> - enables absolute movement towards positive Y, but half as fast.
+
* <code>absmov 90deg 0.5</code> - enables absolute movement towards positive Y, but half as fast.
 
* <code>absmov off</code> - disables absolute movement.
 
* <code>absmov off</code> - disables absolute movement.
=== Set view angles tool ===
+
 
 +
 
 +
== Set view angles tool ==
 
This tool works basically the same as <code>setang</code> console command. It will adjust the view analog in a way so the camera is looking towards given angles.
 
This tool works basically the same as <code>setang</code> console command. It will adjust the view analog in a way so the camera is looking towards given angles.
  
'''''Syntax''''': <code>setang <pitch> <yaw> [time]</code>
+
'''''Syntax''''': <code>setang <pitch> <yaw> [time] [easing]</code>
  
The tool will set the pitch and yaw according to given angles in degrees. time specifies how long a transition to new angles takes, in ticks. If omitted, transition happens instantly.
+
The tool will set the pitch and yaw according to given angles in degrees. <code>time</code> specifies how long a transition to new angles takes, in ticks. If omitted, transition happens instantly. <code>easing</code> specifies which algorithm to use for interpolation. Options are <code>cubic</code>, <code>exp</code>/<code>exponential</code>, <code>linear</code> and <code>sin</code>/<code>sine</code>. If omitted, <code>linear</code> is used.
  
 
'''''Examples''''':
 
'''''Examples''''':
 
* <code>setang 69 42.0</code> - instantly rotates the camera to angles 69 42.
 
* <code>setang 69 42.0</code> - instantly rotates the camera to angles 69 42.
 
* <code>setang 0 0 20</code> - slowly rotates the camera towards angles 0 0 over the next 20 ticks.
 
* <code>setang 0 0 20</code> - slowly rotates the camera towards angles 0 0 over the next 20 ticks.
=== Auto aim tool ===
+
* <code>setang 90 0 10 exp</code> - rotates the camera the camera towards angles 90 0 over 10 ticks using the exponential easing.
 +
 
 +
== Auto aim tool ==
 
The Auto Aim tool will automatically aim towards a specified point in 3D space.
 
The Auto Aim tool will automatically aim towards a specified point in 3D space.
  
'''''Syntax''''': <code>autoaim <x> <y> <z> [time]</code>
+
'''''Syntax''''': <code>autoaim <x> <y> <z> [time] [easing]</code> OR <code>autoaim ent <name> [time] [easing]</code>
 +
 
 +
Giving <code>off</code> as an argument will disable the tool. Time specifies how long a transition to aiming at the point takes, in ticks. If omitted, transition happens instantly. When using this command to place portals, the command <code>portal_report</code> can be helpful. In other cases, the command <code>sar_aim_point_hud 1</code> is recommended. Added in SAR version 1.12.7-pre6, <code>autoaim ent <name></code> will aim at the absolute origin of a named entity. Added in SAR version 1.12.8-pre7, if multiple entities have the same name you can use <code>autoaim ent name[n]</code> (with square brackets) to select the n'th entity with that name or classname (starting at 0), or <code>autoaim ent <index></code> to aim at an entity with a certain index.
 +
 
 +
To find the name or index of an entity, do <code>ent_text</code> in console while looking at it. The first line is <code>(<index>) Name: <name> (<classname>)</code>
  
Giving <code>off</code> as an argument will disable the tool. Time specifies how long a transition to aiming at the point takes, in ticks. If omitted, transition happens instantly. When using this command to place portals, the command <code>portal_report</code> can be helpful. In other cases, the command <code>sar_aim_point_hud 1</code> is recommended.
+
The easing types are the same as the <code>setang</code> tool above
  
 
'''''Examples''''':
 
'''''Examples''''':
Line 232: Line 274:
 
* <code>autoaim 0 0 0 20</code> - aims at world origin, but smoothed over 20 ticks.
 
* <code>autoaim 0 0 0 20</code> - aims at world origin, but smoothed over 20 ticks.
 
* <code>autoaim 1000 2134 47381</code> - aims fucking nowhere.
 
* <code>autoaim 1000 2134 47381</code> - aims fucking nowhere.
=== Deceleration tool ===
+
* <code>autoaim ent red 10</code> - aims at P-Body, smoothed over 10 ticks.
 +
* <code>autoaim ent box[1] 20</code> - aims at the <strong>2nd</strong> entity with the name 'box', smoothed over 20 ticks.
 +
* <code>autoaim ent 20 30</code> - aims at entity index 20 (top cube on Laser Chaining), smoothed over 30 ticks.
 +
 
 +
== Deceleration tool ==
 
The deceleration tool will slow down as quickly as possible to the given speed.
 
The deceleration tool will slow down as quickly as possible to the given speed.
  
Line 240: Line 286:
  
 
'''''Examples''''':
 
'''''Examples''''':
* <code>decel 100</code> - decelerates until it reaches 100ups and then turns off.
+
* <code>decel 100ups</code> - decelerates until it reaches 100ups and then turns off.
 
* <code>decel off</code> - turns off the decel tool.
 
* <code>decel off</code> - turns off the decel tool.
=== Check tool ===
+
 
 +
 
 +
== Check tool ==
 
The check tool will check the player position and angle, and if it doesn't match what is specified in the arguments, it will automatically restart TAS playback. This is useful for inconsistent scripts, as the check tool can automatically retry TAS playback until it is successful. The cvar <code>sar_tas_check_max_replays</code> (default value <code>15</code>) defines how many times the check tool will restart playback until it gives up.
 
The check tool will check the player position and angle, and if it doesn't match what is specified in the arguments, it will automatically restart TAS playback. This is useful for inconsistent scripts, as the check tool can automatically retry TAS playback until it is successful. The cvar <code>sar_tas_check_max_replays</code> (default value <code>15</code>) defines how many times the check tool will restart playback until it gives up.
  
'''''Syntax''''': <code>check pos <x> <y> <z> ang <pitch> <yaw> posepsilon <pos value> angepsilon <ang value></code>
+
'''''Syntax''''': <code>check pos <x> <y> <z> ang <pitch> <yaw> posepsilon <pos value> angepsilon <ang value> holding [entity selector]</code>
  
 
*<code>x</code>, <code>y</code>, and <code>z</code> are the desired x, y, and z positions of the player (NOT the player camera).
 
*<code>x</code>, <code>y</code>, and <code>z</code> are the desired x, y, and z positions of the player (NOT the player camera).
 
*<code>pitch</code> and <code>yaw</code> are the desired pitch and yaw of the player.
 
*<code>pitch</code> and <code>yaw</code> are the desired pitch and yaw of the player.
 
*<code>pos value</code> and <code>ang value</code> are the deviation from actual player position that the check tool will tolerate. If left unspecified, default values will be <code>0.5</code> for <code>pos value</code> and <code>0.2</code> for <code>ang value</code>.
 
*<code>pos value</code> and <code>ang value</code> are the deviation from actual player position that the check tool will tolerate. If left unspecified, default values will be <code>0.5</code> for <code>pos value</code> and <code>0.2</code> for <code>ang value</code>.
 +
*<code>entity selector</code> is an optional specifier for which object the player should be holding. See the [[#Auto_aim_tool|Autoaim]] section for more information.
  
 
'''IMPORTANT NOTE:''' The check tool runs ''before'' all of the other tools and movement in the tick, so it effectively checks the player position and angle of the previous tick. That means that when you are getting your data for the check tool, you should get the data from TAS tick <code>n</code>, and then insert the check command on framebulk <code>n+1</code>.
 
'''IMPORTANT NOTE:''' The check tool runs ''before'' all of the other tools and movement in the tick, so it effectively checks the player position and angle of the previous tick. That means that when you are getting your data for the check tool, you should get the data from TAS tick <code>n</code>, and then insert the check command on framebulk <code>n+1</code>.
  
Not all arguments are required, and their order in the tool does not actually matter, however if you put them out of order then <code>p2tas-lang</code> will yell at you, kind of like <code>absmov 90deg</code> (SAR accepts it but it is technically bad syntax).
+
Not all arguments are required, and their order in the tool does not actually matter, however if you put them out of order then <code>p2tas-lang</code> will yell at you.
  
 
'''''Examples''''':
 
'''''Examples''''':
Line 260: Line 309:
 
*<code>142>||||check pos 4105.78 150.17 2617.03 ang 1.60 178.54 posepsilon 0.05 angepsilon 1</code>
 
*<code>142>||||check pos 4105.78 150.17 2617.03 ang 1.60 178.54 posepsilon 0.05 angepsilon 1</code>
 
**Checks if the player position in tick 141 is within <code>0.05</code> units of <code>4105.78, 150.17, 2617.03</code> and the player angle is within <code>1</code> degree of <code>1.60, 178.54</code>
 
**Checks if the player position in tick 141 is within <code>0.05</code> units of <code>4105.78, 150.17, 2617.03</code> and the player angle is within <code>1</code> degree of <code>1.60, 178.54</code>
 +
*<code>69>>check holding</code>
 +
**Check if the player is holding anything on tick 69
 +
*<code>420>>check holding box</code>
 +
**Check if the player is holding an entity named "box" (see <code>ent_text</code>) on tick 420
 +
 +
= Raw script =
 +
After each tools script playback (a playback of script which contains tools), a new script with the same name followed by <code>_raw</code> suffix is created in the script's directory, which is essentially a dump of all player inputs during TAS playback. This means that it should lead to the same result, but without using any tools. The playback of any script with <code>_raw</code> suffix will use simplified and more legitimate input injection which does not process any automation tool.
 +
 +
This has several purposes:
 +
* our current verification method - tools playback will inject inputs while they're processed, potentially making the game behave in an unintentional way, while raw playback will behave almost as it was a custom hardware plugged into the computer, injecting inputs while they're created.
 +
* raw player input generation, which we can potentially use in the future for better verification methods.
 +
* universal script format - raw script will yield similar results regardless of script version or operating system.
 +
 +
Once your TAS is finished, it is '''strongly''' recommended to record a demo of a raw script instead of the tools script.
 +
 +
= Script example =
 +
Here's an example of a full TAS script, getting out of the elevator in Triple Laser. Note that it's not necessarily the most optimal way to exit the elevator.
 +
{{TASBLOCK|version {{TASVER}}
 +
start map sp_a2_triple_laser
 +
0>{{!}}{{!}}{{!}}sar_trace_record 1
 +
 +
120>>absmov 0 // going to the back of the elevator
 +
192>>absmov 180 // going out of the elevator once it opens
 +
+7>>strafe max 180deg // we're going fast enough. Start strafing!
 +
 +
// Wait some time and duckjump so we can airstrafe
 +
+11>{{!}}{{!}}D{{!}}{{!}}autojump on
  
== Loops ==
+
/* wait a little bit and then stop trace recording
By putting a part of your script in a <code>repeat</code> block, you can repeat it X amount of times.
+
so we can see the result after TAS stops */
 +
+100>{{!}}{{!}}{{!}}sar_trace_record 0
 +
}}
 +
 
 +
= Other useful things =
 +
The TASing experience can be aided by a small number of other tools such as:
  
''Example'': This block will spam the use key ten times. The indentation is not required.
+
== Player Trace ==
<pre>
+
By running <code>sar_trace_draw 1</code> as well as placing <code>sar_trace_record 1</code> at the beginning of the TAS script and <code>sar_trace_record 0</code> at the end in the <code>commands</code> field, a line tracing the path of the player will be drawn in-game.
repeat 10
 
    +1>||U
 
    +1>||u
 
end
 
</pre>
 
These blocks can be nested.
 
Also, keep in mind that both <code>repeat</code> and <code>end</code> keywords are case-sensitive (they have to be lower-case to work).
 
  
== In-game commands ==
+
= In-game commands =
 
You can see a list of the TAS commands by playing <code>find sar_tas</code> in the in-game console, however here is a list of the main ones:
 
You can see a list of the TAS commands by playing <code>find sar_tas</code> in the in-game console, however here is a list of the main ones:
* <code>sar_tas_play <filename> [filename2] </code> - plays the given TAS script. If two filenames are given, play as a coop TAS. The first script is blue and the second is for orange.
+
* <code>sar_tas_play <filename> [filename2]</code> - plays the given TAS script. If two filenames are given, play as a coop TAS. The first script is blue and the second is for orange.
 
* <code>sar_tas_stop</code> - Stop the currently playing TAS.
 
* <code>sar_tas_stop</code> - Stop the currently playing TAS.
 
* <code>sar_tas_replay</code> - Replay the last played TAS.
 
* <code>sar_tas_replay</code> - Replay the last played TAS.
Line 285: Line 359:
 
* <code>sar_tas_pauseat <tick></code> - Automatically pause the TAS at the given tick. 0 disables pausing.
 
* <code>sar_tas_pauseat <tick></code> - Automatically pause the TAS at the given tick. 0 disables pausing.
  
== Other useful things ==
+
= Version history =
The TASsing experience can be aided by a small number of other tools such as:
+
As updates to the TASing tools and environment continue to be made, scripts made on previous versions should retain their functionality, i.e. breaking changes should be kept to an absolute minimum.
  
=== Player Trace ===
+
{| class="wikitable"
By running <code>sar_trace_draw 1</code> as well as placing <code>sar_trace_record 1</code> at the beginning of the TAS script and <code>sar_trace_record 0</code> at the end in the <code>commands</code> field, a line tracing the path of the player will be drawn in-game.
+
! Version number !! Changes
 +
|-
 +
|1||N/A
 +
|-
 +
|2||- fix a bug where strafe tool is suboptimal with manual jump inputs (e.g. <code>>&#124;&#124;J1</code>)
 +
|-
 +
|3||- static tool execution order - check, setang, autoaim, autojump, absmov, strafe, decel
 +
 
 +
- fix some Windows <-> Linux inconsistencies regarding <code>abs</code> overloads
 +
 
 +
- fix forwardmove/sidemove calculation for strafer
 +
 
 +
- disable veccam strafing when we reach the line
 +
|-
 +
|4||- add anti-speedlock functionality to autostrafer (disable with <code>letspeedlock</code>)
 +
 
 +
- fix more Windows <-> Linux inconsistencies (<code>sin</code>, <code>cos</code>, <code>atan2</code>, and <code>pow</code>)
 +
|-
 +
|5||- fix anti-speedlock on the ground
 +
|-
 +
|6||- handle veccam+setang correctly
 +
 
 +
- fix 'autostrafer line following problems'
 +
 
 +
- fix autostrafer behaviour at vel 0
 +
 
 +
- pitchlock to the correct sign
 +
|-
 +
|7||- fix autostrafer air control limit with <code>sar_aircontrol</code> set
 +
|}

Latest revision as of 21:03, 20 June 2024

Portal 2 TAS-ing

Introduction

This article serves as a complete documentation of the Portal 2 TASing environment implemented into SAR and the .p2tas scripting language used by the TAS playback. It also intends to be a guide for people interested in Portal 2 TASing.

Setup

  • Install SAR if you don't already have it.
  • Create a new folder called tas in your Portal 2 directory. This is where all your TAS scripts will live.
    • The script files will need to have a .p2tas extension and can be played using the command sar_tas_play <filename>. For example, if your script file is Portal 2/tas/mytas.p2tas, then to play it in-game you would use the command sar_tas_play mytas.

Visual Studio Code

The VS Code extension currently does not support the playback of coop TASes, so you will need to bind a key to 'sar_tas_replay' and start playback using 'sar_tas_play <filenameblue> <filenameorange>'

Thanks to the diligent coding of RainbowwPhoenixx, Soni, mlugg, and Cqnd, there is a custom language file that can be used with Visual Studio Code (a.k.a. VS Code) to greatly assist in editing .p2tas files. Some major features of this extension include:

  • Integration with SAR's TAS server for TAS playback control within VS Code.
  • Syntax hightlighting to make your scripts easier to read
  • Converting between absolute and relative framebulks
  • Keeping track of your active tools
  • Syntax error checking
  • And more!

Setting up Visual Studio Code with p2tas-lang

Once you have installed Visual Studio Code, you will want to install the p2tas-lang extension in order to use all of its features.

  1. On the left side of the main window of VS Code, there is an extensions menu that you can open (you can also hit ctrl+shift+x).
  2. In the top of the menu where it says "Search Extensions in Marketplace", type in p2tas-lang.
  3. Hit install.

Once installed, there will be a new button on the left side of the main window for TAS server integration, and you can select .p2tas as a file type and it should automatically apply p2tas-lang with all of its features. To use the TAS server integration with SAR, you will need to run the command sar_tas_server 1 in Portal 2 to enable the TAS server.

Note: sometimes there may be beta features for p2tas-lang that are still in development, and will therefore not yet available through the extensions marketplace. If that is the case, you may be able to find a .vsix file from one of the extension developers and install that manually. Make sure you only manually install extensions from trusted sources!

The .p2tas file format

The .p2tas format roughly follows the idea of .srctas, however, while .srctas focuses more on data-based representation of the script, .p2tas deviates into more declarative syntax.

Header

Every .p2tas script needs to start with a header, which will give TAS player essential info for script playback.

Version indicator

At the beginning of every script, you have to include a version <number> command. This number lets the TAS tools know which version of the tools to use in order to maintain compatibility with older scripts (this mostly affects the strafe tool).

The latest version number is 7.

Start commands

After the version command, you have to include a start command. It can have five parameters:

  • now: Starts playing the TAS immediately, or as soon as a session is started if you are not in one (i.e. in the main menu).
  • save <save_name>: Loads the given save and then immediately starts playing the TAS.
  • map <map_name>: Loads the given map and then immediately starts playing the TAS.
    • Internally, SAR uses changelevel to go to the map if the map is different than the current session, and restart_level if the map is the same as the current session. This has the potential to cause slight inconsistencies in how the map is loaded, so when making TASes it is common practice to ignore any outcomes that happen when you start your TAS from a different map, as they could be different from when you start the TAS on the same map.
  • cm <map_name>: Starts playing the TAS after loading into the given map, but in Challenge Mode.
    • The discrepancy listed above with start map also applies to start cm.
  • next <start type>: Emulates one of the above start types, but waits until the next session load to start TAS playback. If left blank, SAR presumes a start type of now.
    • SAR will wait until the next session load to start TAS playback, but in the case of save, map, and cm, SAR will immediately load the save/map requested. This means that SAR will load the save/map, wait until the next load, and then start TAS playback.
    • Due to a bug in how SAR handles TAS playback, when SAR is waiting for the next session load to start TAS playback, even though no TAS is playing, SAR is still in kind of a "half-TAS" state, and thus will make the game ignore your inputs. Due to this, the only currently useful implementation of start next is running start next save <save_name> with a save made just before a level transition. Starting your TASes this way results in level loading and game playback that is theoretically identical to the conditions you would find yourself in during a regular fullgame speedrun, making it the most theoretically legitimate way of making a TAS.

RNG manipulation

Since SAR version 1.12.7-pre6, it is possible to save some of the RNG-dependent elements in current session (gel spread, bridge bump camera shifting etc.) using command sar_rng_save. Then, an optional rngmanip <filepath> header line can be used to automatically inject these values before TAS starts playing. The path is relative to your main "Portal 2" directory.

In the future, this feature will be capable of recording and recreating all of the game's RNG, allowing us much greater playback consistency during the TASing process.

Header examples

Below you can see some examples of how a header file may look like in your script.

Loads Triple Laser and immediately starts the TAS:

version 7
start map sp_a2_triple_laser

Immediately loads the save tas_sp_a2_bts3 and starts the TAS:

version 7
start save tas_sp_a2_bts3

Loads the save future_starter_pre, waits until the next session load, then starts the TAS: In this example, the save future_starter_pre was made on the last tick of a TAS for the map Cube Momentum, meaning that it loads into a save on Cube Momentum, then a tick later the level transition completes, and then the TAS starts playback at the beginning of Future Starter. The end result looks very similar to start map sp_a1_intro6 (Future Starter), but is arguably "more legitimate".

version 7
start next save future_starter_pre


Comments

Comments are sections that will be ignored and can be expressed in 2 ways:

  • Single line comments with //
  • Multi-line comments starting with /* and ending with */

They are useful for adding notes in your script or to temporarily prevent a line from being executed.


Framebulk

Framebulks store the inputs and commands that should be executed by a virtual controller at a given tick. Every line that isn’t a header line, a repeat block (explained later) or a comment is considered to be a framebulk. They’re formatted like this:

tick>movement|angles|buttons|commands|tools

Below is an explanation of each field in a framebulk:

Tick

The number of ticks since the beginning of the TAS. This dictates when the framebulk will be executed. It can also be written with a + in front, in which case it will indicate the number of ticks compared to the previous framebulk instead.

Examples:

  • If a tick value is 20, the framebulk will be executed 20 ticks after TAS script has started.
  • If a tick value is +10, the framebulk will be executed 10 ticks after the previous framebulk.

Movement

Two floating point values from -1.0 and 1.0 representing horizontal and vertical movement analog values respectively.

Example: 0 1 as movement field will move the player forward.

Angles

Two floating point values representing horizontal and vertical view analog values respectively. The values correspond to view rotation in degrees in one tick.

Example: 5 0 as analog field will turn the player 5 degrees to the right in the given tick.

Buttons

A list of digital inputs that should be pressed or released. Each digital action has a letter assigned. These actions are Jump, Duck, Use, Zoom, Blue portal and Orange portal. Uppercase letter presses down the input, lowercase letter releases it.

Additionally, by putting a number after an uppercase button, you can specify a number of ticks after which it will be released automatically.

Examples:

  • D will press the duck input. d will release the duck input.
  • D8 will press and hold the duck input for 8 ticks, then release it.
  • dU1J will release the duck input, press the use input for 1 tick, and press the jump input. All other inputs will remain at their previous state.

Commands

A string representing a console command or multiple console commands separated by semicolons. Everything typed here will be executed in the console at the given tick.

Example: say I hate this game; quit will print "I hate this game" in game’s chat and turn the game off.

Tools

A string representing an automation tool command or a set of tool commands separated by semicolons. The usage of automation tools will be explained later.


Everything connected together gives us an example of a fully populated framebulk:

20>0 1|5 0|dU1J|say I hate this game; quit|strafe max


Preserving rules

The TAS script player will keep the previously set state of a virtual controller input if no information about it was given for current tick. This allows you to skip certain parts of a framebulk or even entire framebulks if you want specific input to continue for a specific amount of ticks.


Examples below are just random framebulks. Dots at the top and the bottom are there to show that it’s only a part of a full script, and you should think about these examples as such.

Example 1: There are no framebulks for ticks 11 to 19, the player will just continue going forward until the tick 20.

...
10>0 1|0 0|jduzbo||
20>0 0|0 0|jduzbo||
...

Example 2: Framebulk 15 is missing information about button states. Jump will be held for that tick.

...
14>0 0|0 0|Jduzbo||
15>0 0|0 0|||
...

Example 3: Framebulk 21 has only movement field and 22 has only button field with lowercase d. Player will move for 11 ticks while trying to crouch, stop moving at tick 21 and stop crouching at tick 22.

...
10>0 1||D
21>0 0
22>||d
...

Notice that, even though fields are not mandatory, you have to use the separator | characters to reach the correct field. Button field is the third one, so even if you don’t use movement and analog fields, you have to put two separators before it.

Additionally, commands and tools fields are not preserved. In other words, they won't be repeatedly executed every tick until the field is “cleaned up”. They will execute only once in a tick specified by a framebulk.


Tools bulk

When TASing, you'll find yourself commonly writing framebulks that only contain information in the tools field. For instance:

+7>||||strafe vec max 0deg

To improve readability of such bulks, SAR version 1.12.7-pre4 introduces 'tools bulks', which only contain the tools field:

tick>>tools

The previous example as a tool bulk would be:

+7>>strafe vec max 0deg


Loops

By putting a part of your script in a repeat block, you can repeat it X amount of times.

Example: This block will spam the use key ten times. The indentation is not required.

repeat 10
    +2>||U1
end

These blocks can be nested. Also, keep in mind that both repeat and end keywords are case-sensitive (they have to be lower-case to work).

Automation tools

Automation tools are controlled through the tools field in a framebulk, which works in a similar way to the commands field. They work by modifying the view/movement analog and button fields in the framebulk every tick.

Tools execution order

Since version 3, tools have static execution order: check, setang, autoaim, autojump, absmov, strafe and decel. For instance, if the tools field is strafe max; setang 10 20, the setang tool will be run before the strafe tool, which means the autostrafer is able to "predict" the angle change caused by the setang (which is important since angle changing is the first thing to happen in a tick). In general, angle-changing tools (setang, autoaim) are executed before the movement tools (strafe, absmov, decel) so they can work accurately. Before version 3, the order in which the tools were evaluated was determined by the order in which they were enabled in the script.

Strafe tool

The strafe tool will adjust player input to get a different kind of strafing depending on parameters.

Syntax: strafe [parameters]

Possible parameters:

Strafing type control:

  • none or off - disables strafing entirely.
  • vec - enables vectorial strafing (movement analog is adjusted to get desired movement direction). (default)
  • ang - enables angular strafing (view analog is adjusted to get desired movement direction). This isn't particularly recommended as it doesn't look appealing, however it is the only effective strafing type while on velocity gel.
  • veccam - enables special vectorial strafing that rotates you towards your current moving direction.

Velocity control:

  • max - makes autostrafer aim for the greatest acceleration. (default)
  • keep - makes autostrafer maintain the current velocity.
  • [number]ups - sets a target velocity of [number] units per second for the autostrafer

Strafe direction control:

  • forward - autostrafer will try to strafe in a straight line, towards the current view angle. (default)
  • forwardvel - autostrafer will try to strafe in a straight line, towards the current velocity angle.
  • left - autostrafer will try to strafe left
  • right - autostrafer will try to strafe right
  • [number]deg - sets a target yaw angle of [number] degrees autostrafer should strafe towards.

Other:

  • nopitchlock - Make the autostrafer not clamp the pitch. The autostrafer will always clamp your pitch angle (up and down) between -30 and 30 when midair, as it gives the fastest possible acceleration (forward movement is being scaled by a cosine of that angle while being airborne). This argument will tell the autostrafer that you wish to enable sub-optimal strafing (this is useful when you need to hit a shot while strafing for example).
  • letspeedlock - By default, autostrafer will attempt to not deviate too much right or left when the velocity is higher than or equal 300 ups, to avoid speedlock and keep acceleration at its maximum. Sometimes that behaviour is not wanted, in which case this parameter should be used.

Examples:

  • strafe 299.999ups left veccam - strafes left while facing current view angle and trying to reach the velocity of 299.999ups (the highest velocity you can get without being affected by the limited air control).
  • strafe vec max forward - strafes with maximum acceleration on a straight line without changing view angles.


Autojump tool

Autojump tool will change the jump button state depending on whether the player is grounded or not, resulting in automatically jumping on the earliest contact with a ground.

Syntax: autojump [on|ducked]

Anything other than on or ducked will disable the autojump.

Examples:

  • autojump on - enables autojump.
  • autojump ducked - enables autojump while also ducking. Ducking slightly increases your jump height.
  • autojump off - disables autojump.

Absolute movement tool

Absolute movement tool will generate movement values depending on the absolute move direction you provide in degrees.

Syntax: absmov <angle> [strength]

Giving off as an argument will disable the tool. The strength parameter must be between 0 and 1 (default) and controls how fast the player will move.

Examples:

  • absmov 90deg - enables absolute movement towards positive Y.
  • absmov 90deg 0.5 - enables absolute movement towards positive Y, but half as fast.
  • absmov off - disables absolute movement.


Set view angles tool

This tool works basically the same as setang console command. It will adjust the view analog in a way so the camera is looking towards given angles.

Syntax: setang <pitch> <yaw> [time] [easing]

The tool will set the pitch and yaw according to given angles in degrees. time specifies how long a transition to new angles takes, in ticks. If omitted, transition happens instantly. easing specifies which algorithm to use for interpolation. Options are cubic, exp/exponential, linear and sin/sine. If omitted, linear is used.

Examples:

  • setang 69 42.0 - instantly rotates the camera to angles 69 42.
  • setang 0 0 20 - slowly rotates the camera towards angles 0 0 over the next 20 ticks.
  • setang 90 0 10 exp - rotates the camera the camera towards angles 90 0 over 10 ticks using the exponential easing.

Auto aim tool

The Auto Aim tool will automatically aim towards a specified point in 3D space.

Syntax: autoaim <x> <y> <z> [time] [easing] OR autoaim ent <name> [time] [easing]

Giving off as an argument will disable the tool. Time specifies how long a transition to aiming at the point takes, in ticks. If omitted, transition happens instantly. When using this command to place portals, the command portal_report can be helpful. In other cases, the command sar_aim_point_hud 1 is recommended. Added in SAR version 1.12.7-pre6, autoaim ent <name> will aim at the absolute origin of a named entity. Added in SAR version 1.12.8-pre7, if multiple entities have the same name you can use autoaim ent name[n] (with square brackets) to select the n'th entity with that name or classname (starting at 0), or autoaim ent <index> to aim at an entity with a certain index.

To find the name or index of an entity, do ent_text in console while looking at it. The first line is (<index>) Name: <name> (<classname>)

The easing types are the same as the setang tool above

Examples:

  • autoaim 0 0 0 - aims at world origin.
  • autoaim 0 0 0 20 - aims at world origin, but smoothed over 20 ticks.
  • autoaim 1000 2134 47381 - aims fucking nowhere.
  • autoaim ent red 10 - aims at P-Body, smoothed over 10 ticks.
  • autoaim ent box[1] 20 - aims at the 2nd entity with the name 'box', smoothed over 20 ticks.
  • autoaim ent 20 30 - aims at entity index 20 (top cube on Laser Chaining), smoothed over 30 ticks.

Deceleration tool

The deceleration tool will slow down as quickly as possible to the given speed.

Syntax: decel <speed>

Giving off as an argument will disable the tool.

Examples:

  • decel 100ups - decelerates until it reaches 100ups and then turns off.
  • decel off - turns off the decel tool.


Check tool

The check tool will check the player position and angle, and if it doesn't match what is specified in the arguments, it will automatically restart TAS playback. This is useful for inconsistent scripts, as the check tool can automatically retry TAS playback until it is successful. The cvar sar_tas_check_max_replays (default value 15) defines how many times the check tool will restart playback until it gives up.

Syntax: check pos <x> <y> <z> ang <pitch> <yaw> posepsilon <pos value> angepsilon <ang value> holding [entity selector]

  • x, y, and z are the desired x, y, and z positions of the player (NOT the player camera).
  • pitch and yaw are the desired pitch and yaw of the player.
  • pos value and ang value are the deviation from actual player position that the check tool will tolerate. If left unspecified, default values will be 0.5 for pos value and 0.2 for ang value.
  • entity selector is an optional specifier for which object the player should be holding. See the Autoaim section for more information.

IMPORTANT NOTE: The check tool runs before all of the other tools and movement in the tick, so it effectively checks the player position and angle of the previous tick. That means that when you are getting your data for the check tool, you should get the data from TAS tick n, and then insert the check command on framebulk n+1.

Not all arguments are required, and their order in the tool does not actually matter, however if you put them out of order then p2tas-lang will yell at you.

Examples:

  • 60>||||check pos 315 598 2145
    • Checks if the player position in tick 59 is within 0.5 (default) units of 315, 598, 2145.
  • 142>||||check pos 4105.78 150.17 2617.03 ang 1.60 178.54 posepsilon 0.05 angepsilon 1
    • Checks if the player position in tick 141 is within 0.05 units of 4105.78, 150.17, 2617.03 and the player angle is within 1 degree of 1.60, 178.54
  • 69>>check holding
    • Check if the player is holding anything on tick 69
  • 420>>check holding box
    • Check if the player is holding an entity named "box" (see ent_text) on tick 420

Raw script

After each tools script playback (a playback of script which contains tools), a new script with the same name followed by _raw suffix is created in the script's directory, which is essentially a dump of all player inputs during TAS playback. This means that it should lead to the same result, but without using any tools. The playback of any script with _raw suffix will use simplified and more legitimate input injection which does not process any automation tool.

This has several purposes:

  • our current verification method - tools playback will inject inputs while they're processed, potentially making the game behave in an unintentional way, while raw playback will behave almost as it was a custom hardware plugged into the computer, injecting inputs while they're created.
  • raw player input generation, which we can potentially use in the future for better verification methods.
  • universal script format - raw script will yield similar results regardless of script version or operating system.

Once your TAS is finished, it is strongly recommended to record a demo of a raw script instead of the tools script.

Script example

Here's an example of a full TAS script, getting out of the elevator in Triple Laser. Note that it's not necessarily the most optimal way to exit the elevator.

version 7
start map sp_a2_triple_laser
0>|||sar_trace_record 1

120>>absmov 0 // going to the back of the elevator
192>>absmov 180 // going out of the elevator once it opens
+7>>strafe max 180deg // we're going fast enough. Start strafing!

// Wait some time and duckjump so we can airstrafe
+11>||D||autojump on

/* wait a little bit and then stop trace recording
so we can see the result after TAS stops */
+100>|||sar_trace_record 0

Other useful things

The TASing experience can be aided by a small number of other tools such as:

Player Trace

By running sar_trace_draw 1 as well as placing sar_trace_record 1 at the beginning of the TAS script and sar_trace_record 0 at the end in the commands field, a line tracing the path of the player will be drawn in-game.

In-game commands

You can see a list of the TAS commands by playing find sar_tas in the in-game console, however here is a list of the main ones:

  • sar_tas_play <filename> [filename2] - plays the given TAS script. If two filenames are given, play as a coop TAS. The first script is blue and the second is for orange.
  • sar_tas_stop - Stop the currently playing TAS.
  • sar_tas_replay - Replay the last played TAS.
  • sar_tas_pause - Pause the TAS playback.
  • sar_tas_advance - Advances TAS playback by 2 ticks in singleplayer and 1 tick in coop.
  • sar_tas_resume - Resume TAS playback.
  • sar_tas_skipto <tick> - Fast forwards the TAS up to the given tick. 0 disables fast-forward.
  • sar_tas_pauseat <tick> - Automatically pause the TAS at the given tick. 0 disables pausing.

Version history

As updates to the TASing tools and environment continue to be made, scripts made on previous versions should retain their functionality, i.e. breaking changes should be kept to an absolute minimum.

Version number Changes
1 N/A
2 - fix a bug where strafe tool is suboptimal with manual jump inputs (e.g. >||J1)
3 - static tool execution order - check, setang, autoaim, autojump, absmov, strafe, decel

- fix some Windows <-> Linux inconsistencies regarding abs overloads

- fix forwardmove/sidemove calculation for strafer

- disable veccam strafing when we reach the line

4 - add anti-speedlock functionality to autostrafer (disable with letspeedlock)

- fix more Windows <-> Linux inconsistencies (sin, cos, atan2, and pow)

5 - fix anti-speedlock on the ground
6 - handle veccam+setang correctly

- fix 'autostrafer line following problems'

- fix autostrafer behaviour at vel 0

- pitchlock to the correct sign

7 - fix autostrafer air control limit with sar_aircontrol set