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