]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/mutators/mutator_dodging.qc
Call the setpressedkeys functions in the right place (doesn't fix prediction, but...
[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
315 void PM_dodging()
316 {
317         if(!PHYS_DODGING) { return; }
318
319         float common_factor;
320         float new_velocity_gain;
321         float velocity_difference;
322         float clean_up_and_do_nothing;
323         float horiz_speed = PHYS_DODGING_HORIZ_SPEED;
324
325 #ifdef SVQC
326         dodging_UpdateStats();
327 #endif
328
329         if(PHYS_FROZEN(self))
330                 horiz_speed = PHYS_DODGING_HORIZ_SPEED_FROZEN;
331
332     if(PHYS_DEAD(self))
333         return;
334
335         new_velocity_gain = 0;
336         clean_up_and_do_nothing = 0;
337
338         // when swimming, no dodging allowed..
339         if (self.waterlevel >= WATERLEVEL_SWIMMING)
340                 clean_up_and_do_nothing = 1;
341
342         if (clean_up_and_do_nothing != 0)
343         {
344                 self.dodging_action = 0;
345                 self.dodging_direction_x = 0;
346                 self.dodging_direction_y = 0;
347                 return;
348         }
349
350         // make sure v_up, v_right and v_forward are sane
351         makevectors(PHYS_WORLD_ANGLES(self));
352
353         // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code
354         // will be called ramp_time/frametime times = 2 times. so, we need to
355         // add 0.5 * the total speed each frame until the dodge action is done..
356         common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME;
357
358         // if ramp time is smaller than frametime we get problems ;D
359         if (common_factor > 1)
360                 common_factor = 1;
361
362         new_velocity_gain = self.dodging_velocity_gain - (common_factor * horiz_speed);
363         if (new_velocity_gain < 0)
364                 new_velocity_gain = 0;
365
366         velocity_difference = self.dodging_velocity_gain - new_velocity_gain;
367
368         // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D
369         if (self.dodging_action == 1)
370         {
371                 //disable jump key during dodge accel phase
372                 if(PHYS_INPUT_MOVEVALUES(self)_z > 0) { PHYS_INPUT_MOVEVALUES(self)_z = 0; }
373
374                 self.velocity =
375                           self.velocity
376                         + ((self.dodging_direction_y * velocity_difference) * v_right)
377                         + ((self.dodging_direction_x * velocity_difference) * v_forward);
378
379                 self.dodging_velocity_gain = self.dodging_velocity_gain - velocity_difference;
380         }
381
382         // the up part of the dodge is a single shot action
383         if (self.dodging_single_action == 1)
384         {
385                 UNSET_ONGROUND(self);
386
387                 self.velocity =
388                           self.velocity
389                         + (PHYS_DODGING_UP_SPEED * v_up);
390
391 #ifdef SVQC
392                 if (autocvar_sv_dodging_sound == 1)
393                         PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
394
395                 animdecide_setaction(self, ANIMACTION_JUMP, TRUE);
396 #endif
397
398                 self.dodging_single_action = 0;
399         }
400
401         // are we done with the dodging ramp yet?
402         if((self.dodging_action == 1) && ((time - self.last_dodging_time) > PHYS_DODGING_RAMP_TIME))
403         {
404                 // reset state so next dodge can be done correctly
405                 self.dodging_action = 0;
406                 self.dodging_direction_x = 0;
407                 self.dodging_direction_y = 0;
408         }
409 }
410
411 #ifdef SVQC
412
413 MUTATOR_HOOKFUNCTION(dodging_GetCvars)
414 {
415         GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_dodging_timeout, "cl_dodging_timeout");
416         return FALSE;
417 }
418
419 MUTATOR_HOOKFUNCTION(dodging_PlayerPhysics)
420 {
421         // print("dodging_PlayerPhysics\n");
422         PM_dodging();
423
424         return FALSE;
425 }
426
427 MUTATOR_HOOKFUNCTION(dodging_GetPressedKeys)
428 {
429         PM_dodging_checkpressedkeys();
430
431         return FALSE;
432 }
433
434 MUTATOR_DEFINITION(mutator_dodging)
435 {
436         // we need to be called before GetPressedKey does its thing so we can
437         // detect state changes and therefore dodging actions..
438         MUTATOR_HOOK(GetPressedKeys, dodging_GetPressedKeys, CBC_ORDER_ANY);
439
440         // in the physics hook we actually implement the dodge..
441         MUTATOR_HOOK(PlayerPhysics, dodging_PlayerPhysics, CBC_ORDER_ANY);
442
443         // get timeout information from the client, so the client can configure it..
444         MUTATOR_HOOK(GetCvars, dodging_GetCvars, CBC_ORDER_ANY);
445
446         // this just turns on the cvar.
447         MUTATOR_ONADD
448         {
449                 g_dodging = cvar("g_dodging");
450                 dodging_Initialize();
451         }
452
453         // this just turns off the cvar.
454         MUTATOR_ONROLLBACK_OR_REMOVE
455         {
456                 g_dodging = 0;
457         }
458
459         return FALSE;
460 }
461 #endif