-
Notifications
You must be signed in to change notification settings - Fork 0
/
cstrafe_detector.cpp
158 lines (148 loc) · 3.83 KB
/
cstrafe_detector.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// sv_auto_cstrafe_min_attempts = 50
// sv_auto_cstrafe_success_threshold = 18
// sv_auto_cstrafe_lower_overlap_pct_threshold = 4
// sv_auto_cstrafe_attempt_window = 100
// sv_auto_cstrafe_upper_overlap_pct_threshold = 6
// sv_auto_cstrafe_sequence_length = 20
// underlaps and overlaps internal array are not used for input detection
#define CS_PLAYER_SPEED_RUN 260.0f
#define CS_PLAYER_SPEED_WALK_MODIFIER 0.52f
void UpdateCStrafeStats(CStrafeStats *ss, CCSPlayerController *controller, CCSUsrMsg_CounterStrafe *message)
{
if (!controller)
{
return;
}
if (!message->has_press_to_release_ns())
{
return;
}
CCSPlayerPawn *pawn = controller->GetPlayerPawn();
if (!pawn)
{
return;
}
// Make sure the player is running fast enough.
if (pawn->m_vecAbsVelocity().Length() < CS_PLAYER_SPEED_WALK_MODIFIER * CS_PLAYER_SPEED_RUN)
{
return;
}
// Only track overlaps and underlaps within 15 ticks (0.23s)
float press_to_release_ticks = message->press_to_release_ns() / 1e9 * ENGINE_FIXED_TICK_RATE;
if (fabs(press_to_release_ticks) >= 15.5)
{
return;
}
// Only track up to 15 ticks window
// In reality this is more like 7-8 because friction
uint32 numTicks = V_roundd(press_to_release_ticks);
if (numTicks > 15)
{
return;
}
OverlapState state;
if (numTicks == 0)
{
state = PERFECT;
}
else if (press_to_release_ticks > 0)
{
state = OVERLAP;
}
else
{
state = UNDERLAP;
}
uint32 attemptWindow = sv_auto_cstrafe_attempt_window.GetInt();
if (attemptWindow > 1000)
{
return;
}
ss->ResizeBuffer(attemptWindow);
// sequences is a cyclic array/buffer
if (ss->sequences.maxCount > 0)
{
ss->sequences.Insert(state);
}
if (message->press_to_release_ns() >= 0)
{
ss->overlaps[numTicks]++;
}
else
{
ss->underlaps[numTicks]++;
}
uint32 seqLength = sv_auto_cstrafe_sequence_length.GetInt();
uint32 numOverlap = 0;
uint32 numPerfect = 0; // in sequence
uint32 maxNumPerfectInSequence = 0;
uint32 numAttempts = 0;
if (seqLength > 1000)
{
return;
}
for (uint32 i = 0; i < attemptWindow; i++)
{
// Compute the number of overlap / attempt / perfect in a sequence
if (i < ss->buffer.sequences.Count())
{
OverlapState st = ss->buffer.GetOffsetFromCurrent(i);
if (st)
{
numAttempts++;
if (st == OVERLAP)
{
numOverlap++;
}
else if (st == PERFECT)
{
numPerfect++;
}
}
}
// The sequence moves forward, verify if the entry that is out of the sequence is perfect or not
if (i >= seqLength)
{
if (i - seqLength < ss->buffer.sequences.Count())
{
OverlapState st = ss->buffer.GetOffsetFromCurrent(i - seqLength);
if (st == PERFECT)
{
numPerfect--;
}
}
}
// Keep track of the max value in the window
if (maxNumPerfectInSequence < numPerfect)
{
maxNumPerfectInSequence = numPerfect;
}
}
float overlapThreshold = 0.0;
uint32 minAttempts = sv_auto_cstrafe_min_attempts.GetInt();
if (numAttempts >= minAttempts)
{
uint32 successThreshold = sv_auto_cstrafe_success_threshold.GetInt();
if (maxNumPerfectInSequence >= successThreshold) // maxNumPerfectInSequence < seqLength
{
float ratio = 0.0;
if (seqLength > successThreshold)
{
ratio = (maxNumPerfectInSequence - successThreshold) / (seqLength - successThreshold);
}
// Interp the threshold.
overlapThreshold =
Lerp(sv_auto_cstrafe_lower_overlap_pct_threshold.GetFloat(), sv_auto_cstrafe_upper_overlap_pct_threshold.GetFloat(), ratio);
}
}
float currentOverlapRate = (float)numOverlap / numAttempts * 100.0;
// Overlapping too little, kicking?
if (currentOverlapRate < overlapThreshold)
{
ss->ResizeBuffer(0);
if (sv_auto_cstrafe_kick.GetBool())
{
// Kicked for input automation
}
}
}