]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/mutators/mutator_dodging.qc
Merge branch 'Mirio/credits' into 'master'
[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 == 1)
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 float check_close_to_wall(float threshold) {
122         if (autocvar_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         if (self.flags & FL_ONGROUND)
155                 return 1;
156
157         return 0;
158 }
159
160
161 MUTATOR_HOOKFUNCTION(dodging_GetPressedKeys) {
162         // print("dodging_PlayerPhysics\n");
163
164         float length;
165         vector tap_direction = '0 0 0';
166
167         float frozen_dodging, frozen_no_doubletap;
168         frozen_dodging = (self.frozen && autocvar_sv_dodging_frozen);
169         frozen_no_doubletap = (frozen_dodging && !autocvar_sv_dodging_frozen_doubletap);
170
171         float dodge_detected;
172         if (g_dodging == 0)
173                 return 0;
174
175         dodge_detected = 0;
176
177         // first check if the last dodge is far enough back in time so we can dodge again
178         if ((time - self.last_dodging_time) < autocvar_sv_dodging_delay)
179                 return 0;
180
181         if (check_close_to_ground(autocvar_sv_dodging_height_threshold) != 1
182                 && check_close_to_wall(autocvar_sv_dodging_wall_distance_threshold) != 1)
183                 return 0;
184
185         if (self.movement.x > 0) {
186                 // is this a state change?
187                 if (!(self.pressedkeys & KEY_FORWARD) || frozen_no_doubletap) {
188                         if ((time - self.last_FORWARD_KEY_time) < self.cvar_cl_dodging_timeout) {
189                                 tap_direction.x = 1.0;
190                                 dodge_detected = 1;
191                         }
192                         self.last_FORWARD_KEY_time = time;
193                 }
194         }
195
196         if (self.movement.x < 0) {
197                 // is this a state change?
198                 if (!(self.pressedkeys & KEY_BACKWARD) || frozen_no_doubletap) {
199                         tap_direction.x = -1.0;
200                         if ((time - self.last_BACKWARD_KEY_time) < self.cvar_cl_dodging_timeout)        {
201                                 dodge_detected = 1;
202                         }
203                         self.last_BACKWARD_KEY_time = time;
204                 }
205         }
206
207         if (self.movement.y > 0) {
208                 // is this a state change?
209                 if (!(self.pressedkeys & KEY_RIGHT) || frozen_no_doubletap) {
210                         tap_direction.y = 1.0;
211                         if ((time - self.last_RIGHT_KEY_time) < self.cvar_cl_dodging_timeout)   {
212                                 dodge_detected = 1;
213                         }
214                         self.last_RIGHT_KEY_time = time;
215                 }
216         }
217
218         if (self.movement.y < 0) {
219                 // is this a state change?
220                 if (!(self.pressedkeys & KEY_LEFT) || frozen_no_doubletap) {
221                         tap_direction.y = -1.0;
222                         if ((time - self.last_LEFT_KEY_time) < self.cvar_cl_dodging_timeout)    {
223                                 dodge_detected = 1;
224                         }
225                         self.last_LEFT_KEY_time = time;
226                 }
227         }
228
229         if (dodge_detected == 1) {
230                 self.last_dodging_time = time;
231
232                 self.dodging_action = 1;
233                 self.dodging_single_action = 1;
234
235                 self.dodging_velocity_gain = autocvar_sv_dodging_horiz_speed;
236
237                 self.dodging_direction_x = tap_direction.x;
238                 self.dodging_direction_y = tap_direction.y;
239
240                 // normalize the dodging_direction vector.. (unlike UT99) XD
241                 length =          self.dodging_direction.x * self.dodging_direction.x;
242                 length = length + self.dodging_direction.y * self.dodging_direction.y;
243                 length = sqrt(length);
244
245                 self.dodging_direction_x = self.dodging_direction.x * 1.0/length;
246                 self.dodging_direction_y = self.dodging_direction.y * 1.0/length;
247         }
248
249         return 0;
250 }
251
252 MUTATOR_DEFINITION(mutator_dodging)
253 {
254         // we need to be called before GetPressedKey does its thing so we can
255         // detect state changes and therefore dodging actions..
256         MUTATOR_HOOK(GetPressedKeys, dodging_GetPressedKeys, CBC_ORDER_ANY);
257
258         // in the physics hook we actually implement the dodge..
259         MUTATOR_HOOK(PlayerPhysics, dodging_PlayerPhysics, CBC_ORDER_ANY);
260
261         // get timeout information from the client, so the client can configure it..
262         MUTATOR_HOOK(GetCvars, dodging_GetCvars, CBC_ORDER_ANY);
263
264         // this just turns on the cvar.
265         MUTATOR_ONADD
266         {
267                 g_dodging = 1;
268         }
269
270         // this just turns off the cvar.
271         MUTATOR_ONROLLBACK_OR_REMOVE
272         {
273                 g_dodging = 0;
274         }
275
276         MUTATOR_ONREMOVE
277         {
278         }
279
280         return 0;
281 }