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)
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
33 .float cvar_cl_dodging_timeout;
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;
50 // set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done..
51 .float dodging_action;
53 // the jump part of the dodge cannot be ramped
54 .float dodging_single_action;
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;
63 // these store the movement direction at the time of the dodge action happening.
64 .float dodging_direction_x;
65 .float dodging_direction_y;
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;
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
74 .float dodging_velocity_gain;
81 void dodging_UpdateStats()
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;
96 void dodging_Initialize()
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);
114 // instantly updates pressed keys, for use with dodging (may be out of date, but we can't care)
115 void PM_dodging_updatepressedkeys()
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;
122 else if (PHYS_INPUT_MOVEVALUES(self)_x < 0)
123 { // backward key pressed
124 self.pressedkeys |= KEY_BACKWARD;
125 self.pressedkeys &= ~KEY_FORWARD;
129 self.pressedkeys &= ~KEY_FORWARD;
130 self.pressedkeys &= ~KEY_BACKWARD;
133 if (PHYS_INPUT_MOVEVALUES(self)_y > 0)
134 { // right key pressed
135 self.pressedkeys |= KEY_RIGHT;
136 self.pressedkeys &= ~KEY_LEFT;
138 else if (PHYS_INPUT_MOVEVALUES(self)_y < 0)
139 { // left key pressed
140 self.pressedkeys |= KEY_LEFT;
141 self.pressedkeys &= ~KEY_RIGHT;
145 self.pressedkeys &= ~KEY_RIGHT;
146 self.pressedkeys &= ~KEY_LEFT;
149 if (PHYS_INPUT_BUTTONS(self) & 2) // get if jump and crouch keys are pressed
150 self.pressedkeys |= KEY_JUMP;
152 self.pressedkeys &= ~KEY_JUMP;
153 if (PHYS_INPUT_BUTTONS(self) & 16)
154 self.pressedkeys |= KEY_CROUCH;
156 self.pressedkeys &= ~KEY_CROUCH;
158 if (PHYS_INPUT_BUTTONS(self) & 1)
159 self.pressedkeys |= KEY_ATCK;
161 self.pressedkeys &= ~KEY_ATCK;
162 if (PHYS_INPUT_BUTTONS(self) & 4)
163 self.pressedkeys |= KEY_ATCK2;
165 self.pressedkeys &= ~KEY_ATCK2;
169 // returns 1 if the player is close to a wall
170 float check_close_to_wall(float threshold)
172 if (PHYS_DODGING_WALL == 0) { return FALSE; }
177 trace_start = self.origin;
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)
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)
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)
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)
202 float check_close_to_ground(float threshold)
204 if (IS_ONGROUND(self))
210 void PM_dodging_checkpressedkeys()
212 if(!PHYS_DODGING) { return; }
215 float tap_direction_x;
216 float tap_direction_y;
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);
225 float dodge_detected = 0;
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)
231 makevectors(PHYS_WORLD_ANGLES(self));
233 if (check_close_to_ground(PHYS_DODGING_HEIGHT_THRESHOLD) != 1
234 && check_close_to_wall(PHYS_DODGING_DISTANCE_THRESHOLD) != 1)
237 if (PHYS_INPUT_MOVEVALUES(self)_x > 0)
239 // is this a state change?
240 if (!(PHYS_DODGING_PRESSED_KEYS(self) & KEY_FORWARD) || frozen_no_doubletap)
242 if ((time - self.last_FORWARD_KEY_time) < PHYS_DODGING_TIMEOUT(self))
244 tap_direction_x = 1.0;
247 self.last_FORWARD_KEY_time = time;
251 if (PHYS_INPUT_MOVEVALUES(self)_x < 0)
253 // is this a state change?
254 if (!(PHYS_DODGING_PRESSED_KEYS(self) & KEY_BACKWARD) || frozen_no_doubletap)
256 tap_direction_x = -1.0;
257 if ((time - self.last_BACKWARD_KEY_time) < PHYS_DODGING_TIMEOUT(self))
261 self.last_BACKWARD_KEY_time = time;
265 if (PHYS_INPUT_MOVEVALUES(self)_y > 0)
267 // is this a state change?
268 if (!(PHYS_DODGING_PRESSED_KEYS(self) & KEY_RIGHT) || frozen_no_doubletap)
270 tap_direction_y = 1.0;
271 if ((time - self.last_RIGHT_KEY_time) < PHYS_DODGING_TIMEOUT(self))
275 self.last_RIGHT_KEY_time = time;
279 if (PHYS_INPUT_MOVEVALUES(self)_y < 0)
281 // is this a state change?
282 if (!(PHYS_DODGING_PRESSED_KEYS(self) & KEY_LEFT) || frozen_no_doubletap)
284 tap_direction_y = -1.0;
285 if ((time - self.last_LEFT_KEY_time) < PHYS_DODGING_TIMEOUT(self))
289 self.last_LEFT_KEY_time = time;
293 if (dodge_detected == 1)
295 self.last_dodging_time = time;
297 self.dodging_action = 1;
298 self.dodging_single_action = 1;
300 self.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED;
302 self.dodging_direction_x = tap_direction_x;
303 self.dodging_direction_y = tap_direction_y;
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);
310 self.dodging_direction_x = self.dodging_direction_x * 1.0/length;
311 self.dodging_direction_y = self.dodging_direction_y * 1.0/length;
315 PM_dodging_updatepressedkeys();
321 if(!PHYS_DODGING) { return; }
324 float new_velocity_gain;
325 float velocity_difference;
326 float clean_up_and_do_nothing;
327 float horiz_speed = PHYS_DODGING_HORIZ_SPEED;
330 dodging_UpdateStats();
333 if(PHYS_FROZEN(self))
334 horiz_speed = PHYS_DODGING_HORIZ_SPEED_FROZEN;
339 new_velocity_gain = 0;
340 clean_up_and_do_nothing = 0;
342 // when swimming, no dodging allowed..
343 if (self.waterlevel >= WATERLEVEL_SWIMMING)
344 clean_up_and_do_nothing = 1;
346 if (clean_up_and_do_nothing != 0)
348 self.dodging_action = 0;
349 self.dodging_direction_x = 0;
350 self.dodging_direction_y = 0;
354 // make sure v_up, v_right and v_forward are sane
355 makevectors(PHYS_WORLD_ANGLES(self));
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;
362 // if ramp time is smaller than frametime we get problems ;D
363 if (common_factor > 1)
366 new_velocity_gain = self.dodging_velocity_gain - (common_factor * horiz_speed);
367 if (new_velocity_gain < 0)
368 new_velocity_gain = 0;
370 velocity_difference = self.dodging_velocity_gain - new_velocity_gain;
372 // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D
373 if (self.dodging_action == 1)
375 //disable jump key during dodge accel phase
376 if(PHYS_INPUT_MOVEVALUES(self)_z > 0) { PHYS_INPUT_MOVEVALUES(self)_z = 0; }
380 + ((self.dodging_direction_y * velocity_difference) * v_right)
381 + ((self.dodging_direction_x * velocity_difference) * v_forward);
383 self.dodging_velocity_gain = self.dodging_velocity_gain - velocity_difference;
386 // the up part of the dodge is a single shot action
387 if (self.dodging_single_action == 1)
389 UNSET_ONGROUND(self);
393 + (PHYS_DODGING_UP_SPEED * v_up);
396 if (autocvar_sv_dodging_sound == 1)
397 PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
399 animdecide_setaction(self, ANIMACTION_JUMP, TRUE);
402 self.dodging_single_action = 0;
405 // are we done with the dodging ramp yet?
406 if((self.dodging_action == 1) && ((time - self.last_dodging_time) > PHYS_DODGING_RAMP_TIME))
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;
415 PM_dodging_checkpressedkeys();
421 MUTATOR_HOOKFUNCTION(dodging_GetCvars)
423 GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_dodging_timeout, "cl_dodging_timeout");
427 MUTATOR_HOOKFUNCTION(dodging_PlayerPhysics)
429 // print("dodging_PlayerPhysics\n");
435 MUTATOR_HOOKFUNCTION(dodging_GetPressedKeys)
437 PM_dodging_checkpressedkeys();
442 MUTATOR_DEFINITION(mutator_dodging)
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);
448 // in the physics hook we actually implement the dodge..
449 MUTATOR_HOOK(PlayerPhysics, dodging_PlayerPhysics, CBC_ORDER_ANY);
451 // get timeout information from the client, so the client can configure it..
452 MUTATOR_HOOK(GetCvars, dodging_GetCvars, CBC_ORDER_ANY);
454 // this just turns on the cvar.
457 g_dodging = cvar("g_dodging");
458 dodging_Initialize();
461 // this just turns off the cvar.
462 MUTATOR_ONROLLBACK_OR_REMOVE