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