In this article, you will learn what is TAS and how to TAS with using BunnymodXT and bxt-rs
There are some TAS guides out there under video format that you must check out:
Tool-assisted speedrun (TAS) is speedrunning assisted by some tools. TAS runs are not considered legit. TAS runs are considered showcase runs, demonstrating limits of the game or just typical fun of speedrun. Here are some examples of GoldSrc TAS:
[TAS] Half-Life - Surface Tension in 1:00.839: A featured chapter in Half-Life
Opposing Force Boot Camp TAS in 8:26.363: A non-campaign run in Opposing Force
Half-Life: Blue Shift Cutsceneless TAS by Snok in 11:34.648: A complete campaign run of Blue Shift
[TAS] Counter-Strike - de_nuke B rush: A showcase route in Counter-Strike
To make TAS, you need to have BunnymodXT and bxt-rs. The bxt-rs requirement is fairly recent. Out of the 4 videos listed above in What Is TAS?, 3 of them are made with just BunnymodXT. So what are these tools?
BunnymodXT is a Half-Life speedrunning tool that slowly feature creeps into having a scripted run executor of .hltas format.
bxt-rs is a Half-Life video recording tool that slowly feature creeps into having an interactive 3D environment integrated with BunnymodXT scripted run executor.
At the beginning, you could only make TAS with BunnymodXT. The process was to manually manipulating a .hltas file containing data related to strafing, yaw, pitch, frame count, etc... of the current TAS with a text editor. Afterward, you must manually reload and replay the TAS on every single changes. After some updates, an interactive 3D environment integrated with BunnymodXT scripted run executor is introduced inside BunnymodXT. And after some time later, an interacted 3D environment integrated with Bunnymod scripted run executor is introduced inside bxt-rs. This is where we are.
The setup is to have BunnymodXT as the backend executing scripted run and bxt-rs as the frontend interactively modify the scripted run.
As far as we know, BunnymodXT and bxt-rs are the only GoldSrc TAS tool available publicly.
Head to BunnymodXT and bxt-rs repository and follow instructions there to set them up.
It is possible to create TAS with just BunnymodXT. However, the interactive editor in BunnymodXT is deprecated in favor of bxt-rs version. On top of that, bxt-rs version has a lots new features that you won't miss from BunnymodXT.
Introduction chapter has two critical resources that you should check out before proceeding further. Monkey see, monkey do. Here is another TAS stream to up your monkey business.
The following information will assume the combo of BunnymodXT and bxt-rs and the intention to use bxt-rs as the interactive editor.
When you are confused about the usage of commands related to TASing, you can always check out bxt-rs wiki and BunnymodXT wiki
There are some basic key terms mentioned around. Here are the definitions.
-bridged.hltas file.hltas file.hltasproj filebxt_tas_studio_new <filename> <starting command> <FPS>
This is the command to start everything. If you are new, you should always start small and try doing a movement-only run such as AG or KZ.
Example:
bxt_tas_studio_new full_game "map c1a0" 100
Remember to put map <mapname> for the "starting command" so that you can load into the map after executing the command.
After executing the command, you will load into the map and possibly greeted with a green line in front of you on the ground while your camera view is floating in the air.
In the background, you should have <filename>.hltasproj and <filename>-bridged.hltas inside your Half-Life folder. These two files will be the TAS data and you wouldn't want to lose them.
.hltasproj is the SQLite file that stores everything related to the TAS. You should not touch this file.
-briged.hltas is the "communication" file between your text editor and the TAS. It is a bxt-rs only feature. The use case is that you can opt-in manually editting your TAS with text editor beside the in-game interactive editor. You might find it useful when the time arises. You can touch this file. But you should know whatever changes you bring to this file, it will be pushed onto your TAS. -briged.hltas is derived from the .hltasproj file so it is safe to lose it or delete it.
The final step to TAS startup is to launch another instance of the game. Yes, that is right. You need to run two instances of Half-Life to TAS. You can find the explanation in this video TASing Updates: Two Games & Camera Editor. In short, you have your main game that is running the interactive editor and your second game to execute the run. Your main game spends most of its time giving you interactive interaction to interact with the run so it cannot execute the run. Hence, you need the second game to execute the run.
To launch another instance of the game, you just need to start the game like how you did with your first game. Because TASing requires bxt-rs, you can only launch the game through the command prompt. This means you need to start another instance of command prompt to start another instance of Half-Life.
After starting another instance, it will automagically be picked up by the main game as the second game. You don't have to do anything beyond starting the second instance.
To verify that this works, check whether your second game is inside the map that is specified inside the your TAS run.
In case the second game crashes, you can just start the second game again. It just works.
There are some extra console variables you have to set to smoothly create TAS run for Counter-Strike 1.6.
You need to add these cvars in your TAS file.
bxt_anglespeed_cap 0; bxt_speed_scaling 0
This could be added at the header where load_command line is or inside a frame bulk. There is no easy way to do this and you have to open the bridge file with a text editor. There is an alternative and that is to hover over a frame in the editor and enter bxt_tas_studio_set_commands <the commands>. But overall, editing the bridge file might be a lot easier.
In the case of appending these cvars to your load_command, the result looks like this.
version 1
hlstrafe_version 5
load_command map fastrun; bxt_anglespeed_cap 0; bxt_speed_scaling 0
frames
target_yaw velocity_lock
----------|------|------|0.010000001|-|-|1|bxt_timer_reset
...
When you start the server with the map command, no game mode is enabled. This means you spawn without weapon and options to select side and characters and buy weapons and do objectives. To properly start the game mode, do deathmatch 1 before starting the map or add that cvar in the load_command line. With that, the game will start like how you would expect it to start.
There is an alternative way to start the game without deathmatch 1 and that is to enable sv_cheats 1 and then bxt_give weapon_usp and alikes to mimic general issued weapons. The game mode is not enabled but you can start making movement right away.
It is crucial that you play around with the TAS tool to get yourself proficient. There is not a lot of things could be said about making TAS except some tips and tricks that you can check later.
It is recommended that your first TAS is an HLKZ TAS. Movement focused TAS provides a good starting point to understand many concepts and core bases of TASing.
TASing is a simple loop of these steps.
When you first load the TAS, the only interaction you have with the TAS editor is using your mouse, left clicking to extend and right clicking to drag. Now, you must familiarize yourself with some BXT TAS commands. Simply typing bxt_tas_studio_toggle in the console will give you all of the available keys that you can bind to efficiently change a frame bulk behavior.
Usage: bxt_tas_studio_toggle <what>
Toggles a value on the selected frame bulk.
Values that you can toggle:
- s03: speed increasing strafing
- s13: quick turn strafing
- s22: slow down strafing
- s00: speed increasing strafing to the left
- s01: speed increasing strafing to the right
- s10: quick turn strafing to the left
- s11: quick turn strafing to the right
- s06: left-right strafing
- s07: right-left strafing
- s40: constant turn rate to the left
- s41: constant turn rate to the right
- s50: accelerated turn to the left
- s51: accelerated turn to the right
- lgagst: makes autojump and ducktap trigger at optimal speed
- autojump
- ducktap
- jumpbug
- dbc: duck before collision
- dbcceilings: duck before collision, including ceilings
- dbg: duck before ground
- dwj: duck when jump (useful for the long-jump module)
- forward: +forward
- left: +moveleft
- right: +moveright
- back: +back
- up: +moveup
- down: +movedown
- jump: +jump
- duck: +duck
- use: +use
- attack1: +attack1
- attack2: +attack2
- reload: +reload
Of all toggleables, what you should pay attention to is s03, autojump, ducktap, dbc, dbcceilings, and lgagst. These are the most basic toggles you need to create a run.
One example keybind will look like following:
bind f11 "bxt_tas_studio_toggle s03"
Over the course of making your TAS, you will find yourself wanting to bind more and more keys. It is a good thing. You should make yourself comfortable with your editor environment.
Here is an example of a bind config.
bind f11 "bxt_tas_studio_toggle autojump"
bind f12 "bxt_tas_studio_toggle ducktap"
bind f6 "bxt_tas_studio_toggle s00"
bind f7 "bxt_tas_studio_toggle s01"
bind f5 "bxt_tas_studio_toggle dbg"
bind f10 "bxt_tas_studio_toggle dbc"
bind f9 "bxt_tas_studio_toggle lgagst"
bind f8 "bxt_tas_studio_toggle s22"
bind \ "bxt_tas_studio_set_stop_frame"
bind ENTER "bxt_tas_studio_replay"
bind [ "bxt_tas_studio_split"
bind DEL "bxt_tas_studio_delete_last"
bind ] "bxt_tas_studio_toggle s03"
bind o "bxt_tas_studio_toggle s07"
alias camera_toggle "camera_toggle_on"
alias camera_toggle_on "bxt_tas_studio_camera_editor 1; alias camera_toggle camera_toggle_off"
alias camera_toggle_off "bxt_tas_studio_camera_editor 0; alias camera_toggle camera_toggle_on"
bind f1 camera_toggle
bind f2 "+bxt_tas_studio_insert_camera_line"
bind f3 "_bxt_tas_studio_smooth_globally "
bind ctrl "+bxt_tas_studio_look_around"
bind z bxt_tas_studio_undo
bind x bxt_tas_studio_redo
bind 1 "bxt_tas_studio_toggle s50"
bind 2 "bxt_tas_studio_toggle s51"
bind 3 "bxt_tas_studio_optim_init"
bind 4 "bxt_tas_optim_start"
bind 5 "bxt_tas_optim_reset"
bind 6 "bxt_tas_optim_disable"
bind 7 "bxt_tas_studio_optim_apply"
bind f4 "cl_forwardspeed 400; cl_sidespeed 400; cl_backspeed 400; sensitivity 0.7; bxt_tas_studio_close; bxt_cam_clear"
bind alt +alt1
bind shift +duck
There are a lot of data coming in to decide what a bulk does. Current speed, current position, what is the frame count, what is the strafe type, etc... Most of these cvars are available in BunnymodXT. Check out bxt_hud_... cvars to see what you want to see.
Here is an example config that enables some helpful HUDs.
bxt_hud_jumpspeed 1
bxt_hud_jumpdistance 1
bxt_hud_timer 1
bxt_hud_origin 1
bxt_hud_velocity 1
bxt_hud_stamina 1
bxt_hud_tas_studio 1
bxt_hud_distance 1
bxt_hud_viewangles 1
bxt_hud_entity_info 1
By default, loading a TAS will enable TAS editor HUD. It might be everything you want. You should see it for youself.
(insert some images here)
You will use your keyboard to change move around, enable mouse look, and toggle frame bulk. Any toggle keybinds you press will operate on the selected bulk.
You need to have these key binds to comfortably, or at the very least, ably, use the editor.
+bxt_tas_studio_look_around to look around. When you move your mouse in the editor, your camera doesn't move. Instead, you have a cursor to select a frame bulk and do some operations over it.
+alt1 to move faster. You move around with WASD key. Holding down this +alt1 key will get you go faster.
+duck to move slower.
While not toggling mouse look, you will have a mouse cursor to select bulks.
During adjustment (click hold), you can +alt1 or +duck to increase or decrease adjustment speed.
There is no "save button" in TAS editor. Every operation is saved into the .hltasproj file automatically.
As mentioned, in side the interactive editor, two most basic operations you can do and you will always be doing is to extend bulk and adjust yaw with your mouse. Here are some other basic operations that will help you create the perfect TAS.
bxt_tas_studio_split: splits a bulk. You can split a bulk into two with this command and change them however you likebxt_tas_studio_delete_last: deletes the very last bulk in the runbxt_tas_studio_delete: deletes the selected bulkbxt_tas_studio_toggle. They will manipulate the bulk and does the things they are supposed to doOne big feature of bxt-rs interactive editor and newer TAS format is that you can undo and redo, infinitely.
bxt_tas_studio_undo: undoes your mistakebxt_tas_studio_redo: redoes your undoThis is very powerful feature. You should have zero fear messing things up.
It is also possible to directly manipulate the TAS through the bridge file with your text editor. This is usually more ergonomic when adding custom commands such as starting/stopping timer, recording demo, and plethora of other things.
To see what a TAS file looks like under the hood, simply open the bridge file with a text editor.
This is a fairly advanced way to make a TAS. You should refer to .hltas reference documentation to see how to manually edit a TAS.
The bridge file is made very convenient. Make some changes then save the file. Your changes will update immediately inside the interactive editor. Make sure to have the correct syntax.
With two-game setup, every changes you create will run through the second game. So, there is a brief delay to have the accurate frame bulks.
This is presented by the brightness of the frame bulk. For bulks that are correctly predicted and simulated, they would have bright green color. For bulks that are in progress of being simulated, they will have dimmed green color. This simulation takes time. For a very long TAS, it will take longer to simulate.
Very often, the second game will crash. Make sure that you notice it. Even though the main game shows "correct path", it is only predicted, not simulated.
After you have assembled some bulks to do some coherent movement (and hopefully to beat the map), you want to see what you have.
bxt_tas_studio_replay: plays back the TAS by executing itbxt_tas_studio_set_stop_frame: sets the frame to stop executing. You can hover over any frame and enter this command. It is recommeded to make it a bind. You can supply an argument to this command to precisely set where the stop frame isbxt_tas_norefresh_until_last_frames <frame count>: sets start frame counting from stop frame to display. This hides the playback until the start frame. It is for faster TAS executionAfter seeing the fruit of your labor, you can work on it again and refine it however you want.
It is time to post your work on #goldsrc-tas. For general recording advices, refer to (link to the recording article here).
TAS recording isn't much different but you have an option to record it while it is executing. You might find it useful once you want to create a campaign run.
For the sake of simplicity, you are making an HLKZ TAS. So, you need to record a demo of the run.
The first thing you have to do is to close the second game. If you don't do this, you will record the demo twice. Second game demo will override your demo and the result is you have a broken demo.
Ok, now you can record a demo. Historically, you add demo <demoname> line at the beginning of the TAS and then set bxt_tas_norefresh_until_last_frames to 0. But it is not very flexible. The good way to do it is to put the record <demoname> directly into one frame bulk to start recording and then stop in another to stop recording. Very often, record command does not work but bxt_record works. Your milage may vary.
But how do you add the demo recording command? This is more of a trick for your convenience. After closing the second game, you also need to close the TAS with bxt_tas_studio_close or just simply restart your game. As mentioned, the bridge file is derived from the TAS project file. When you edit the bridge file, the TAS project file is updated. However, upon a TAS load, TAS project file will overwrite the bridge file. That means, the bridge file you have when the TAS project file is not loaded is just a normal TAS file. Any changes you make inside that TAS file does not affect your progress inside the TAS project file. So, you can safely make changes to the bridge file and no need to worry about ruining your perfect TAS.
Here is an example change to record a demo
version 1
hlstrafe_version 5
load_command map fastrun; bxt_anglespeed_cap 0; bxt_speed_scaling 0
frames
target_yaw velocity_lock
----------|------|------|0.010000001|-|-|1|bxt_timer_reset
----------|------|------|0.010000001|-|-|100|
----------|------|------|0.010000001|-|-|1|record my_first_demo
...
----------|------|------|0.010000001|-|-|100|
----------|------|------|0.010000001|-|-|1|stop
----------|------|------|0.010000001|-|-|100|
After that, you can load (or more precisely, execute) the TAS file .hltas instead of the TAS project file .hltasproj. bxt_tas_loadscript <script.hltas> to load the TAS file.
Make sure that bxt_tas_norefresh_until_last_frames is set to 0 so it plays back the whole TAS. You can do bxt_tas_playback_speed 0 to playback the TAS as fast as possible.
Now you have a demo file .dem. Refer to bxt-rs recording (link to article) to record a demo.
If you want to record the run live, you can add bxt-rs recording commands directly into the bulk like how it is done with demo recording commands.
The key point here is our TASers are AG players, they’re not speedrunners. They’re typically, fairly young, fresh out of fastrun, probably got some top15, maybe learned KZ or surf, probably learned game physics. They’re not capable of understanding a brilliant TAS but we want to use global smoothing to build good TAS. So, the global smoothing that we give them has to be easy for them to understand and easy to adopt.
Camera smoothing makes your movement TAS looking a lot better. There are some examples you can checkout:
CS 1.6 Surf TAS Compilation Vol. 1: CS1.6 Surf TAS
[TAS] bhop_m_novice in 22.220: Your typical HLKZ TAS
If you don't smooth your TAS, they might look like this:
[CS 1.6 TAS] deathrun_4rooms_fg (timestamp 1:34): 100 AA TAS with wiggling movement
[CS 1.6 TAS] dyd_axn_plant in 40.16: 10 AA TAS with wiggling movement
Simply put, if you want your movement TAS to look good, you need to smooth your camera.
After having your TAS simulated by the second game, enter _bxt_tas_studio_smooth_globally in the console and your TAS is smoothed.
You can check the bridge file to see there are two new lines (that are extremely long) at the beginning of the TAS. Those are target_yaw_override and pitch_override where each are followed with a lot of numbers. Here is what it might look like.
version 1
hlstrafe_version 5
load_command map arte_drift;bxt_timer_reset; bxt_anglespeed_cap 0; bxt_speed_scaling 0; bxt_tas_editor_apply_smoothing_over_s 0.24
frames
target_yaw_override 0 0 0 ...
pitch_override 0 0 0 ...
strafing vectorial
target_yaw velocity_lock
----------|------|------|0.010000001|-|-|7
----------|------|------|0.010000001|-|-|1|sv_cheats 1; bxt_give weapon_deagle; bxt_give item_suit; bxt_ch_set_health 10000; bxt_record drift_skip
----------|------|------|0.010000001|-|-|1|bxt_ch_set_pos 1090.339600 3065.695557 -330.968750
...
Each number in those lines, separated by a space, representing the current yaw/pitch at that frame. So, they basically "override" your view angles. The numbers are determined based on some math.
Replay your TAS and you will see smoothed camera. If you have a problem, which is very likely, check the next section.
Smoothing camera is not an inconsequential operation, unless you make it to be. Smoothed pitch usually does not affect strafing but smoothed yaw does. Specifically, target_yaw_override can desync your TAS due to some math. There is one way to skip this math and avoid desyncing and it is to using render_yaw_override instead.
Simply replacing target_yaw_override with render_yaw_override at the beginning of your TAS and you are one.
You can do this for pitch_override by replacing it with render_pitch_override.
There is one thing you need to know about this method. With target_yaw_override, your view is from the perspective of the player. With render_yaw_override, your view is from a camera on a gimbal on your head (precisely where your eyes are). So, the rendered viewangle is fake camera. The downside of it is that live playback will have very weird view model sway. If you record to a demo, the effect is mitigated but still noticable with jittery view model movement.
Overriding rendered viewangles will not affect your TAS in any sort of ways unlike the default camera smoothing.
If you want your camera to be smooth-er, set _bxt_tas_studio_smooth_window_s to some higher values than 0.15 (eg: 0.21).
This is a feature in bxt-rs where every time you make an adjustment, the camera smoothing is automatically applied and you have an "orange" line representing your real path.
Check _bxt_tas_studio_auto_smoothing to know more.
Also known as TAS brute forcing. Check out this video below. Keep in mind that the workflow has changed somewhat since the video was made, but the core principles remain the same.
TASing Updates: Brute-Force Optimizer
You can check all related cvar and commmand usages here.
In short, TAS optimizer will run the game thousands of times every second with different path for each iteration to find the best outcome for your run.
When running optimizer, there are a lot of repeated commands. You should do some keybinds.
bxt_tas_studio_optim_init: initializes TAS optimization from selected bulk. After issuing this command, you will see a teal line mimicking your current TASbxt_tas_optim_start: starts TAS optimization from given constraints. You can set the constraints from bxt_tas_optim_constaint_... or use a .rhai file. You can invoke this command again to re-run the optimizer with new constraints in .rhai file if needed.bxt_tas_optim_reset: resets the current progress of optimization. This is for when you have new constraints.bxt_tas_optim_disable: disables TAS optimization. You will no longer see the teal linebxt_tas_studio_optim_apply: applies the current progress of optimization. This command will overwrite your current TAS with the bruteforced run.rhai fileAs mentioned, you can set optimizer contraints by setting some cvars bxt_tas_optim_constaint_.... However, it is possible that you might find it cumbersome and non optimal. Every time you set a new constraint, you have to edit it through the console. It indeed is.
The alternative way, which is the preferred way, is to have optimizer constraints inside a scripted .rhai file. Check back in the TAS optimizer video and you can see for yourself how it works.
.rhai file does not only make changing constraints easy but also allows having multiple constraints with much more sophisticated conditions. It is basically doing Python script to check if your TAS run has achieved your ideal state.
To setup a .rhai file, simply enter this command.
bxt_tas_optim_rhai_file "<your file>.rhai"
You should have it inside your bridge file where the load_command is.
This is the default .rhai file.
fn is_valid(curr) {
// X pos < -3500
curr.pos[0] < -3500
}
fn is_better(curr, best) {
// New Y pos > best Y pos
curr.pos[1] > best.pos[1]
}
fn to_string(curr) {
// Need to convert to string manually at the moment
curr.pos[0].to_string()
}
It always has 3 functions.
is_valid(): checks if current optimzer state is valid.is_better(): checks if current optimzer state is better than the best state.to_string(): prints into the console every time a better route is found.Keep in mind, this is Rust syntax. If you don't have semicolon (;) at the end of an expression, that expression is returned. That is why you see the comparisons (comparison yields a boolean value) in is_valid() and is_better() don't have semicolon at the end. You can opt-in explicit return by using return keyword.
An example .rhai file in practice.
fn speed(curr) {
(curr.vel[0]**2 + curr.vel[1]**2)**0.5
}
fn is_valid(curr) {
// curr.pos[z] < 2100 && curr.pos[x] > -3500 && curr.pos[y] < -9500 && curr.pos[y] > -11000
// curr.pos[z] > -11315
true
}
fn is_better(curr, best) {
curr.pos[x] > best.pos[x]
// speed(curr) > speed(best)
}
fn to_string(curr) {
// Need to convert to string manually at the moment
curr.pos[2].to_string()
}
In the .rhai file above. There is a custom speed() function that checks for horizontal speed. There are multiple comparisons in is_valid() that are commented in and out any time you want.
You don't have to reload a .rhai file every time you save it. Simply stop the optimizer and start again will reload it.
TAS optimizer gathers the game's state to predict the physics. Prediction is not simulation. So, there are some edge cases where TAS optimizer does something that are illogical because it does not have all the information required.
For collision, the usual easy fix is to set bxt_tas_optim_simulation_accuracy 1. This does result in slower optimizer iterations.
For entity interaction, there is no easy way to work around. There is one feature in TAS optimizer called "multigame optimizer". The way optimizer works is to run the game thousands of iterations per second to find the best outcome. With multigame optimizer, you have to actually run the game with simulation, not prediction. This is obviously slower than the normal way. Set bxt_tas_optim_multiple_games 1 before starting the optimizer and then launch multiple instances of the game like how it is done for second game. You are free to close existing instance any time you want.
And for the rest of the other times, the optimizer path does not match with the simulation for some technical reasons that it is best to just give up knowing about.
This section contains a collection of tips and tricks to aid you on your TASing journey.
Many beginners find TAsing ladder to be challenging.
Keep in mind, you have a TAS tool that does every input in the game and more. If anything, you should try doing ladder yourself by actually playing the game, see what keys you press, and redo that in TAS.
When you grab the ladder, you can only press WASD to move around. Let's say you want to go up ladder fast. You will look up, left, parallel to the ladder, and then hold W key and D key (+forward and +moveright).
That is it. Now you know the input. You should do that exact same thing in the TAS editor.
But, there is no easy way to do these inputs quickly. This is where you should edit the bridge file instead.
Refer to the .hltas reference to see how you can edit these fields.
With that out of the way, now we TAS.
You want to:
-90. Set pitch to -90 in the pitch field.90, assuming the looking straight at the ladder is 0. Set yaw to -90 in the yaw field.+forward, which is f in manual input field. Mark the manual input field with f where it applies.+moveight, which is r in manual input field. Mark the manual input field with r where it applies.Then set the frame count high enough that you can visibly get out of ladder.
Here is what it might look like in practice (from a TAS run). There is yaw 75 and pitch 89. That means the player is looking down to the left of the ladder. The input to get up the ladder are moving back and moving left.
change target_yaw to 90 over 0.17000002 s // Smoothly change the yaw without using yaw field
change pitch to 89 over 0.17000002 s // Smoothly change the pitch without using pitch field
s03-j--c--|------|------|0.010000001|-499.8796|-|11
s03-j--c--|------|------|0.010000001|-518.9515|-|19
s03-j--c--|------|------|0.010000001|-518.9515|-|1 // Player is in the air, not grabbing the ladder yet
// ladder
----------|-l-b--|------|0.010000001|75|-|18 // Ladder grab frame with target yaw 75 and pitch 90
s03-j--C--|------|------|0.010000001|-633.54517|-|4 // Exitting the ladder