]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/mutators/mutator_dodging.qc
Merge branch 'master' into TimePath/experiments/csqc_prediction
[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_delay;
37 .float stat_dodging_horiz_speed_frozen;
38 .float stat_dodging_frozen_nodoubletap;
39 .float stat_dodging_frozen;
40 .float stat_dodging_horiz_speed;
41 .float stat_dodging_height_threshold;
42 .float stat_dodging_distance_threshold;
43 .float stat_dodging_ramp_time;
44 .float stat_dodging_up_speed;
45 .float stat_dodging_wall;
46
47 #endif
48
49 // set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done..
50 .float dodging_action;
51
52 // the jump part of the dodge cannot be ramped
53 .float dodging_single_action;
54
55
56 // these are used to store the last key press time for each of the keys..
57 .float last_FORWARD_KEY_time;
58 .float last_BACKWARD_KEY_time;
59 .float last_LEFT_KEY_time;
60 .float last_RIGHT_KEY_time;
61
62 // these store the movement direction at the time of the dodge action happening.
63 .float dodging_direction_x;
64 .float dodging_direction_y;
65
66 // this indicates the last time a dodge was executed. used to check if another one is allowed
67 // and to ramp up the dodge acceleration in the physics hook.
68 .float last_dodging_time;
69
70 // This is the velocity gain to be added over the ramp time.
71 // It will decrease from frame to frame during dodging_action = 1
72 // until it's 0.
73 .float dodging_velocity_gain;
74
75 #ifdef CSQC
76 .float pressedkeys;
77
78 #elif defined(SVQC)
79
80 void dodging_UpdateStats()
81 {
82         self.stat_dodging = PHYS_DODGING;
83         self.stat_dodging_delay = PHYS_DODGING_DELAY;
84         self.stat_dodging_horiz_speed_frozen = PHYS_DODGING_HORIZ_SPEED_FROZEN;
85         self.stat_dodging_frozen = PHYS_DODGING_FROZEN;
86         self.stat_dodging_frozen_nodoubletap = PHYS_DODGING_FROZEN_NODOUBLETAP;
87         self.stat_dodging_height_threshold = PHYS_DODGING_HEIGHT_THRESHOLD;
88         self.stat_dodging_distance_threshold = PHYS_DODGING_DISTANCE_THRESHOLD;
89         self.stat_dodging_ramp_time = PHYS_DODGING_RAMP_TIME;
90         self.stat_dodging_up_speed = PHYS_DODGING_UP_SPEED;
91         self.stat_dodging_wall = PHYS_DODGING_WALL;
92 }
93
94 void dodging_Initialize()
95 {
96         addstat(STAT_DODGING, AS_INT, stat_dodging);
97         addstat(STAT_DODGING_DELAY, AS_FLOAT, stat_dodging_delay);
98         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)
99         addstat(STAT_DODGING_FROZEN_NO_DOUBLETAP, AS_INT, stat_dodging_frozen_nodoubletap);
100         addstat(STAT_DODGING_HORIZ_SPEED_FROZEN, AS_FLOAT, stat_dodging_horiz_speed_frozen);
101         addstat(STAT_DODGING_FROZEN, AS_INT, stat_dodging_frozen);
102         addstat(STAT_DODGING_HORIZ_SPEED, AS_FLOAT, stat_dodging_horiz_speed);
103         addstat(STAT_DODGING_HEIGHT_THRESHOLD, AS_FLOAT, stat_dodging_height_threshold);
104         addstat(STAT_DODGING_DISTANCE_THRESHOLD, AS_FLOAT, stat_dodging_distance_threshold);
105         addstat(STAT_DODGING_RAMP_TIME, AS_FLOAT, stat_dodging_ramp_time);
106         addstat(STAT_DODGING_UP_SPEED, AS_FLOAT, stat_dodging_up_speed);
107         addstat(STAT_DODGING_WALL, AS_FLOAT, stat_dodging_wall);
108 }
109
110 #endif
111
112 // returns 1 if the player is close to a wall
113 float check_close_to_wall(float threshold)
114 {
115         if (PHYS_DODGING_WALL == 0) { return FALSE; }
116
117         #define X(OFFSET)                                                                                                                               \
118         tracebox(self.origin, self.mins, self.maxs, self.origin + OFFSET, TRUE, self);  \
119         if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)                \
120                 return TRUE;
121         X(1000*v_right);
122         X(-1000*v_right);
123         X(1000*v_forward);
124         X(-1000*v_forward);
125         #undef X
126
127         return FALSE;
128 }
129
130 float check_close_to_ground(float threshold)
131 {
132         return IS_ONGROUND(self) ? TRUE : FALSE;
133 }
134
135 float PM_dodging_checkpressedkeys()
136 {
137         if(!PHYS_DODGING)
138                 return FALSE;
139
140         float frozen_dodging = (PHYS_FROZEN(self) && PHYS_DODGING_FROZEN);
141         float frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_NODOUBLETAP);
142
143         // first check if the last dodge is far enough back in time so we can dodge again
144         if ((time - self.last_dodging_time) < PHYS_DODGING_DELAY)
145                 return FALSE;
146
147         makevectors(PHYS_WORLD_ANGLES(self));
148
149         if (check_close_to_ground(PHYS_DODGING_HEIGHT_THRESHOLD) != 1
150                 && check_close_to_wall(PHYS_DODGING_DISTANCE_THRESHOLD) != 1)
151                 return TRUE;
152
153         float tap_direction_x = 0;
154         float tap_direction_y = 0;
155         float dodge_detected = 0;
156
157         #define X(COND,BTN,RESULT)                                                                                                                      \
158         if (PHYS_INPUT_MOVEVALUES(self)_##COND)                                                                                         \
159                 /* is this a state change? */                                                                                                   \
160                 if(!(PHYS_DODGING_PRESSED_KEYS(self) & KEY_##BTN) || frozen_no_doubletap) {             \
161                                 tap_direction_##RESULT;                                                                                                 \
162                                 if ((time - self.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(self))   \
163                                         dodge_detected = 1;                                                                                                     \
164                                 self.last_##BTN##_KEY_time = time;                                                                              \
165                 }
166         X(x < 0, BACKWARD,      x--);
167         X(x > 0, FORWARD,       x++);
168         X(y < 0, LEFT,          y--);
169         X(y > 0, RIGHT,         y++);
170         #undef X
171
172         if (dodge_detected == 1)
173         {
174                 self.last_dodging_time = time;
175
176                 self.dodging_action = 1;
177                 self.dodging_single_action = 1;
178
179                 self.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED;
180
181                 self.dodging_direction_x = tap_direction_x;
182                 self.dodging_direction_y = tap_direction_y;
183
184                 // normalize the dodging_direction vector.. (unlike UT99) XD
185                 float length = self.dodging_direction_x * self.dodging_direction_x
186                                         + self.dodging_direction_y * self.dodging_direction_y;
187                 length = sqrt(length);
188
189                 self.dodging_direction_x = self.dodging_direction_x * 1.0 / length;
190                 self.dodging_direction_y = self.dodging_direction_y * 1.0 / length;
191                 return TRUE;
192         }
193         return FALSE;
194 }
195
196 void PM_dodging()
197 {
198         if (!PHYS_DODGING)
199                 return;
200
201 #ifdef SVQC
202         dodging_UpdateStats();
203 #endif
204
205     if (PHYS_DEAD(self))
206         return;
207
208         // when swimming, no dodging allowed..
209         if (self.waterlevel >= WATERLEVEL_SWIMMING)
210         {
211                 self.dodging_action = 0;
212                 self.dodging_direction_x = 0;
213                 self.dodging_direction_y = 0;
214                 return;
215         }
216
217         // make sure v_up, v_right and v_forward are sane
218         makevectors(PHYS_WORLD_ANGLES(self));
219
220         // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code
221         // will be called ramp_time/frametime times = 2 times. so, we need to
222         // add 0.5 * the total speed each frame until the dodge action is done..
223         float common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME;
224
225         // if ramp time is smaller than frametime we get problems ;D
226         common_factor = min(common_factor, 1);
227
228         float horiz_speed = PHYS_FROZEN(self) ? PHYS_DODGING_HORIZ_SPEED_FROZEN : PHYS_DODGING_HORIZ_SPEED;
229         float new_velocity_gain = self.dodging_velocity_gain - (common_factor * horiz_speed);
230         new_velocity_gain = max(0, new_velocity_gain);
231
232         float velocity_difference = self.dodging_velocity_gain - new_velocity_gain;
233
234         // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D
235         if (self.dodging_action == 1)
236         {
237                 //disable jump key during dodge accel phase
238                 if(PHYS_INPUT_MOVEVALUES(self)_z > 0) { PHYS_INPUT_MOVEVALUES(self)_z = 0; }
239
240                 self.velocity += ((self.dodging_direction_y * velocity_difference) * v_right)
241                                         + ((self.dodging_direction_x * velocity_difference) * v_forward);
242
243                 self.dodging_velocity_gain = self.dodging_velocity_gain - velocity_difference;
244         }
245
246         // the up part of the dodge is a single shot action
247         if (self.dodging_single_action == 1)
248         {
249                 UNSET_ONGROUND(self);
250
251                 self.velocity += PHYS_DODGING_UP_SPEED * v_up;
252
253 #ifdef SVQC
254                 if (autocvar_sv_dodging_sound == 1)
255                         PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
256
257                 animdecide_setaction(self, ANIMACTION_JUMP, TRUE);
258 #endif
259
260                 self.dodging_single_action = 0;
261         }
262
263         // are we done with the dodging ramp yet?
264         if((self.dodging_action == 1) && ((time - self.last_dodging_time) > PHYS_DODGING_RAMP_TIME))
265         {
266                 // reset state so next dodge can be done correctly
267                 self.dodging_action = 0;
268                 self.dodging_direction_x = 0;
269                 self.dodging_direction_y = 0;
270         }
271 }
272
273 #ifdef SVQC
274
275 MUTATOR_HOOKFUNCTION(dodging_GetCvars)
276 {
277         GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_dodging_timeout, "cl_dodging_timeout");
278         return FALSE;
279 }
280
281 MUTATOR_HOOKFUNCTION(dodging_PlayerPhysics)
282 {
283         // print("dodging_PlayerPhysics\n");
284         PM_dodging();
285
286         return FALSE;
287 }
288
289 MUTATOR_HOOKFUNCTION(dodging_GetPressedKeys)
290 {
291         PM_dodging_checkpressedkeys();
292
293         return FALSE;
294 }
295
296 MUTATOR_DEFINITION(mutator_dodging)
297 {
298         // we need to be called before GetPressedKey does its thing so we can
299         // detect state changes and therefore dodging actions..
300         MUTATOR_HOOK(GetPressedKeys, dodging_GetPressedKeys, CBC_ORDER_ANY);
301
302         // in the physics hook we actually implement the dodge..
303         MUTATOR_HOOK(PlayerPhysics, dodging_PlayerPhysics, CBC_ORDER_ANY);
304
305         // get timeout information from the client, so the client can configure it..
306         MUTATOR_HOOK(GetCvars, dodging_GetCvars, CBC_ORDER_ANY);
307
308         // this just turns on the cvar.
309         MUTATOR_ONADD
310         {
311                 g_dodging = cvar("g_dodging");
312                 dodging_Initialize();
313         }
314
315         // this just turns off the cvar.
316         MUTATOR_ONROLLBACK_OR_REMOVE
317         {
318                 g_dodging = 0;
319         }
320
321         return FALSE;
322 }
323 #endif