]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/mutators/mutator_dodging.qc
58d24a4d820c526376026c84bfa1c774e1506545
[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
11 // these store the movement direction at the time of the dodge action happening.
12 .vector dodging_direction;
13
14 // this indicates the last time a dodge was executed. used to check if another one is allowed
15 // and to ramp up the dodge acceleration in the physics hook.
16 .float last_dodging_time;
17
18 // This is the velocity gain to be added over the ramp time.
19 // It will decrease from frame to frame during dodging_action = 1
20 // until it's 0.
21 .float dodging_velocity_gain;
22
23 MUTATOR_HOOKFUNCTION(dodging_GetCvars) {
24         GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_dodging_timeout, "cl_dodging_timeout");
25         return 0;
26 }
27
28 MUTATOR_HOOKFUNCTION(dodging_PlayerPhysics) {
29         // print("dodging_PlayerPhysics\n");
30
31         float common_factor;
32         float new_velocity_gain;
33         float velocity_difference;
34         float clean_up_and_do_nothing;
35         float horiz_speed = autocvar_sv_dodging_horiz_speed;
36
37         if(self.frozen)
38                 horiz_speed = autocvar_sv_dodging_horiz_speed_frozen;
39
40     if (self.deadflag != DEAD_NO)
41         return 0;
42
43         new_velocity_gain = 0;
44         clean_up_and_do_nothing = 0;
45
46         if (g_dodging == 0)
47                 clean_up_and_do_nothing = 1;
48
49         // when swimming, no dodging allowed..
50         if (self.waterlevel >= WATERLEVEL_SWIMMING)
51                 clean_up_and_do_nothing = 1;
52
53         if (clean_up_and_do_nothing != 0) {
54                 self.dodging_action = 0;
55                 self.dodging_direction_x = 0;
56                 self.dodging_direction_y = 0;
57                 return 0;
58         }
59
60         // make sure v_up, v_right and v_forward are sane
61         makevectors(self.angles);
62
63         // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code
64         // will be called ramp_time/frametime times = 2 times. so, we need to
65         // add 0.5 * the total speed each frame until the dodge action is done..
66         common_factor = sys_frametime / autocvar_sv_dodging_ramp_time;
67
68         // if ramp time is smaller than frametime we get problems ;D
69         if (common_factor > 1)
70                 common_factor = 1;
71
72         new_velocity_gain = self.dodging_velocity_gain - (common_factor * horiz_speed);
73         if (new_velocity_gain < 0)
74                 new_velocity_gain = 0;
75
76         velocity_difference = self.dodging_velocity_gain - new_velocity_gain;
77
78         // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D
79         if (self.dodging_action == 1) {
80                 //disable jump key during dodge accel phase
81                 if (self.movement.z > 0) self.movement_z = 0;
82
83                 self.velocity =
84                           self.velocity
85                         + ((self.dodging_direction.y * velocity_difference) * v_right)
86                         + ((self.dodging_direction.x * velocity_difference) * v_forward);
87
88                 self.dodging_velocity_gain = self.dodging_velocity_gain - velocity_difference;
89         }
90
91         // the up part of the dodge is a single shot action
92         if (self.dodging_single_action == 1) {
93                 self.flags &= ~FL_ONGROUND;
94
95                 self.velocity =
96                           self.velocity
97                         + (autocvar_sv_dodging_up_speed * v_up);
98
99                 if (autocvar_sv_dodging_sound)
100                         PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
101
102                 animdecide_setaction(self, ANIMACTION_JUMP, 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) > autocvar_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 bool check_close_to_wall(float threshold)
122 {
123         if(!autocvar_sv_dodging_wall_dodging)
124                 return false;
125
126         vector trace_start;
127         vector trace_end;
128
129         trace_start = self.origin;
130
131         trace_end = self.origin + (1000*v_right);
132         tracebox(trace_start, self.mins, self.maxs, trace_end, true, self);
133         if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
134                 return true;
135
136         trace_end = self.origin - (1000*v_right);
137         tracebox(trace_start, self.mins, self.maxs, trace_end, true, self);
138         if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
139                 return true;
140
141         trace_end = self.origin + (1000*v_forward);
142         tracebox(trace_start, self.mins, self.maxs, trace_end, true, self);
143         if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
144                 return true;
145
146         trace_end = self.origin - (1000*v_forward);
147         tracebox(trace_start, self.mins, self.maxs, trace_end, true, self);
148         if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
149                 return true;
150
151         return false;
152 }
153
154 bool check_close_to_ground(float threshold)
155 {
156         if (self.flags & FL_ONGROUND)
157                 return true;
158
159         return false;
160 }
161
162
163 MUTATOR_HOOKFUNCTION(dodging_GetPressedKeys) {
164         // print("dodging_PlayerPhysics\n");
165
166         float length;
167         vector tap_direction = '0 0 0';
168
169         float frozen_dodging, frozen_no_doubletap;
170         frozen_dodging = (self.frozen && autocvar_sv_dodging_frozen);
171         frozen_no_doubletap = (frozen_dodging && !autocvar_sv_dodging_frozen_doubletap);
172
173         float dodge_detected;
174         if (g_dodging == 0)
175                 return 0;
176
177         dodge_detected = 0;
178
179         // first check if the last dodge is far enough back in time so we can dodge again
180         if ((time - self.last_dodging_time) < autocvar_sv_dodging_delay)
181                 return 0;
182
183         if (check_close_to_ground(autocvar_sv_dodging_height_threshold) != 1
184                 && check_close_to_wall(autocvar_sv_dodging_wall_distance_threshold) != 1)
185                 return 0;
186
187         if (self.movement.x > 0) {
188                 // is this a state change?
189                 if (!(self.pressedkeys & KEY_FORWARD) || frozen_no_doubletap) {
190                         if ((time - self.last_FORWARD_KEY_time) < self.cvar_cl_dodging_timeout) {
191                                 tap_direction.x = 1.0;
192                                 dodge_detected = 1;
193                         }
194                         self.last_FORWARD_KEY_time = time;
195                 }
196         }
197
198         if (self.movement.x < 0) {
199                 // is this a state change?
200                 if (!(self.pressedkeys & KEY_BACKWARD) || frozen_no_doubletap) {
201                         tap_direction.x = -1.0;
202                         if ((time - self.last_BACKWARD_KEY_time) < self.cvar_cl_dodging_timeout)        {
203                                 dodge_detected = 1;
204                         }
205                         self.last_BACKWARD_KEY_time = time;
206                 }
207         }
208
209         if (self.movement.y > 0) {
210                 // is this a state change?
211                 if (!(self.pressedkeys & KEY_RIGHT) || frozen_no_doubletap) {
212                         tap_direction.y = 1.0;
213                         if ((time - self.last_RIGHT_KEY_time) < self.cvar_cl_dodging_timeout)   {
214                                 dodge_detected = 1;
215                         }
216                         self.last_RIGHT_KEY_time = time;
217                 }
218         }
219
220         if (self.movement.y < 0) {
221                 // is this a state change?
222                 if (!(self.pressedkeys & KEY_LEFT) || frozen_no_doubletap) {
223                         tap_direction.y = -1.0;
224                         if ((time - self.last_LEFT_KEY_time) < self.cvar_cl_dodging_timeout)    {
225                                 dodge_detected = 1;
226                         }
227                         self.last_LEFT_KEY_time = time;
228                 }
229         }
230
231         if (dodge_detected == 1) {
232                 self.last_dodging_time = time;
233
234                 self.dodging_action = 1;
235                 self.dodging_single_action = 1;
236
237                 self.dodging_velocity_gain = autocvar_sv_dodging_horiz_speed;
238
239                 self.dodging_direction_x = tap_direction.x;
240                 self.dodging_direction_y = tap_direction.y;
241
242                 // normalize the dodging_direction vector.. (unlike UT99) XD
243                 length =          self.dodging_direction.x * self.dodging_direction.x;
244                 length = length + self.dodging_direction.y * self.dodging_direction.y;
245                 length = sqrt(length);
246
247                 self.dodging_direction_x = self.dodging_direction.x * 1.0/length;
248                 self.dodging_direction_y = self.dodging_direction.y * 1.0/length;
249         }
250
251         return 0;
252 }
253
254 MUTATOR_DEFINITION(mutator_dodging)
255 {
256         // we need to be called before GetPressedKey does its thing so we can
257         // detect state changes and therefore dodging actions..
258         MUTATOR_HOOK(GetPressedKeys, dodging_GetPressedKeys, CBC_ORDER_ANY);
259
260         // in the physics hook we actually implement the dodge..
261         MUTATOR_HOOK(PlayerPhysics, dodging_PlayerPhysics, CBC_ORDER_ANY);
262
263         // get timeout information from the client, so the client can configure it..
264         MUTATOR_HOOK(GetCvars, dodging_GetCvars, CBC_ORDER_ANY);
265
266         // this just turns on the cvar.
267         MUTATOR_ONADD
268         {
269                 g_dodging = 1;
270         }
271
272         // this just turns off the cvar.
273         MUTATOR_ONROLLBACK_OR_REMOVE
274         {
275                 g_dodging = 0;
276         }
277
278         MUTATOR_ONREMOVE
279         {
280         }
281
282         return 0;
283 }