]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/mutators/mutator_dodging.qc
DODGING: first part of ruling out small jump+dodge glitch
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / mutator_dodging.qc
1
2 .float cvar_cl_dodging_timeout;
3
4
5 // these are used to store the last key press time for each of the keys..
6 .float last_FORWARD_KEY_time;
7 .float last_BACKWARD_KEY_time;
8 .float last_LEFT_KEY_time;
9 .float last_RIGHT_KEY_time;
10 .float last_JUMP_KEY_time;
11
12 // these store the movement direction at the time of the dodge action happening.
13 .float dodging_direction_x;
14 .float dodging_direction_y;
15
16 // this indicates the last time a dodge was executed. used to check if another one is allowed
17 // and to ramp up the dodge acceleration in the physics hook.
18 .float last_dodging_time;
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 void dodging_Initialize() {
27         // print("dodging_Initialize\n");
28
29         self.last_FORWARD_KEY_time = 0;
30         self.last_BACKWARD_KEY_time = 0;
31         self.last_RIGHT_KEY_time = 0;
32         self.last_LEFT_KEY_time = 0;
33         self.last_JUMP_KEY_time = 0;
34         self.last_dodging_time = 0;
35         self.dodging_action = 0;
36         self.dodging_single_action = 0;
37         self.dodging_direction_x = 0;
38         self.dodging_direction_y = 0;
39 }
40
41 MUTATOR_HOOKFUNCTION(dodging_GetCvars) {
42         GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_dodging_timeout, "cl_dodging_timeout");
43         return 0;
44 }
45
46 MUTATOR_HOOKFUNCTION(dodging_PlayerPhysics) {
47         // print("dodging_PlayerPhysics\n");
48
49         float common_factor;
50
51         float clean_up_and_do_nothing;
52
53         clean_up_and_do_nothing = 0;
54
55         if (g_dodging == 0)
56                 clean_up_and_do_nothing = 1;
57
58         // when swimming, no dodging allowed..
59         if (self.waterlevel >= WATERLEVEL_SWIMMING)
60                 clean_up_and_do_nothing = 1;
61
62         if (clean_up_and_do_nothing != 0) {
63                 self.dodging_action = 0;
64                 return 0;
65         }
66
67         // make sure v_up, v_right and v_forward are sane
68         makevectors(self.angles);
69
70         // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code 
71         // will be called ramp_time/frametime times = 2 times. so, we need to 
72         // add 0.5 * the total speed each frame until the dodge action is done..
73         common_factor = sys_frametime / cvar("sv_dodging_ramp_time");
74
75         // if ramp time is smaller than frametime we get problems ;D
76         if (common_factor > 1) 
77                 common_factor = 1;
78
79
80         // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D
81         if (self.dodging_action == 1) {
82                 //disable jump key during dodge accel phase
83                 if (self.movement_z > 0) self.movement_z = 0;
84
85                 self.velocity = 
86                           self.velocity 
87                         + (common_factor * (self.dodging_direction_y * cvar("sv_dodging_horiz_speed")) * v_right) 
88                         + (common_factor * (self.dodging_direction_x * cvar("sv_dodging_horiz_speed")) * v_forward);
89         }
90
91         // the up part of the dodge is a single shot action
92         if (self.dodging_single_action == 1) {
93                 self.velocity = 
94                           self.velocity 
95                         + (cvar("sv_dodging_up_speed") * v_up);
96
97                 if (cvar("sv_dodging_sound") == 1)
98                         PlayerSound(playersound_jump, CHAN_PLAYER, VOICETYPE_PLAYERSOUND);
99
100                 setanim(self, self.anim_jump, TRUE, FALSE, TRUE);
101
102                 self.dodging_single_action = 0;
103         }
104
105         // are we done with the dodging ramp yet?
106         if((self.dodging_action == 1) && ((time - self.last_dodging_time) > cvar("sv_dodging_ramp_time")))
107         {
108                 // reset state so next dodge can be done correctly
109                 self.dodging_action = 0;
110                 self.dodging_direction_x = 0;
111                 self.dodging_direction_y = 0;
112         }
113
114         return 0;
115 }
116
117
118 // returns 1 if the player is close to a wall
119 float check_close_to_wall(float threshold) {
120         if (cvar("sv_dodging_wall_dodging") == 0)
121                 return 0;
122
123         vector trace_start;
124         vector trace_end;
125
126         trace_start = self.origin;
127
128         trace_end = self.origin + (1000*v_right);
129         tracebox(trace_start, self.mins, self.maxs, trace_end, TRUE, self);
130         if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
131                 return 1;
132
133         trace_end = self.origin - (1000*v_right);
134         tracebox(trace_start, self.mins, self.maxs, trace_end, TRUE, self);
135         if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
136                 return 1;
137
138         trace_end = self.origin + (1000*v_forward);
139         tracebox(trace_start, self.mins, self.maxs, trace_end, TRUE, self);
140         if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
141                 return 1;
142
143         trace_end = self.origin - (1000*v_forward);
144         tracebox(trace_start, self.mins, self.maxs, trace_end, TRUE, self);
145         if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
146                 return 1;
147
148         return 0;
149 }
150
151 float check_close_to_ground(float threshold) {
152         vector trace_start;
153         vector trace_end;
154
155         // determine height above ground is below a threshold
156         trace_start = self.origin;
157         trace_end = self.origin - (1000*v_up);
158
159         tracebox(trace_start, self.mins, self.maxs, trace_end, TRUE, self);
160
161         // check if the trace hit anything at all
162         if (trace_fraction > 1)
163                 return 0;
164
165         if(self.origin_z - trace_endpos_z < threshold) 
166                 return 1;
167
168         return 0;
169 }
170
171
172 MUTATOR_HOOKFUNCTION(dodging_GetPressedKeys) {
173         // print("dodging_PlayerPhysics\n");
174
175         float length;
176         float dodge_detected;
177         if (g_dodging == 0)
178                 return 0;
179
180         dodge_detected = 0;
181
182         // no dodging and jumping at the same time..
183         if (self.BUTTON_JUMP)
184                 return 0;
185
186         // first check if the last dodge is far enough back in time so we can dodge again
187         if ((time - self.last_dodging_time) < cvar("sv_dodging_delay"))
188                 return 0;
189
190         if (check_close_to_ground(cvar("sv_dodging_height_threshold")) != 1 
191                 && check_close_to_wall(cvar("sv_dodging_wall_distance_threshold")) != 1)
192                 return 0;
193
194         // remember last jump key time, so we can check in dodging code, if it
195         // was pressed between the two dodges..
196         if (self.BUTTON_JUMP)
197                 self.last_JUMP_KEY_time = time;
198
199         if (self.movement_x > 0) {
200                 // is this a state change?
201                 if (!(self.pressedkeys & KEY_FORWARD)) {
202                         if ((time - self.last_FORWARD_KEY_time) < self.cvar_cl_dodging_timeout) { 
203                                 dodge_detected = 1;
204                                 self.dodging_direction_x = 1.0;
205                         }
206                         self.last_FORWARD_KEY_time = time;
207                 }
208         }
209
210         if (self.movement_x < 0) {
211                 // is this a state change?
212                 if (!(self.pressedkeys & KEY_BACKWARD)) {
213                         if ((time - self.last_BACKWARD_KEY_time) < self.cvar_cl_dodging_timeout)        { 
214                                 dodge_detected = 1;
215                                 self.dodging_direction_x = -1.0;
216                         }
217                         self.last_BACKWARD_KEY_time = time;
218                 }
219         }
220
221         if (self.movement_y > 0) {
222                 // is this a state change?
223                 if (!(self.pressedkeys & KEY_RIGHT)) {
224                         if ((time - self.last_RIGHT_KEY_time) < self.cvar_cl_dodging_timeout)   { 
225                                 dodge_detected = 1;
226                                 self.dodging_direction_y = 1.0;
227                         }
228                         self.last_RIGHT_KEY_time = time;
229                 }
230         }
231
232         if (self.movement_y < 0) {
233                 // is this a state change?
234                 if (!(self.pressedkeys & KEY_LEFT)) {
235                         if ((time - self.last_LEFT_KEY_time) < self.cvar_cl_dodging_timeout)    { 
236                                 dodge_detected = 1;
237                                 self.dodging_direction_y = -1.0;
238                         }
239                         self.last_LEFT_KEY_time = time;
240                 }
241         }
242
243
244
245         if (dodge_detected == 1) {
246                 // If the player pressed JUMP between the two taps, disallow dodging,
247                 // cause he obviously wants to jump instead
248                 if ((time - self.last_JUMP_KEY_time) < self.cvar_cl_dodging_timeout)
249                         return 0;
250
251                 self.last_dodging_time = time;
252
253                 self.dodging_action = 1;
254                 self.dodging_single_action = 1;
255
256                 // normalize the dodging_direction vector.. (unlike UT99) XD
257                 length = length + self.dodging_direction_x * self.dodging_direction_x;
258                 length = length + self.dodging_direction_y * self.dodging_direction_y;
259                 length = sqrt(length);
260
261                 self.dodging_direction_x = self.dodging_direction_x * 1.0/length;
262                 self.dodging_direction_y = self.dodging_direction_y * 1.0/length;
263         }
264
265         return 0;
266 }
267
268 MUTATOR_DEFINITION(dodging)
269 {
270         // we need to be called before GetPressedKey does its thing so we can
271         // detect state changes and therefore dodging actions..
272         MUTATOR_HOOK(GetPressedKeys, dodging_GetPressedKeys, CBC_ORDER_ANY);
273
274         // in the physics hook we actually implement the dodge..
275         MUTATOR_HOOK(PlayerPhysics, dodging_PlayerPhysics, CBC_ORDER_ANY);
276
277         // get timeout information from the client, so the client can configure it..
278         MUTATOR_HOOK(GetCvars, dodging_GetCvars, CBC_ORDER_ANY);
279
280         // this just turns on the cvar.
281         MUTATOR_ONADD
282         {
283                 g_dodging = 1;
284                 dodging_Initialize();
285         }
286
287         // this just turns off the cvar.
288         MUTATOR_ONREMOVE
289         {        
290                 g_dodging = 0;
291         }
292
293         return 0;
294 }