Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
sv_dodging.qc
Go to the documentation of this file.
1#include "sv_dodging.qh"
2
3// TODO the CSQC blocks in this sv_ file are currently not compiled but will be when dodging prediction gets enabled
4
5#define PHYS_DODGING g_dodging
6#define PHYS_DODGING_DELAY autocvar_sv_dodging_delay
7#define PHYS_DODGING_DISTANCE_THRESHOLD autocvar_sv_dodging_wall_distance_threshold
8#define PHYS_DODGING_FROZEN_DOUBLETAP autocvar_sv_dodging_frozen_doubletap
9#define PHYS_DODGING_HEIGHT_THRESHOLD autocvar_sv_dodging_height_threshold
10#define PHYS_DODGING_HORIZ_SPEED_MIN autocvar_sv_dodging_horiz_speed_min
11#define PHYS_DODGING_HORIZ_SPEED_MAX autocvar_sv_dodging_horiz_speed_max
12#define PHYS_DODGING_HORIZ_FORCE_SLOWEST autocvar_sv_dodging_horiz_force_slowest
13#define PHYS_DODGING_HORIZ_FORCE_FASTEST autocvar_sv_dodging_horiz_force_fastest
14#define PHYS_DODGING_HORIZ_FORCE_FROZEN autocvar_sv_dodging_horiz_force_frozen
15#define PHYS_DODGING_RAMP_TIME autocvar_sv_dodging_ramp_time
16#define PHYS_DODGING_UP_SPEED autocvar_sv_dodging_up_speed
17#define PHYS_DODGING_WALL autocvar_sv_dodging_wall_dodging
18#define PHYS_DODGING_AIR autocvar_sv_dodging_air_dodging
19#define PHYS_DODGING_MAXSPEED autocvar_sv_dodging_maxspeed
20#define PHYS_DODGING_AIR_MAXSPEED autocvar_sv_dodging_air_maxspeed
21#define PHYS_DODGING_CLIENTSELECT autocvar_sv_dodging_clientselect
22
23// we ran out of stats slots! TODO: re-enable this when prediction is available for dodging
24#if 0
25#define PHYS_DODGING STAT(DODGING, this)
26#define PHYS_DODGING_DELAY STAT(DODGING_DELAY, this)
27#define PHYS_DODGING_DISTANCE_THRESHOLD STAT(DODGING_DISTANCE_THRESHOLD, this)
28#define PHYS_DODGING_FROZEN_DOUBLETAP STAT(DODGING_FROZEN_DOUBLETAP, this)
29#define PHYS_DODGING_HEIGHT_THRESHOLD STAT(DODGING_HEIGHT_THRESHOLD, this)
30#define PHYS_DODGING_HORIZ_SPEED_MIN STAT(DODGING_HORIZ_SPEED_MIN, this)
31#define PHYS_DODGING_HORIZ_SPEED_MAX STAT(DODGING_HORIZ_SPEED_MAX, this)
32#define PHYS_DODGING_HORIZ_FORCE_SLOWEST STAT(DODGING_HORIZ_FORCE_SLOWEST, this)
33#define PHYS_DODGING_HORIZ_FORCE_FASTEST STAT(DODGING_HORIZ_FORCE_FASTEST, this)
34#define PHYS_DODGING_HORIZ_FORCE_FROZEN STAT(DODGING_HORIZ_FORCE_FROZEN, this)
35#define PHYS_DODGING_RAMP_TIME STAT(DODGING_RAMP_TIME, this)
36#define PHYS_DODGING_UP_SPEED STAT(DODGING_UP_SPEED, this)
37#define PHYS_DODGING_WALL STAT(DODGING_WALL, this)
38#define PHYS_DODGING_AIR STAT(DODGING_AIR, this)
39#define PHYS_DODGING_MAXSPEED STAT(DODGING_MAXSPEED, this)
40#define PHYS_DODGING_AIR_MAXSPEED STAT(DODGING_AIR_MAXSPEED, this)
41#define PHYS_DODGING_CLIENTSELECT STAT(DODGING_CLIENTSELECT, this)
42#endif
43
44#ifdef CSQC
46 #define PHYS_DODGING_FRAMETIME (1 / (frametime <= 0 ? 60 : frametime))
47 #define PHYS_DODGING_TIMEOUT(s) STAT(DODGING_TIMEOUT)
48 #define PHYS_DODGING_PRESSED_KEYS(s) (s).pressedkeys
49 #define PHYS_DODGING_ENABLED(s) autocvar_cl_dodging
50#elif defined(SVQC)
51 #define PHYS_DODGING_FRAMETIME sys_frametime
52 #define PHYS_DODGING_TIMEOUT(s) CS_CVAR(s).cvar_cl_dodging_timeout
53 #define PHYS_DODGING_PRESSED_KEYS(s) CS(s).pressedkeys
54 #define PHYS_DODGING_ENABLED(s) CS_CVAR(s).cvar_cl_dodging
55#endif
56
57REPLICATE(cvar_cl_dodging_timeout, float, "cl_dodging_timeout");
58REPLICATE(cvar_cl_dodging, bool, "cl_dodging");
59
60#ifdef SVQC
61
63
64#include <common/animdecide.qh>
66
67REGISTER_MUTATOR(dodging, cvar("g_dodging"))
68{
69 // this just turns on the cvar.
71 {
72 g_dodging = cvar("g_dodging");
73 }
74
75 // this just turns off the cvar.
77 {
78 g_dodging = 0;
79 }
80
81 return false;
82}
83
84#elif defined(CSQC)
85REGISTER_MUTATOR(dodging, true);
86#endif
87
88// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done..
90
91// the jump part of the dodge cannot be ramped
93
94// these are used to store the last key press time for each of the keys..
99
100// these store the movement direction at the time of the dodge action happening.
102
103// this indicates the last time a dodge was executed. used to check if another one is allowed
104// and to ramp up the dodge acceleration in the physics hook.
106
107// the total speed that will be added over the ramp time
109// the part of total yet to be added
111
112#ifdef CSQC
114#endif
115
116#define X(dir) \
117 tracebox(this.origin, this.mins, this.maxs, this.origin + threshold * dir, true, this); \
118 if (trace_fraction < 1 && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) \
119 return true;
120
121// returns true if the player is close to a wall
122bool is_close_to_wall(entity this, float threshold, vector forward, vector right)
123{
124 X(right);
125 X(-right);
126 X(forward);
127 X(-forward);
128
129 return false;
130}
131
132bool is_close_to_ground(entity this, float threshold, vector up)
133{
134 if (IS_ONGROUND(this)) return true;
135 X(-up); // necessary for dodging down a slope using doubletap (using `+dodge` works anyway)
136
137 return false;
138}
139
140#undef X
141
150
152{
153 bool frozen_dodging = (PHYS_FROZEN(this) && PHYS_DODGING_FROZEN(this));
154 bool frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_DOUBLETAP);
155
156 float tap_direction_x = 0;
157 float tap_direction_y = 0;
158 bool dodge_detected = false;
159 vector mymovement = PHYS_CS(this).movement;
160
161 #define X(COND,BTN,RESULT) \
162 if (mymovement_##COND) { \
163 /* is this a state change? */ \
164 if(!(PHYS_DODGING_PRESSED_KEYS(this) & KEY_##BTN) || frozen_no_doubletap) { \
165 tap_direction_##RESULT; \
166 if ((time - this.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(this) || frozen_no_doubletap) { \
167 dodge_detected = true; \
168 } else if(PHYS_INPUT_BUTTON_DODGE(this)) { \
169 dodge_detected = true; \
170 } \
171 this.last_##BTN##_KEY_time = time; \
172 } \
173 }
174 X(x < 0, BACKWARD, x--);
175 X(x > 0, FORWARD, x++);
176 X(y < 0, LEFT, y--);
177 X(y > 0, RIGHT, y++);
178 #undef X
179
180 if (!dodge_detected) return false;
181
182 // this check has to be after checking keys:
183 // the first key press of the double tap is allowed to be before dodging delay,
184 // only the second has to be after, otherwise +dodge gives an advantage because typical repress time is 0.1 s
185 // or higher which means players using +dodge would be able to do it more often
187 return false;
188
189 vector forward, right, up;
190 MAKE_VECTORS(this.angles, forward, right, up);
191
193 bool can_wall_dodge = (PHYS_DODGING_WALL && is_close_to_wall(this, PHYS_DODGING_DISTANCE_THRESHOLD, forward, right));
194 bool can_air_dodge = (PHYS_DODGING_AIR && (PHYS_DODGING_AIR_MAXSPEED == 0 || vdist(this.velocity, <, PHYS_DODGING_AIR_MAXSPEED)));
195 if (!can_dodge && !can_wall_dodge && !can_air_dodge) return false;
196
197 this.last_dodging_time = time;
198
199 this.dodging_action = 1;
200 this.dodging_single_action = 1;
201
204
205 this.dodging_direction.x = tap_direction_x;
206 this.dodging_direction.y = tap_direction_y;
207
208 // normalize the dodging_direction vector.. (unlike UT99) XD
209 float length = sqrt(this.dodging_direction.x ** 2 + this.dodging_direction.y ** 2);
210
211 this.dodging_direction.x = this.dodging_direction.x / length;
212 this.dodging_direction.y = this.dodging_direction.y / length;
213
214 return true;
215}
216
218{
219 // can't use return value from PM_dodging_checkpressedkeys because they're called from different hooks
220 if (!this.dodging_action) return;
221
222 // when swimming or dead, no dodging allowed..
223 bool frozen_dodging = (PHYS_FROZEN(this) && PHYS_DODGING_FROZEN(this));
224 if (this.waterlevel >= WATERLEVEL_SWIMMING || IS_DEAD(this) || (PHYS_DODGING_CLIENTSELECT && !PHYS_DODGING_ENABLED(this) && !frozen_dodging))
225 {
226 this.dodging_action = 0;
227 this.dodging_direction.x = 0;
228 this.dodging_direction.y = 0;
229 return;
230 }
231
232 vector forward, right, up;
234 MAKE_VECTORS(this.v_angle, forward, right, up);
235 else
236 MAKE_VECTORS(this.angles, forward, right, up);
237
238 // fraction of the force to apply each frame
239 // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code
240 // will be called ramp_time/frametime times = 2 times. so, we need to
241 // add 0.5 * the total speed each frame until the dodge action is done..
242 float common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME;
243 // NOTE: depending on cl_netfps the client may (and probably will) send more input frames during each server frame
244 // but common_factor uses server frame rate so players with higher cl_netfps will ramp slightly faster
245
246 float velocity_increase = min(common_factor * this.dodging_force_total, this.dodging_force_remaining);
247 this.dodging_force_remaining -= velocity_increase;
248 this.velocity += this.dodging_direction.x * velocity_increase * forward
249 + this.dodging_direction.y * velocity_increase * right;
250
251 // the up part of the dodge is a single shot action
252 if (this.dodging_single_action == 1)
253 {
254 UNSET_ONGROUND(this);
255
256 this.velocity += PHYS_DODGING_UP_SPEED * up;
257
258#ifdef SVQC
260 PlayerSound(this, playersound_jump, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND);
261
263#endif
264
265 this.dodging_single_action = 0;
266 }
267
268 if(this.dodging_force_remaining <= 0)
269 {
270 // reset state so next dodge can be done correctly
271 this.dodging_action = 0;
272 this.dodging_direction.x = 0;
273 this.dodging_direction.y = 0;
274 }
275}
276
277#ifdef CSQC
279{
281
282 // NOTE: GetPressedKeys and PM_dodging_GetPressedKeys use similar code
283 int keys = this.pressedkeys;
284 keys = BITSET(keys, KEY_FORWARD, PHYS_CS(this).movement.x > 0);
285 keys = BITSET(keys, KEY_BACKWARD, PHYS_CS(this).movement.x < 0);
286 keys = BITSET(keys, KEY_RIGHT, PHYS_CS(this).movement.y > 0);
287 keys = BITSET(keys, KEY_LEFT, PHYS_CS(this).movement.y < 0);
288
289 keys = BITSET(keys, KEY_JUMP, PHYS_INPUT_BUTTON_JUMP(this));
290 keys = BITSET(keys, KEY_CROUCH, IS_DUCKED(this)); // workaround: player can't un-crouch until their path is clear, so we keep the button held here
291 keys = BITSET(keys, KEY_ATCK, PHYS_INPUT_BUTTON_ATCK(this));
292 keys = BITSET(keys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(this));
293 this.pressedkeys = keys;
294}
295#endif
296
297MUTATOR_HOOKFUNCTION(dodging, PlayerPhysics)
298{
299 entity player = M_ARGV(0, entity);
300
301#ifdef CSQC
303#endif
304 PM_dodging(player);
305}
306
307#ifdef SVQC
308
310{
311 this.last_dodging_time = 0;
312
313 this.dodging_action = 0;
314 this.dodging_single_action = 0;
315
316 this.dodging_force_total = 0;
318
319 this.dodging_direction = '0 0 0';
320}
321
322MUTATOR_HOOKFUNCTION(dodging, PlayerSpawn)
323{
324 entity player = M_ARGV(0, entity);
325 dodging_ResetPlayer(player);
326}
327
328MUTATOR_HOOKFUNCTION(dodging, MakePlayerObserver)
329{
330 entity player = M_ARGV(0, entity);
331 dodging_ResetPlayer(player);
332}
333
335{
336 entity player = M_ARGV(0, entity);
337
339}
340
341#endif
void animdecide_setaction(entity e, float action, float restart)
const int ANIMACTION_JUMP
#define MUTATOR_ONADD
Definition base.qh:309
#define MUTATOR_ONROLLBACK_OR_REMOVE
Definition base.qh:311
#define REGISTER_MUTATOR(...)
Definition base.qh:295
#define MUTATOR_HOOKFUNCTION(...)
Definition base.qh:335
#define BITSET(var, mask, flag)
Definition bits.qh:11
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
#define M_ARGV(x, type)
Definition events.qh:17
vector movement
Definition player.qh:229
#define PHYS_CS(s)
Definition player.qh:258
#define IS_DEAD(s)
Definition player.qh:245
#define PHYS_INPUT_BUTTON_JUMP(s)
Definition player.qh:151
vector v_angle
Definition player.qh:237
float waterlevel
Definition player.qh:226
#define PHYS_FROZEN(s)
Definition player.qh:118
#define IS_DUCKED(s)
Definition player.qh:210
#define PHYS_DODGING_FROZEN(s)
Definition player.qh:112
#define PHYS_INPUT_BUTTON_ATCK(s)
Definition player.qh:150
#define PHYS_INPUT_BUTTON_ATCK2(s)
Definition player.qh:152
int g_dodging
cvar loopback
Definition stats.qh:251
const int KEY_JUMP
Definition constants.qh:39
const int KEY_ATCK2
Definition constants.qh:42
const int KEY_RIGHT
Definition constants.qh:38
const int KEY_BACKWARD
Definition constants.qh:36
const int KEY_FORWARD
Definition constants.qh:35
const int KEY_LEFT
Definition constants.qh:37
const int KEY_CROUCH
Definition constants.qh:40
const int KEY_ATCK
Definition constants.qh:41
vector velocity
float time
#define MAKE_VECTORS(angles, forward, right, up)
Same as the makevectors builtin but uses the provided locals instead of the v_* globals.
v y
Definition ent_cs.qc:121
ent angles
Definition ent_cs.qc:121
#define PlayerSound(this, def, chan, vol, voicetype)
const int VOICETYPE_PLAYERSOUND
#define REPLICATE(...)
Replicates a client cvar into a server field.
Definition replicate.qh:23
ERASEABLE float map_bound_ranges(float value, float src_min, float src_max, float dest_min, float dest_max)
Same as map_ranges except that values outside the source range are clamped to min or max.
Definition math.qh:372
float cvar(string name)
float vlen(vector v)
float sqrt(float f)
float min(float f,...)
const int WATERLEVEL_SWIMMING
Definition movetypes.qh:13
#define UNSET_ONGROUND(s)
Definition movetypes.qh:18
#define IS_ONGROUND(s)
Definition movetypes.qh:16
vector
Definition self.qh:92
void GetPressedKeys(entity this)
Definition client.qc:1761
const float VOL_BASE
Definition sound.qh:36
const int CH_PLAYER
Definition sound.qh:20
int pressedkeys
void dodging_ResetPlayer(entity this)
vector dodging_direction
#define PHYS_DODGING_ENABLED(s)
Definition sv_dodging.qc:49
float dodging_force_total
#define PHYS_DODGING_HORIZ_FORCE_SLOWEST
Definition sv_dodging.qc:12
bool autocvar_cl_dodging
Definition sv_dodging.qc:45
#define PHYS_DODGING_AIR_MAXSPEED
Definition sv_dodging.qc:20
#define PHYS_DODGING_FROZEN_DOUBLETAP
Definition sv_dodging.qc:8
#define PHYS_DODGING_AIR
Definition sv_dodging.qc:18
float last_RIGHT_KEY_time
Definition sv_dodging.qc:98
#define PHYS_DODGING_HORIZ_FORCE_FROZEN
Definition sv_dodging.qc:14
#define PHYS_DODGING_MAXSPEED
Definition sv_dodging.qc:19
float last_dodging_time
#define PHYS_DODGING_WALL
Definition sv_dodging.qc:17
float dodging_single_action
Definition sv_dodging.qc:92
bool autocvar_sv_dodging_sound
Definition sv_dodging.qc:62
#define PHYS_DODGING_HEIGHT_THRESHOLD
Definition sv_dodging.qc:9
#define PHYS_DODGING_HORIZ_SPEED_MAX
Definition sv_dodging.qc:11
#define PHYS_DODGING_FRAMETIME
Definition sv_dodging.qc:46
void PM_dodging(entity this)
#define PHYS_DODGING_CLIENTSELECT
Definition sv_dodging.qc:21
#define PHYS_DODGING_RAMP_TIME
Definition sv_dodging.qc:15
float last_FORWARD_KEY_time
Definition sv_dodging.qc:95
#define PHYS_DODGING_DELAY
Definition sv_dodging.qc:6
float dodging_action
Definition sv_dodging.qc:89
float last_LEFT_KEY_time
Definition sv_dodging.qc:97
#define PHYS_DODGING_HORIZ_SPEED_MIN
Definition sv_dodging.qc:10
#define X(dir)
float dodging_force_remaining
#define PHYS_DODGING_HORIZ_FORCE_FASTEST
Definition sv_dodging.qc:13
bool is_close_to_ground(entity this, float threshold, vector up)
void PM_dodging_GetPressedKeys(entity this)
#define PHYS_DODGING_DISTANCE_THRESHOLD
Definition sv_dodging.qc:7
bool PM_dodging_checkpressedkeys(entity this)
#define PHYS_DODGING_UP_SPEED
Definition sv_dodging.qc:16
bool is_close_to_wall(entity this, float threshold, vector forward, vector right)
float last_BACKWARD_KEY_time
Definition sv_dodging.qc:96
float determine_force(entity player)
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt()
Definition vector.qh:8
#define vec2(...)
Definition vector.qh:90