]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/mutators/mutator_dodging.qc
Rough support for dodging prediction
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / mutator_dodging.qc
1
2 #ifdef SVQC
3
4 .float cvar_cl_dodging_timeout;
5
6 .float stat_dodging;
7 .float stat_dodging_timeout;
8 .float stat_dodging_delay;
9 .float stat_dodging_horiz_speed_frozen;
10 .float stat_dodging_frozen_nodoubletap;
11 .float stat_dodging_frozen;
12 .float stat_dodging_horiz_speed;
13 .float stat_dodging_height_threshold;
14 .float stat_dodging_distance_threshold;
15 .float stat_dodging_ramp_time;
16 .float stat_dodging_up_speed;
17 .float stat_dodging_wall;
18
19 #endif
20
21 // set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done..
22 .float dodging_action;
23
24 // the jump part of the dodge cannot be ramped
25 .float dodging_single_action;
26
27
28 // these are used to store the last key press time for each of the keys..
29 .float last_FORWARD_KEY_time;
30 .float last_BACKWARD_KEY_time;
31 .float last_LEFT_KEY_time;
32 .float last_RIGHT_KEY_time;
33
34 // these store the movement direction at the time of the dodge action happening.
35 .float dodging_direction_x;
36 .float dodging_direction_y;
37
38 // this indicates the last time a dodge was executed. used to check if another one is allowed
39 // and to ramp up the dodge acceleration in the physics hook.
40 .float last_dodging_time;
41
42 // This is the velocity gain to be added over the ramp time.
43 // It will decrease from frame to frame during dodging_action = 1
44 // until it's 0.
45 .float dodging_velocity_gain;
46
47 #ifdef CSQC
48 .float pressedkeys;
49
50 #define PHYS_INPUT_MOVEVALUES(s)                        input_movevalues
51 #define PHYS_INPUT_BUTTONS(s)                           input_buttons
52 #define PHYS_INPUT_ANGLES(s)                            input_angles
53
54 #define UNSET_ONGROUND(s)                                       s.pmove_flags &= ~PMF_ONGROUND
55 #define PHYS_FROZEN(s)                                          getstati(STAT_FROZEN)
56 #define IS_ONGROUND(s)                                          (s.pmove_flags & PMF_ONGROUND)
57
58 #define PHYS_DODGING_FRAMETIME                          input_timelength
59 #define PHYS_DODGING                                            getstati(STAT_DODGING)
60 #define PHYS_DODGING_DELAY                                      getstati(STAT_DODGING_DELAY)
61 #define PHYS_DODGING_TIMEOUT(s)                         getstatf(STAT_DODGING_TIMEOUT)
62 #define PHYS_DODGING_HORIZ_SPEED_FROZEN         getstatf(STAT_DODGING_HORIZ_SPEED_FROZEN)
63 #define PHYS_DODGING_FROZEN                                     getstati(STAT_DODGING_FROZEN)
64 #define PHYS_DODGING_FROZEN_NODOUBLETAP         getstati(STAT_DODGING_FROZEN_NO_DOUBLETAP)
65 #define PHYS_DODGING_HORIZ_SPEED                        getstatf(STAT_DODGING_HORIZ_SPEED)
66 #define PHYS_DODGING_PRESSED_KEYS(s)            s.pressedkeys
67 #define PHYS_DODGING_HEIGHT_THRESHOLD           getstatf(STAT_DODGING_HEIGHT_THRESHOLD)
68 #define PHYS_DODGING_DISTANCE_THRESHOLD         getstatf(STAT_DODGING_DISTANCE_THRESHOLD)
69 #define PHYS_DODGING_RAMP_TIME                          getstatf(STAT_DODGING_RAMP_TIME)
70 #define PHYS_DODGING_UP_SPEED                           getstatf(STAT_DODGING_UP_SPEED)
71 #define PHYS_DODGING_WALL                                       getstatf(STAT_DODGING_WALL)
72
73 #elif defined(SVQC)
74
75 #define PHYS_INPUT_ANGLES(s)                            s.angles
76 #define PHYS_INPUT_MOVEVALUES(s)                        s.movement
77 #define PHYS_INPUT_BUTTONS(s)                           (s.BUTTON_ATCK + 2 * s.BUTTON_JUMP + 4 * s.BUTTON_ATCK2 + 8 * s.BUTTON_ZOOM + 16 * s.BUTTON_CROUCH + 32 * s.BUTTON_HOOK + 64 * s.BUTTON_USE + 128 * (s.movement_x < 0) + 256 * (s.movement_x > 0) + 512 * (s.movement_y < 0) + 1024 * (s.movement_y > 0))
78 #define UNSET_ONGROUND(s)                                       s.flags &= ~FL_ONGROUND
79 #define PHYS_FROZEN(s)                                          s.frozen
80 #define IS_ONGROUND(s)                                          (s.flags & FL_ONGROUND)
81
82 #define PHYS_DODGING_FRAMETIME                          sys_frametime
83 #define PHYS_DODGING                                            g_dodging
84 #define PHYS_DODGING_DELAY                                      autocvar_sv_dodging_delay
85 #define PHYS_DODGING_TIMEOUT(s)                         s.cvar_cl_dodging_timeout
86 #define PHYS_DODGING_HORIZ_SPEED_FROZEN         autocvar_sv_dodging_horiz_speed_frozen
87 #define PHYS_DODGING_FROZEN                                     autocvar_sv_dodging_frozen
88 #define PHYS_DODGING_FROZEN_NODOUBLETAP         autocvar_sv_dodging_frozen_doubletap
89 #define PHYS_DODGING_HORIZ_SPEED                        autocvar_sv_dodging_horiz_speed
90 #define PHYS_DODGING_PRESSED_KEYS(s)            s.pressedkeys
91 #define PHYS_DODGING_HEIGHT_THRESHOLD           autocvar_sv_dodging_height_threshold
92 #define PHYS_DODGING_DISTANCE_THRESHOLD         autocvar_sv_dodging_wall_distance_threshold
93 #define PHYS_DODGING_RAMP_TIME                          autocvar_sv_dodging_ramp_time
94 #define PHYS_DODGING_UP_SPEED                           autocvar_sv_dodging_up_speed
95 #define PHYS_DODGING_WALL                                       autocvar_sv_dodging_wall_dodging
96
97
98 void dodging_UpdateStats()
99 {
100         self.stat_dodging = PHYS_DODGING;
101         self.stat_dodging_delay = PHYS_DODGING_DELAY;
102         self.stat_dodging_timeout = PHYS_DODGING_TIMEOUT(self);
103         self.stat_dodging_horiz_speed_frozen = PHYS_DODGING_HORIZ_SPEED_FROZEN;
104         self.stat_dodging_frozen = PHYS_DODGING_FROZEN;
105         self.stat_dodging_frozen_nodoubletap = PHYS_DODGING_FROZEN_NODOUBLETAP;
106         self.stat_dodging_height_threshold = PHYS_DODGING_HEIGHT_THRESHOLD;
107         self.stat_dodging_distance_threshold = PHYS_DODGING_DISTANCE_THRESHOLD;
108         self.stat_dodging_ramp_time = PHYS_DODGING_RAMP_TIME;
109         self.stat_dodging_up_speed = PHYS_DODGING_UP_SPEED;
110         self.stat_dodging_wall = PHYS_DODGING_WALL;
111 }
112
113 void dodging_Initialize()
114 {
115         addstat(STAT_DODGING, AS_INT, stat_dodging);
116         addstat(STAT_DODGING_DELAY, AS_FLOAT, stat_dodging_delay);
117         addstat(STAT_DODGING_TIMEOUT, AS_FLOAT, cvar_cl_dodging_timeout); // we stat this, so it is updated on the client when updated on server (otherwise, chaos)
118         addstat(STAT_DODGING_FROZEN_NO_DOUBLETAP, AS_INT, stat_dodging_frozen_nodoubletap);
119         addstat(STAT_DODGING_HORIZ_SPEED_FROZEN, AS_FLOAT, stat_dodging_horiz_speed_frozen);
120         addstat(STAT_DODGING_FROZEN, AS_INT, stat_dodging_frozen);
121         addstat(STAT_DODGING_HORIZ_SPEED, AS_FLOAT, stat_dodging_horiz_speed);
122         addstat(STAT_DODGING_HEIGHT_THRESHOLD, AS_FLOAT, stat_dodging_height_threshold);
123         addstat(STAT_DODGING_DISTANCE_THRESHOLD, AS_FLOAT, stat_dodging_distance_threshold);
124         addstat(STAT_DODGING_RAMP_TIME, AS_FLOAT, stat_dodging_ramp_time);
125         addstat(STAT_DODGING_UP_SPEED, AS_FLOAT, stat_dodging_up_speed);
126         addstat(STAT_DODGING_WALL, AS_FLOAT, stat_dodging_wall);
127 }
128
129 #endif
130 #ifdef CSQC
131 // instantly updates pressed keys, for use with dodging (may be out of date, but we can't care)
132 void PM_dodging_updatepressedkeys()
133 {
134         if (PHYS_INPUT_MOVEVALUES(self)_x > 0) // get if movement keys are pressed
135         {       // forward key pressed
136                 self.pressedkeys |= KEY_FORWARD;
137                 self.pressedkeys &= ~KEY_BACKWARD;
138         }
139         else if (PHYS_INPUT_MOVEVALUES(self)_x < 0)
140         {       // backward key pressed
141                 self.pressedkeys |= KEY_BACKWARD;
142                 self.pressedkeys &= ~KEY_FORWARD;
143         }
144         else
145         {       // no x input
146                 self.pressedkeys &= ~KEY_FORWARD;
147                 self.pressedkeys &= ~KEY_BACKWARD;
148         }
149
150         if (PHYS_INPUT_MOVEVALUES(self)_y > 0)
151         {       // right key pressed
152                 self.pressedkeys |= KEY_RIGHT;
153                 self.pressedkeys &= ~KEY_LEFT;
154         }
155         else if (PHYS_INPUT_MOVEVALUES(self)_y < 0)
156         {       // left key pressed
157                 self.pressedkeys |= KEY_LEFT;
158                 self.pressedkeys &= ~KEY_RIGHT;
159         }
160         else
161         {       // no y input
162                 self.pressedkeys &= ~KEY_RIGHT;
163                 self.pressedkeys &= ~KEY_LEFT;
164         }
165
166         if (PHYS_INPUT_BUTTONS(self) & 2) // get if jump and crouch keys are pressed
167                 self.pressedkeys |= KEY_JUMP;
168         else
169                 self.pressedkeys &= ~KEY_JUMP;
170         if (PHYS_INPUT_BUTTONS(self) & 16)
171                 self.pressedkeys |= KEY_CROUCH;
172         else
173                 self.pressedkeys &= ~KEY_CROUCH;
174
175         if (PHYS_INPUT_BUTTONS(self) & 1)
176                 self.pressedkeys |= KEY_ATCK;
177         else
178                 self.pressedkeys &= ~KEY_ATCK;
179         if (PHYS_INPUT_BUTTONS(self) & 4)
180                 self.pressedkeys |= KEY_ATCK2;
181         else
182                 self.pressedkeys &= ~KEY_ATCK2;
183 }
184 #endif
185
186 // returns 1 if the player is close to a wall
187 float check_close_to_wall(float threshold)
188 {
189         if (PHYS_DODGING_WALL == 0) { return 0; }
190
191         vector trace_start;
192         vector trace_end;
193
194         trace_start = self.origin;
195
196         trace_end = self.origin + (1000*v_right);
197         tracebox(trace_start, self.mins, self.maxs, trace_end, TRUE, self);
198         if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
199                 return 1;
200
201         trace_end = self.origin - (1000*v_right);
202         tracebox(trace_start, self.mins, self.maxs, trace_end, TRUE, self);
203         if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
204                 return 1;
205
206         trace_end = self.origin + (1000*v_forward);
207         tracebox(trace_start, self.mins, self.maxs, trace_end, TRUE, self);
208         if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
209                 return 1;
210
211         trace_end = self.origin - (1000*v_forward);
212         tracebox(trace_start, self.mins, self.maxs, trace_end, TRUE, self);
213         if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
214                 return 1;
215
216         return 0;
217 }
218
219 float check_close_to_ground(float threshold)
220 {
221         if (IS_ONGROUND(self))
222                 return 1;
223
224         return 0;
225 }
226
227 void PM_dodging_checkpressedkeys()
228 {
229         if(!PHYS_DODGING) { return; }
230
231         float length;
232         float tap_direction_x;
233         float tap_direction_y;
234
235         tap_direction_x = 0;
236         tap_direction_y = 0;
237
238         float frozen_dodging, frozen_no_doubletap;
239         frozen_dodging = (PHYS_FROZEN(self) && PHYS_DODGING_FROZEN);
240         frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_NODOUBLETAP);
241
242         float dodge_detected;
243
244         dodge_detected = 0;
245
246         // first check if the last dodge is far enough back in time so we can dodge again
247         if ((time - self.last_dodging_time) < PHYS_DODGING_DELAY)
248                 return;
249
250         makevectors(PHYS_INPUT_ANGLES(self));
251
252         if (check_close_to_ground(PHYS_DODGING_HEIGHT_THRESHOLD) != 1
253                 && check_close_to_wall(PHYS_DODGING_DISTANCE_THRESHOLD) != 1)
254                 return;
255
256         if (PHYS_INPUT_MOVEVALUES(self)_x > 0)
257         {
258                 // is this a state change?
259                 if (!(PHYS_DODGING_PRESSED_KEYS(self) & KEY_FORWARD) || frozen_no_doubletap)
260                 {
261                         if ((time - self.last_FORWARD_KEY_time) < PHYS_DODGING_TIMEOUT(self))
262                         {
263                                 tap_direction_x = 1.0;
264                                 dodge_detected = 1;
265                         }
266                         self.last_FORWARD_KEY_time = time;
267                 }
268         }
269
270         if (PHYS_INPUT_MOVEVALUES(self)_x < 0)
271         {
272                 // is this a state change?
273                 if (!(PHYS_DODGING_PRESSED_KEYS(self) & KEY_BACKWARD) || frozen_no_doubletap)
274                 {
275                         tap_direction_x = -1.0;
276                         if ((time - self.last_BACKWARD_KEY_time) < PHYS_DODGING_TIMEOUT(self))
277                         {
278                                 dodge_detected = 1;
279                         }
280                         self.last_BACKWARD_KEY_time = time;
281                 }
282         }
283
284         if (PHYS_INPUT_MOVEVALUES(self)_y > 0)
285         {
286                 // is this a state change?
287                 if (!(PHYS_DODGING_PRESSED_KEYS(self) & KEY_RIGHT) || frozen_no_doubletap)
288                 {
289                         tap_direction_y = 1.0;
290                         if ((time - self.last_RIGHT_KEY_time) < PHYS_DODGING_TIMEOUT(self))
291                         {
292                                 dodge_detected = 1;
293                         }
294                         self.last_RIGHT_KEY_time = time;
295                 }
296         }
297
298         if (PHYS_INPUT_MOVEVALUES(self)_y < 0)
299         {
300                 // is this a state change?
301                 if (!(PHYS_DODGING_PRESSED_KEYS(self) & KEY_LEFT) || frozen_no_doubletap)
302                 {
303                         tap_direction_y = -1.0;
304                         if ((time - self.last_LEFT_KEY_time) < PHYS_DODGING_TIMEOUT(self))
305                         {
306                                 dodge_detected = 1;
307                         }
308                         self.last_LEFT_KEY_time = time;
309                 }
310         }
311
312         if (dodge_detected == 1)
313         {
314                 self.last_dodging_time = time;
315
316                 self.dodging_action = 1;
317                 self.dodging_single_action = 1;
318
319                 self.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED;
320
321                 self.dodging_direction_x = tap_direction_x;
322                 self.dodging_direction_y = tap_direction_y;
323
324                 // normalize the dodging_direction vector.. (unlike UT99) XD
325                 length =          self.dodging_direction_x * self.dodging_direction_x;
326                 length = length + self.dodging_direction_y * self.dodging_direction_y;
327                 length = sqrt(length);
328
329                 self.dodging_direction_x = self.dodging_direction_x * 1.0/length;
330                 self.dodging_direction_y = self.dodging_direction_y * 1.0/length;
331         }
332 }
333
334 void PM_dodging()
335 {
336         if(!PHYS_DODGING) { return; }
337
338         float common_factor;
339         float new_velocity_gain;
340         float velocity_difference;
341         float clean_up_and_do_nothing;
342         float horiz_speed = PHYS_DODGING_HORIZ_SPEED;
343
344 #ifdef SVQC
345         dodging_UpdateStats();
346 #endif
347
348         if(PHYS_FROZEN(self))
349                 horiz_speed = PHYS_DODGING_HORIZ_SPEED_FROZEN;
350
351 #ifdef SVQC
352     if (self.deadflag != DEAD_NO)
353         return;
354 #endif
355
356         new_velocity_gain = 0;
357         clean_up_and_do_nothing = 0;
358
359         // when swimming, no dodging allowed..
360         if (self.waterlevel >= WATERLEVEL_SWIMMING)
361                 clean_up_and_do_nothing = 1;
362
363         if (clean_up_and_do_nothing != 0)
364         {
365                 self.dodging_action = 0;
366                 self.dodging_direction_x = 0;
367                 self.dodging_direction_y = 0;
368                 return;
369         }
370
371         // make sure v_up, v_right and v_forward are sane
372         makevectors(PHYS_INPUT_ANGLES(self));
373
374         // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code
375         // will be called ramp_time/frametime times = 2 times. so, we need to
376         // add 0.5 * the total speed each frame until the dodge action is done..
377         common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME;
378
379         // if ramp time is smaller than frametime we get problems ;D
380         if (common_factor > 1)
381                 common_factor = 1;
382
383         new_velocity_gain = self.dodging_velocity_gain - (common_factor * horiz_speed);
384         if (new_velocity_gain < 0)
385                 new_velocity_gain = 0;
386
387         velocity_difference = self.dodging_velocity_gain - new_velocity_gain;
388
389         // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D
390         if (self.dodging_action == 1)
391         {
392                 //disable jump key during dodge accel phase
393 #ifdef SVQC
394                 if(self.movement_z > 0) self.movement_z = 0;
395 #elif defined(CSQC)
396                 if(input_movevalues_z > 0) input_movevalues_z = 0;
397                 print("DODGE!\n");
398 #endif
399
400                 self.velocity =
401                           self.velocity
402                         + ((self.dodging_direction_y * velocity_difference) * v_right)
403                         + ((self.dodging_direction_x * velocity_difference) * v_forward);
404
405                 self.dodging_velocity_gain = self.dodging_velocity_gain - velocity_difference;
406         }
407
408         // the up part of the dodge is a single shot action
409         if (self.dodging_single_action == 1)
410         {
411                 UNSET_ONGROUND(self);
412
413                 self.velocity =
414                           self.velocity
415                         + (PHYS_DODGING_UP_SPEED * v_up);
416
417 #ifdef SVQC
418                 if (autocvar_sv_dodging_sound == 1)
419                         PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
420
421                 animdecide_setaction(self, ANIMACTION_JUMP, TRUE);
422 #endif
423
424                 self.dodging_single_action = 0;
425         }
426
427         // are we done with the dodging ramp yet?
428         if((self.dodging_action == 1) && ((time - self.last_dodging_time) > PHYS_DODGING_RAMP_TIME))
429         {
430                 // reset state so next dodge can be done correctly
431                 self.dodging_action = 0;
432                 self.dodging_direction_x = 0;
433                 self.dodging_direction_y = 0;
434         }
435 }
436
437 #ifdef SVQC
438
439 MUTATOR_HOOKFUNCTION(dodging_GetCvars)
440 {
441         GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_dodging_timeout, "cl_dodging_timeout");
442         return 0;
443 }
444
445 MUTATOR_HOOKFUNCTION(dodging_PlayerPhysics)
446 {
447         // print("dodging_PlayerPhysics\n");
448         PM_dodging();
449
450         return 0;
451 }
452
453 MUTATOR_HOOKFUNCTION(dodging_GetPressedKeys)
454 {
455         PM_dodging_checkpressedkeys();
456
457         return 0;
458 }
459
460 MUTATOR_DEFINITION(mutator_dodging)
461 {
462         // we need to be called before GetPressedKey does its thing so we can
463         // detect state changes and therefore dodging actions..
464         MUTATOR_HOOK(GetPressedKeys, dodging_GetPressedKeys, CBC_ORDER_ANY);
465
466         // in the physics hook we actually implement the dodge..
467         MUTATOR_HOOK(PlayerPhysics, dodging_PlayerPhysics, CBC_ORDER_ANY);
468
469         // get timeout information from the client, so the client can configure it..
470         MUTATOR_HOOK(GetCvars, dodging_GetCvars, CBC_ORDER_ANY);
471
472         // this just turns on the cvar.
473         MUTATOR_ONADD
474         {
475                 g_dodging = cvar("g_dodging");
476                 dodging_Initialize();
477         }
478
479         // this just turns off the cvar.
480         MUTATOR_ONROLLBACK_OR_REMOVE
481         {
482                 g_dodging = 0;
483         }
484
485         MUTATOR_ONREMOVE
486         {
487         }
488
489         return 0;
490 }
491 #endif
492
493 // we use this elsewhere, but let's undefine it anyway
494 #undef PHYS_DODGING_FROZEN
495 #undef PHYS_INPUT_MOVEVALUES
496 #undef PHYS_INPUT_ANGLES
497 #undef PHYS_INPUT_BUTTONS
498 #undef UNSET_ONGROUND
499 #undef PHYS_FROZEN
500 #undef IS_ONGROUND