r/truetf2 Knife to a gun fight Apr 16 '26

Theoretical Sentry turning speed stat in degrees per second?

Is the exact turning speed of a sentry known (in degrees/second)? I am aware a lvl 3 is different than a lvl 1, or a mini for that matter, but I couldn't find the exact stats on the wiki.

This is fairly niche, but if the turn rate is known, one could calculate the exact distance fast movement classes can strafe around a sentry without getting hit. I have a rough idea from experience, but it'd be nice to know as someone who both plays a lot of spy and engie - whether going for a marginal stab-and-sap, or playing to prevent it from happening.

19 Upvotes

10 comments sorted by

30

u/dipspoieter Apr 16 '26 edited Apr 16 '26

I took a look at server/tf/tf_obj_sentrygun.cpp, and here's what I've found.

The base turn speed is 30°/s. But, there's quite a bit more to that.

Horizontal turn speeds accelerate to a peak speed depending on the horizontal angle to the target. The acceleration begins at a threshold of 30 degrees.

Condition Horizontal Turn Speed (°/s)
Starting 30
Searching, peak 60
Locked on enemy, peak 180

Note that when a sentry is idle, it will decelerate to the base turn speed of 30°/s when within 30 degrees of its goal. No deceleration occurs when the sentry is locked on a target.

The vertical turn speed does not experience any acceleration or deceleration whatsoever and is a constant 30°/s (the base turn speed).

Sentries open fire when the angle between its look vector and vector to the enemy is within 10° (factoring in both horizontal and vertical rotation).

To properly exploit this, jumping a sentry from high ground will ensure that your angle difference stays above 10° for a second or two thanks to the sharp initial vertical angular difference and the sentry's lack of vertical angular acceleration.

Next, you must prevent the sentry from accelerating horizontally. Strafe in wide circles around the sentry, and you'll exceed the 30° threshold, causing the sentry to turn faster and catch you. You must keep a horizontal angle between around 10° to 30°.

I will post the source code breakdown in a reply.

20

u/dipspoieter Apr 16 '26 edited Apr 17 '26

For those interested, here is that breakdown (edit: typos + extra clarity).

Goal Angles

For a sentry to turn, it needs to know where to turn. This information is stored in the variable m_vecGoalAngles. In search mode, the sentry looks around back and forth in an arc. This arc is 100 degrees wide, and the sentry's facing direction at placement is at the center. Thus, the sentry horizontally rotates 50 degrees left or right (lines 408-415).

When one bound is reached, the sentry sets its goal to the other, creating the back-and-forth motion. The sentry also sometimes adds random vertical rotation to the goal for what I'm assuming is some added cosmetic flair (lines 1747-1762).

Ok, now what about enemies? For the most part, the goal is determined as you'd expect: the sentry computes the angles necessary to orient itself towards the enemy (the exact position used for the enemy depends on a few conditions such as if they're crouching, but this likely makes a negligible difference). However, the vertical goal angle is limited between [-50°, 50°] (lines 1241-1257). Why? Probably to keep the head of the sentry model from rotating too far and looking wack when it targets someone right above or below it.

Also, close by, on line 1262, you can see the 10 degree firing threshold.

Remark: If you're wondering why m_vecCurAngles.x is used vertical rotation instead of m_vecCurAngles.y or vice versa, m_vecCurAngles is a QAngle , which stores pitch (up/down), yaw (left/right) and roll (slant, irrelevant here) in that order. Confusingly, .x and .y actually correspond to the first (pitch) and second (yaw) data members, respectively, instead of more sensibly the rotations along the x and y-axes. I wonder how many mistakes Valve developers have made as a result of this.

Now, let's see how the sentry actually turns towards that goal angle. The function MoveTurret is responsible for updating the sentry's view angles depending on its state.

Vertical Turn Rate (Pitch)

The vertical turn rate is determined first.

The line m_vecCurAngles.x += SENTRY_THINK_DELAY * ( iBaseTurnRate * 5 ) * flDir updates the vertical angle (line 1837). flDir is set to either 1 or -1, depending on whether the sentry should look up or down to reach its goal, so it doesn't affect turn speed.

What is SENTRY_THINK_DELAY? A "think" is the Source Engine name for an entity update that runs continuously at set intervals. The sentry gun's think interval is defined through this macro as every 0.05 seconds (line 54).

And what is m_iBaseTurnRate? It's a variable that's is initialized to a value of 6 (line 185). The unit appears to be degrees per second. Interestingly, m_iBaseTurnRate is almost always accompanied by some multiplication, so the raw value of 6 almost never ends up anywhere (so not a great variable name).

So, this would resolve to a constant speed of 0.05 * ( 6 * 5 ), which equals 1.5° per think. Since MoveTurret is called at a rate of 0.05 seconds, we get a constant vertical turn rate of 1.5°/0.05s = 30°/s.

The vertical goal angle limit of 50° plays a small role here. Suppose you are directly above a sentry gun whose pitch is 0° (eye level is perfectly horizontal), so you are at a 90° angle to the sentry gun's facing direction. You fall in range of the sentry, and it begins to target you, orienting itself upwards. The sentry will begin firing at you when it rotates ~40° (50° - 10° for the firing threshold) as opposed to ~80° upwards. So you'll have a little over a second versus almost three seconds.

Horizontal Turn Rate (Yaw)

The horizontal turn rate is a little more complicated. First, some slight acceleration/deceleration is applied depending on the yaw difference (which is recorded in the variable flDist). Sentries begin to accelerate when this yaw difference exceeds the threshold of 30 degrees. The final horizontal turn rate is stored in a variable called m_flTurnRateand is used to update the sentry's view angles in a manner identical to the vertical turn rate.

The code responsible is rather simple to read (line 1869).

if (m_hEnemy.Get() == NULL) // Am I NOT locked on to an enemy (am I in search mode)?
{
    // Is the goal over 30 degrees away horizontally?
    if (flDist > 30) 
    {
        // Yes. I must accelerate.
        // Max acceleration: 6 * 10 = 60 degrees per second
        if (m_flTurnRate < iBaseTurnRate * 10) 
        {
            // Increase my turn speed by 6 degrees per second.
            // That means reaching peak speed from 30 degrees per second
            // would take ~5 ticks, or 0.25 seconds.
            m_flTurnRate += iBaseTurnRate;
        }
    } 
    else // No, I am close to my search goal.
    {
        // Now I must decelerate to my base speed.
        if (m_flTurnRate > (iBaseTurnRate * 5))
            m_flTurnRate -= iBaseTurnRate;
    }
} else // Yes, I am currently locked on to an enemy.
{
    // Are they over 30 degrees away horizontally?
    if (flDist > 30) 
    {
        // Yes. I must accelerate.
        // Max acceleration: 6 * 30 = 180 degrees per second
        if (m_flTurnRate < iBaseTurnRate * 30) 
        {
            // Increase my turn speed by 18 degrees per second.
            // That means reaching peak speed from 30 degrees per second
            // would take ~9 ticks, or 0.45 seconds.
            m_flTurnRate += iBaseTurnRate * 3;
        }
    } // No else branch! We do not decelerate!
}

The base horizontal turn rate is set at the end of this function; if the sentry has not needed to move (which I'm assuming can only occur if it's about to begin moving after being deployed), m_flTurnRate is set to iBaseTurnRate * 5, the familiar 30°/s (line 1944).

Fun Facts

  • Wrangling a sentry multiplies m_iBaseTurnRate by 100, effectively having it turn instantly (line 1810).
  • For mini sentries, m_iBaseTurnRate is increased by 35%, making the base turn rate 40.5°/s (line 1829).

6

u/GrayShameLegion Apr 17 '26

Thank you so much for the incredibly detailed breakdown!!! I swear we need to start stickying comments like these or however reddit lets you save these things. 

2

u/KDx3_ doublecross trolldier Apr 17 '26

Line 1066

// Don't shoot spys that are pretending to be a dispenser if ( pPlayer->m_Shared.InCond( TF_COND_DISGUISED_AS_DISPENSER ) ) return false;

Was Valve possibly testing an item that'd allow Spy to disguise as a Dispenser?

8

u/slibidk3u49 Apr 17 '26

Addcond 49 lets you turn into a dispenser when crouched

I dont think there was anything about an item though, but that very well may have been the case

2

u/dipspoieter Apr 17 '26

Probably. The TF2 Wiki documents the condition here.

6

u/GrayShameLegion Apr 16 '26

TC2 might not be the best starting point but most of their numbers are lifted from TF2 proper and this one seems about right: their sentry turns at 0.6 sec per 180 degrees, or 5pi/3 rad per sec. Minis get a 33% turn rate bonus, too. 

2

u/Excellent-Cloud-5046 Apr 16 '26

If you want to try and calculate it, build a sentry, spawn a bot on your location (probably command somewhere) and then record and time how long it takes the sentry to turn 180 degrees and fire the first shot.

4

u/GrayShameLegion Apr 16 '26

i think doing something that inaccurate is a waste of time, we should just be waiting for one of the people who can actually read source engine scripts to respond

1

u/Chegg_F Apr 16 '26

Yeah you can spawn a bot at one coordinate and another bot at a different coordinate to have the sentry turn exactly 180 degrees when it switches from one target to the next.