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