]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/mutators/mutator_dodging.qc
Factor out animation decisions and gameplay
[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 .float dodging_direction_x;
13 .float dodging_direction_y;
14
15 // this indicates the last time a dodge was executed. used to check if another one is allowed
16 // and to ramp up the dodge acceleration in the physics hook.
17 .float last_dodging_time;
18
19 // set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done..
20 .float dodging_action;
21
22 // This is the velocity gain to be added over the ramp time.
23 // It will decrease from frame to frame during dodging_action = 1
24 // until it's 0.
25 .float dodging_velocity_gain;
26
27 // the jump part of the dodge cannot be ramped
28 .float dodging_single_action;
29
30 void dodging_Initialize() {
31         // print("dodging_Initialize\n");
32
33         self.last_FORWARD_KEY_time = 0;
34         self.last_BACKWARD_KEY_time = 0;
35         self.last_RIGHT_KEY_time = 0;
36         self.last_LEFT_KEY_time = 0;
37         self.last_dodging_time = 0;
38         self.dodging_action = 0;
39         self.dodging_velocity_gain = 0;
40         self.dodging_single_action = 0;
41         self.dodging_direction_x = 0;
42         self.dodging_direction_y = 0;
43 }
44
45 MUTATOR_HOOKFUNCTION(dodging_GetCvars) {
46         GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_dodging_timeout, "cl_dodging_timeout");
47         return 0;
48 }
49
50 MUTATOR_HOOKFUNCTION(dodging_PlayerPhysics) {
51         // print("dodging_PlayerPhysics\n");
52
53         float common_factor;
54         float new_velocity_gain;
55         float velocity_difference;
56         float clean_up_and_do_nothing;
57
58     if (self.deadflag != DEAD_NO)
59         return 0;
60
61         new_velocity_gain = 0;
62         clean_up_and_do_nothing = 0;
63
64         if (g_dodging == 0)
65                 clean_up_and_do_nothing = 1;
66
67         // when swimming, no dodging allowed..
68         if (self.waterlevel >= WATERLEVEL_SWIMMING)
69                 clean_up_and_do_nothing = 1;
70
71         if (clean_up_and_do_nothing != 0) {
72                 self.dodging_action = 0;
73                 self.dodging_direction_x = 0;
74                 self.dodging_direction_y = 0;
75                 return 0;
76         }
77
78         // make sure v_up, v_right and v_forward are sane
79         makevectors(self.angles);
80
81         // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code 
82         // will be called ramp_time/frametime times = 2 times. so, we need to 
83         // add 0.5 * the total speed each frame until the dodge action is done..
84         common_factor = sys_frametime / autocvar_sv_dodging_ramp_time;
85
86         // if ramp time is smaller than frametime we get problems ;D
87         if (common_factor > 1) 
88                 common_factor = 1;
89
90         new_velocity_gain = self.dodging_velocity_gain - (common_factor * autocvar_sv_dodging_horiz_speed);
91         if (new_velocity_gain < 0)
92                 new_velocity_gain = 0;
93
94         velocity_difference = self.dodging_velocity_gain - new_velocity_gain;
95
96         // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D
97         if (self.dodging_action == 1) {
98                 //disable jump key during dodge accel phase
99                 if (self.movement_z > 0) self.movement_z = 0;
100
101                 self.velocity = 
102                           self.velocity 
103                         + ((self.dodging_direction_y * velocity_difference) * v_right)
104                         + ((self.dodging_direction_x * velocity_difference) * v_forward);
105
106                 self.dodging_velocity_gain = self.dodging_velocity_gain - velocity_difference;
107         }
108
109         // the up part of the dodge is a single shot action
110         if (self.dodging_single_action == 1) {
111                 self.flags &~= FL_ONGROUND;
112
113                 self.velocity = 
114                           self.velocity 
115                         + (autocvar_sv_dodging_up_speed * v_up);
116
117                 if (autocvar_sv_dodging_sound == 1)
118                         PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
119
120                 animdecide_setaction(self, ANIMACTION_JUMP, TRUE);
121
122                 self.dodging_single_action = 0;
123         }
124
125         // are we done with the dodging ramp yet?
126         if((self.dodging_action == 1) && ((time - self.last_dodging_time) > autocvar_sv_dodging_ramp_time))
127         {
128                 // reset state so next dodge can be done correctly
129                 self.dodging_action = 0;
130                 self.dodging_direction_x = 0;
131                 self.dodging_direction_y = 0;
132         }
133
134         return 0;
135 }
136
137
138 // returns 1 if the player is close to a wall
139 float check_close_to_wall(float threshold) {
140         if (autocvar_sv_dodging_wall_dodging == 0)
141                 return 0;
142
143         vector trace_start;
144         vector trace_end;
145
146         trace_start = self.origin;
147
148         trace_end = self.origin + (1000*v_right);
149         tracebox(trace_start, self.mins, self.maxs, trace_end, TRUE, self);
150         if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
151                 return 1;
152
153         trace_end = self.origin - (1000*v_right);
154         tracebox(trace_start, self.mins, self.maxs, trace_end, TRUE, self);
155         if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
156                 return 1;
157
158         trace_end = self.origin + (1000*v_forward);
159         tracebox(trace_start, self.mins, self.maxs, trace_end, TRUE, self);
160         if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
161                 return 1;
162
163         trace_end = self.origin - (1000*v_forward);
164         tracebox(trace_start, self.mins, self.maxs, trace_end, TRUE, self);
165         if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
166                 return 1;
167
168         return 0;
169 }
170
171 float check_close_to_ground(float threshold) {
172         if (self.flags & FL_ONGROUND)
173                 return 1;
174
175         return 0;
176 }
177
178
179 MUTATOR_HOOKFUNCTION(dodging_GetPressedKeys) {
180         // print("dodging_PlayerPhysics\n");
181
182         float length;
183         float tap_direction_x;
184         float tap_direction_y;
185
186         tap_direction_x = 0;
187         tap_direction_y = 0;
188
189         float dodge_detected;
190         if (g_dodging == 0)
191                 return 0;
192
193         dodge_detected = 0;
194
195         // first check if the last dodge is far enough back in time so we can dodge again
196         if ((time - self.last_dodging_time) < autocvar_sv_dodging_delay)
197                 return 0;
198
199         if (check_close_to_ground(autocvar_sv_dodging_height_threshold) != 1 
200                 && check_close_to_wall(autocvar_sv_dodging_wall_distance_threshold) != 1)
201                 return 0;
202
203         if (self.movement_x > 0) {
204                 // is this a state change?
205                 if (!(self.pressedkeys & KEY_FORWARD)) {
206                         if ((time - self.last_FORWARD_KEY_time) < self.cvar_cl_dodging_timeout) { 
207                                 tap_direction_x = 1.0;
208                                 dodge_detected = 1;
209                         }
210                         self.last_FORWARD_KEY_time = time;
211                 }
212         }
213
214         if (self.movement_x < 0) {
215                 // is this a state change?
216                 if (!(self.pressedkeys & KEY_BACKWARD)) {
217                         tap_direction_x = -1.0;
218                         if ((time - self.last_BACKWARD_KEY_time) < self.cvar_cl_dodging_timeout)        { 
219                                 dodge_detected = 1;
220                         }
221                         self.last_BACKWARD_KEY_time = time;
222                 }
223         }
224
225         if (self.movement_y > 0) {
226                 // is this a state change?
227                 if (!(self.pressedkeys & KEY_RIGHT)) {
228                         tap_direction_y = 1.0;
229                         if ((time - self.last_RIGHT_KEY_time) < self.cvar_cl_dodging_timeout)   { 
230                                 dodge_detected = 1;
231                         }
232                         self.last_RIGHT_KEY_time = time;
233                 }
234         }
235
236         if (self.movement_y < 0) {
237                 // is this a state change?
238                 if (!(self.pressedkeys & KEY_LEFT)) {
239                         tap_direction_y = -1.0;
240                         if ((time - self.last_LEFT_KEY_time) < self.cvar_cl_dodging_timeout)    { 
241                                 dodge_detected = 1;
242                         }
243                         self.last_LEFT_KEY_time = time;
244                 }
245         }
246
247         if (dodge_detected == 1) {
248                 self.last_dodging_time = time;
249
250                 self.dodging_action = 1;
251                 self.dodging_single_action = 1;
252
253                 self.dodging_velocity_gain = autocvar_sv_dodging_horiz_speed;
254
255                 self.dodging_direction_x = tap_direction_x;
256                 self.dodging_direction_y = tap_direction_y;
257
258                 // normalize the dodging_direction vector.. (unlike UT99) XD
259                 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(mutator_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 }