TASing
Contents
Introduction
This article serves as a complete documentation of Portal 2 TASing environment implemented into SAR and scripting language .p2tas
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 yourPortal 2
directory. This is where all your TAS scripts will live. Note: This has to go in the rootPortal 2
directory, not theportal2
directory inside it.- The script files will need to have a
.p2tas
extension and can be played using the commandsar_tas_play <filename>
. For example, if your script file isPortal 2/tas/mytas.p2tas
, then to play it in-game you would use the commandsar_tas_play mytas
.
- The script files will need to have a
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, 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.
- 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).
- In the top of the menu where it says "Search Extensions in Marketplace", type in
p2tas-lang
. - 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 as of July 12th, 2022 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, andrestart_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.
- Internally, SAR uses
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 tostart cm
.
- The discrepancy listed above with
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 ofnow
.- SAR will wait until the next session load to start TAS playback, but in the case of
save
,map
, andcm
, 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 runningstart 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.
- SAR will wait until the next session load to start TAS playback, but in the case of
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
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.
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
, abmsov
, 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
oroff
- 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 leftright
- 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]
Anything other than on
will disable the autojump.
Examples:
autojump on
- enables autojump.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]
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.
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.
Auto aim tool
The Auto Aim tool will automatically aim towards a specified point in 3D space.
Syntax: autoaim <x> <y> <z> [time]
OR autoaim ent <name> [time]
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.
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.
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>
x
,y
, andz
are the desired x, y, and z positions of the player (NOT the player camera).pitch
andyaw
are the desired pitch and yaw of the player.pos value
andang value
are the deviation from actual player position that the check tool will tolerate. If left unspecified, default values will be0.5
forpos value
and0.2
forang value
.
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 of315, 598, 2145
.
- Checks if the player position in tick 59 is within
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 of4105.78, 150.17, 2617.03
and the player angle is within1
degree of1.60, 178.54
- Checks if the player position in tick 141 is within
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 | - fixed a bug where strafe tool is suboptimal with manual jump inputs (e.g. >||J1 )
|
3 | - static tool execution order - check, setang, autoaim, autojump, abmsov, strafe, decel
- fixed some Windows <-> Linux inconsistencies regarding - fix forwardmove/sidemove calculation for strafer - disable veccam strafing when we reach the line |
4 | - add anti-speedlock functionality to autostrafer
- fix more Windows <-> Linux inconsistencies ( |
5 | - fix anti-speedlock on the ground |