]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/server/cl_physics.qc
1634961dcf2ee3651a1c71c85511fe1e6d3dbf48
[voretournament/voretournament.git] / data / qcsrc / server / cl_physics.qc
1 .float race_penalty;\r
2 .float restart_jump;\r
3 \r
4 float sv_accelerate;\r
5 float sv_friction;\r
6 float sv_maxspeed;\r
7 float sv_airaccelerate;\r
8 float sv_maxairspeed;\r
9 float sv_stopspeed;\r
10 float sv_gravity;\r
11 float sv_airaccel_sideways_friction;\r
12 float sv_airaccel_qw;\r
13 float sv_airstopaccelerate;\r
14 float sv_airstrafeaccelerate;\r
15 float sv_maxairstrafespeed;\r
16 float sv_aircontrol;\r
17 float sv_warsowbunny_airforwardaccel;\r
18 float sv_warsowbunny_accel;\r
19 float sv_warsowbunny_topspeed;\r
20 float sv_warsowbunny_turnaccel;\r
21 float sv_warsowbunny_backtosideratio;\r
22 \r
23 .float ladder_time;\r
24 .entity ladder_entity;\r
25 .float gravity;\r
26 .float swamp_slowdown;\r
27 .float lastflags;\r
28 .float lastground;\r
29 .float wasFlying;\r
30 .float spectatorspeed;\r
31 \r
32 /*\r
33 =============\r
34 PlayerJump\r
35 \r
36 When you press the jump key\r
37 =============\r
38 */\r
39 void PlayerJump (void)\r
40 {\r
41         float mjumpheight;\r
42 \r
43         mjumpheight = cvar("sv_jumpvelocity");\r
44         if (self.waterlevel >= WATERLEVEL_SWIMMING)\r
45         {\r
46                 if (self.watertype == CONTENT_WATER)\r
47                         self.velocity_z = 200;\r
48                 else if (self.watertype == CONTENT_SLIME)\r
49                         self.velocity_z = 80;\r
50                 else\r
51                         self.velocity_z = 50;\r
52 \r
53                 return;\r
54         }\r
55 \r
56         if (!(self.flags & FL_ONGROUND))\r
57                 return;\r
58 \r
59         if(!sv_pogostick)\r
60                 if (!(self.flags & FL_JUMPRELEASED))\r
61                         return;\r
62 \r
63         if(self.health <= g_bloodloss)\r
64                 return;\r
65 \r
66         if(cvar_string("sv_jumpspeedcap_min") != "")\r
67                 self.velocity_z = max(cvar("sv_jumpvelocity") * cvar("sv_jumpspeedcap_min"), self.velocity_z);\r
68         if(cvar_string("sv_jumpspeedcap_max") != "") {\r
69                 if(trace_fraction < 1 && trace_plane_normal_z < 0.98 && cvar("sv_jumpspeedcap_max_disable_on_ramps")) {\r
70                         // don't do jump speedcaps on ramps to preserve old voretournament ramjump style\r
71                         //print("Trace plane normal z: ", ftos(trace_plane_normal_z), ", disabling speed cap!\n");\r
72                 }\r
73                 else\r
74                         self.velocity_z = min(cvar("sv_jumpvelocity") * cvar("sv_jumpspeedcap_max"), self.velocity_z) + trace_ent.velocity_z;\r
75         }\r
76 \r
77         if(!(self.lastflags & FL_ONGROUND))\r
78         {\r
79                 if(cvar("speedmeter"))\r
80                         dprint(strcat("landing velocity: ", vtos(self.velocity), " (abs: ", ftos(vlen(self.velocity)), ")\n"));\r
81                 if(self.lastground < time - 0.3)\r
82                 {\r
83                         self.velocity_x *= (1 - cvar("sv_friction_on_land"));\r
84                         self.velocity_y *= (1 - cvar("sv_friction_on_land"));\r
85                 }\r
86                 if(self.jumppadcount > 1)\r
87                         dprint(strcat(ftos(self.jumppadcount), "x jumppad combo\n"));\r
88                 self.jumppadcount = 0;\r
89         }\r
90 \r
91         self.velocity_z = self.velocity_z + mjumpheight;\r
92         self.oldvelocity_z = self.velocity_z;\r
93 \r
94         self.flags &~= FL_ONGROUND;\r
95         self.flags &~= FL_JUMPRELEASED;\r
96 \r
97         if (self.crouch)\r
98                 setanim(self, self.anim_duckjump, FALSE, TRUE, TRUE);\r
99         else\r
100                 setanim(self, self.anim_jump, FALSE, TRUE, TRUE);\r
101 \r
102         if(g_jump_grunt)\r
103                 PlayerSound(self, playersound_jump, CHAN_PLAYER, VOICETYPE_PLAYERSOUND);\r
104 \r
105         self.restart_jump = -1; // restart jump anim next time\r
106         // value -1 is used to not use the teleport bit (workaround for tiny hitch when re-jumping)\r
107 }\r
108 \r
109 void CheckWaterJump()\r
110 {\r
111         local vector start, end;\r
112 \r
113 // check for a jump-out-of-water\r
114         makevectors (self.angles);\r
115         start = self.origin;\r
116         start_z = start_z + 8;\r
117         v_forward_z = 0;\r
118         normalize(v_forward);\r
119         end = start + v_forward*24;\r
120         traceline (start, end, TRUE, self);\r
121         if (trace_fraction < 1)\r
122         {       // solid at waist\r
123                 start_z = start_z + self.maxs_z - 8;\r
124                 end = start + v_forward*24;\r
125                 self.movedir = trace_plane_normal * -50;\r
126                 traceline (start, end, TRUE, self);\r
127                 if (trace_fraction == 1)\r
128                 {       // open at eye level\r
129                         self.flags |= FL_WATERJUMP;\r
130                         self.velocity_z = 225;\r
131                         self.flags &~= FL_JUMPRELEASED;\r
132                         self.teleport_time = time + 2;  // safety net\r
133                         return;\r
134                 }\r
135         }\r
136 };\r
137 \r
138 float racecar_angle(float forward, float down)\r
139 {\r
140         float ret, angle_mult;\r
141 \r
142         if(forward < 0)\r
143         {\r
144                 forward = -forward;\r
145                 down = -down;\r
146         }\r
147 \r
148         ret = vectoyaw('0 1 0' * down + '1 0 0' * forward);\r
149 \r
150         angle_mult = forward / (800 + forward);\r
151 \r
152         if(ret > 180)\r
153                 return ret * angle_mult + 360 * (1 - angle_mult);\r
154         else\r
155                 return ret * angle_mult;\r
156 }\r
157 \r
158 void RaceCarPhysics()\r
159 {\r
160         // using this move type for "big rigs"\r
161         // the engine does not push the entity!\r
162 \r
163         float accel, steer, f;\r
164         vector angles_save, rigvel;\r
165 \r
166         angles_save = self.angles;\r
167         accel = bound(-1, self.movement_x / sv_maxspeed, 1);\r
168         steer = bound(-1, self.movement_y / sv_maxspeed, 1);\r
169 \r
170         if(g_bugrigs_reverse_speeding)\r
171         {\r
172                 if(accel < 0)\r
173                 {\r
174                         // back accel is DIGITAL\r
175                         // to prevent speedhack\r
176                         if(accel < -0.5)\r
177                                 accel = -1;\r
178                         else\r
179                                 accel = 0;\r
180                 }\r
181         }\r
182 \r
183         self.angles_x = 0;\r
184         self.angles_z = 0;\r
185         makevectors(self.angles); // new forward direction!\r
186 \r
187         if(self.flags & FL_ONGROUND || g_bugrigs_air_steering)\r
188         {\r
189                 float myspeed, upspeed, steerfactor, accelfactor;\r
190 \r
191                 myspeed = self.velocity * v_forward;\r
192                 upspeed = self.velocity * v_up;\r
193 \r
194                 // responsiveness factor for steering and acceleration\r
195                 f = 1 / (1 + pow(max(-myspeed, myspeed) / g_bugrigs_speed_ref, g_bugrigs_speed_pow));\r
196                 //MAXIMA: f(v) := 1 / (1 + (v / g_bugrigs_speed_ref) ^ g_bugrigs_speed_pow);\r
197 \r
198                 if(myspeed < 0 && g_bugrigs_reverse_spinning)\r
199                         steerfactor = -myspeed * g_bugrigs_steer;\r
200                 else\r
201                         steerfactor = -myspeed * f * g_bugrigs_steer;\r
202 \r
203                 if(myspeed < 0 && g_bugrigs_reverse_speeding)\r
204                         accelfactor = g_bugrigs_accel;\r
205                 else\r
206                         accelfactor = f * g_bugrigs_accel;\r
207                 //MAXIMA: accel(v) := f(v) * g_bugrigs_accel;\r
208 \r
209                 if(accel < 0)\r
210                 {\r
211                         if(myspeed > 0)\r
212                         {\r
213                                 myspeed = max(0, myspeed - frametime * (g_bugrigs_friction_floor - g_bugrigs_friction_brake * accel));\r
214                         }\r
215                         else\r
216                         {\r
217                                 if(!g_bugrigs_reverse_speeding)\r
218                                         myspeed = min(0, myspeed + frametime * g_bugrigs_friction_floor);\r
219                         }\r
220                 }\r
221                 else\r
222                 {\r
223                         if(myspeed >= 0)\r
224                         {\r
225                                 myspeed = max(0, myspeed - frametime * g_bugrigs_friction_floor);\r
226                         }\r
227                         else\r
228                         {\r
229                                 if(g_bugrigs_reverse_stopping)\r
230                                         myspeed = 0;\r
231                                 else\r
232                                         myspeed = min(0, myspeed + frametime * (g_bugrigs_friction_floor + g_bugrigs_friction_brake * accel));\r
233                         }\r
234                 }\r
235                 // terminal velocity = velocity at which 50 == accelfactor, that is, 1549 units/sec\r
236                 //MAXIMA: friction(v) := g_bugrigs_friction_floor;\r
237 \r
238                 self.angles_y += steer * frametime * steerfactor; // apply steering\r
239                 makevectors(self.angles); // new forward direction!\r
240 \r
241                 myspeed += accel * accelfactor * frametime;\r
242 \r
243                 rigvel = myspeed * v_forward + '0 0 1' * upspeed;\r
244         }\r
245         else\r
246         {\r
247                 myspeed = vlen(self.velocity);\r
248 \r
249                 // responsiveness factor for steering and acceleration\r
250                 f = 1 / (1 + pow(max(0, myspeed / g_bugrigs_speed_ref), g_bugrigs_speed_pow));\r
251                 steerfactor = -myspeed * f;\r
252                 self.angles_y += steer * frametime * steerfactor; // apply steering\r
253 \r
254                 rigvel = self.velocity;\r
255                 makevectors(self.angles); // new forward direction!\r
256         }\r
257 \r
258         rigvel = rigvel * max(0, 1 - vlen(rigvel) * g_bugrigs_friction_air * frametime);\r
259         //MAXIMA: airfriction(v) := v * v * g_bugrigs_friction_air;\r
260         //MAXIMA: total_acceleration(v) := accel(v) - friction(v) - airfriction(v);\r
261         //MAXIMA: solve(total_acceleration(v) = 0, v);\r
262 \r
263         if(g_bugrigs_planar_movement)\r
264         {\r
265                 vector rigvel_xy, neworigin, up;\r
266                 float mt;\r
267 \r
268                 rigvel_z -= frametime * sv_gravity; // 4x gravity plays better\r
269                 rigvel_xy = rigvel;\r
270                 rigvel_xy_z = 0;\r
271 \r
272                 if(g_bugrigs_planar_movement_car_jumping && !g_touchexplode) // touchexplode is a better way to handle collisions\r
273                         mt = MOVE_NORMAL;\r
274                 else\r
275                         mt = MOVE_NOMONSTERS;\r
276 \r
277                 tracebox(self.origin, self.mins, self.maxs, self.origin + '0 0 1024', mt, self);\r
278                 up = trace_endpos - self.origin;\r
279 \r
280                 // BUG RIGS: align the move to the surface instead of doing collision testing\r
281                 // can we move?\r
282                 tracebox(trace_endpos, self.mins, self.maxs, trace_endpos + rigvel_xy * frametime, mt, self);\r
283 \r
284                 // align to surface\r
285                 tracebox(trace_endpos, self.mins, self.maxs, trace_endpos - up + '0 0 1' * rigvel_z * frametime, mt, self);\r
286 \r
287                 if(trace_fraction < 0.5)\r
288                 {\r
289                         trace_fraction = 1;\r
290                         neworigin = self.origin;\r
291                 }\r
292                 else\r
293                         neworigin = trace_endpos;\r
294 \r
295                 if(trace_fraction < 1)\r
296                 {\r
297                         // now set angles_x so that the car points parallel to the surface\r
298                         self.angles = vectoangles(\r
299                                         '1 0 0' * v_forward_x * trace_plane_normal_z\r
300                                         +\r
301                                         '0 1 0' * v_forward_y * trace_plane_normal_z\r
302                                         +\r
303                                         '0 0 1' * -(v_forward_x * trace_plane_normal_x + v_forward_y * trace_plane_normal_y)\r
304                                         );\r
305                         self.flags |= FL_ONGROUND;\r
306                 }\r
307                 else\r
308                 {\r
309                         // now set angles_x so that the car points forward, but is tilted in velocity direction\r
310                         self.flags &~= FL_ONGROUND;\r
311                 }\r
312 \r
313                 self.velocity = (neworigin - self.origin) * (1.0 / frametime);\r
314                 self.movetype = MOVETYPE_NOCLIP;\r
315         }\r
316         else\r
317         {\r
318                 rigvel_z -= frametime * sv_gravity; // 4x gravity plays better\r
319                 self.velocity = rigvel;\r
320                 self.movetype = MOVETYPE_FLY;\r
321         }\r
322 \r
323         trace_fraction = 1;\r
324         tracebox(self.origin, self.mins, self.maxs, self.origin - '0 0 4', MOVE_NORMAL, self);\r
325         if(trace_fraction != 1)\r
326         {\r
327                 self.angles = vectoangles2(\r
328                                 '1 0 0' * v_forward_x * trace_plane_normal_z\r
329                                 +\r
330                                 '0 1 0' * v_forward_y * trace_plane_normal_z\r
331                                 +\r
332                                 '0 0 1' * -(v_forward_x * trace_plane_normal_x + v_forward_y * trace_plane_normal_y),\r
333                                 trace_plane_normal\r
334                                 );\r
335         }\r
336         else\r
337         {\r
338                 vector vel_local;\r
339 \r
340                 vel_local_x = v_forward * self.velocity;\r
341                 vel_local_y = v_right * self.velocity;\r
342                 vel_local_z = v_up * self.velocity;\r
343 \r
344                 self.angles_x = racecar_angle(vel_local_x, vel_local_z);\r
345                 self.angles_z = racecar_angle(-vel_local_y, vel_local_z);\r
346         }\r
347 \r
348         // smooth the angles\r
349         vector vf1, vu1, smoothangles;\r
350         makevectors(self.angles);\r
351         f = bound(0, frametime * g_bugrigs_angle_smoothing, 1);\r
352         if(f == 0)\r
353                 f = 1;\r
354         vf1 = v_forward * f;\r
355         vu1 = v_up * f;\r
356         makevectors(angles_save);\r
357         vf1 = vf1 + v_forward * (1 - f);\r
358         vu1 = vu1 + v_up * (1 - f);\r
359         smoothangles = vectoangles2(vf1, vu1);\r
360         self.angles_x = -smoothangles_x;\r
361         self.angles_z =  smoothangles_z;\r
362 }\r
363 \r
364 float IsMoveInDirection(vector mv, float angle) // key mix factor\r
365 {\r
366         if(mv_x == 0 && mv_y == 0)\r
367                 return 0; // avoid division by zero\r
368         angle = RAD2DEG * atan2(mv_y, mv_x);\r
369         angle = remainder(angle, 360) / 45;\r
370         if(angle >  1)\r
371                 return 0;\r
372         if(angle < -1)\r
373                 return 0;\r
374         return 1 - fabs(angle);\r
375 }\r
376 \r
377 void CPM_PM_Aircontrol(vector wishdir, float wishspeed)\r
378 {\r
379         float zspeed, xyspeed, dot, k;\r
380 \r
381 #if 0\r
382         // this doesn't play well with analog input\r
383         if(self.movement_x == 0 || self.movement_y != 0)\r
384                 return; // can't control movement if not moving forward or backward\r
385         k = 32;\r
386 #else\r
387         k = 32 * (2 * IsMoveInDirection(self.movement, 0) - 1);\r
388         if(k <= 0)\r
389                 return;\r
390 #endif\r
391 \r
392         k *= bound(0, wishspeed / sv_maxairspeed, 1);\r
393 \r
394         zspeed = self.velocity_z;\r
395         self.velocity_z = 0;\r
396         xyspeed = vlen(self.velocity); self.velocity = normalize(self.velocity);\r
397 \r
398         dot = self.velocity * wishdir;\r
399         k *= sv_aircontrol*dot*dot*frametime;\r
400 \r
401         if(dot > 0) // we can't change direction while slowing down\r
402         {\r
403                 self.velocity = normalize(self.velocity * xyspeed + wishdir * k);\r
404         }\r
405 \r
406         self.velocity = self.velocity * xyspeed;\r
407         self.velocity_z = zspeed;\r
408 }\r
409 \r
410 // example config for alternate speed clamping:\r
411 //   sv_airaccel_qw 0.8\r
412 //   sv_airaccel_sideways_friction 0\r
413 //   prvm_globalset server speedclamp_mode 1\r
414 //     (or 2)\r
415 void PM_Accelerate(vector wishdir, float wishspeed, float wishspeed0, float accel, float accelqw, float sidefric)\r
416 {\r
417         float vel_straight;\r
418         float vel_z;\r
419         vector vel_perpend;\r
420         float step;\r
421 \r
422         vector vel_xy;\r
423         float vel_xy_current;\r
424         float vel_xy_backward, vel_xy_forward;\r
425         float speedclamp;\r
426 \r
427         speedclamp = (accelqw < 0);\r
428         if(speedclamp)\r
429                 accelqw = -accelqw;\r
430 \r
431         if(cvar("sv_gameplayfix_q2airaccelerate"))\r
432                 wishspeed0 = wishspeed;\r
433 \r
434         vel_straight = self.velocity * wishdir;\r
435         vel_z = self.velocity_z;\r
436         vel_xy = self.velocity - vel_z * '0 0 1';\r
437         vel_perpend = vel_xy - vel_straight * wishdir;\r
438 \r
439         step = accel * frametime * wishspeed0;\r
440 \r
441         vel_xy_current  = vlen(vel_xy);\r
442         vel_xy_forward  = vel_xy_current + bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw);\r
443         vel_xy_backward = vel_xy_current - bound(0, wishspeed + vel_xy_current, step) * accelqw - step * (1 - accelqw);\r
444         if(vel_xy_backward < 0)\r
445                 vel_xy_backward = 0; // not that it REALLY occurs that this would cause wrong behaviour afterwards\r
446 \r
447         vel_straight = vel_straight + bound(0, wishspeed - vel_straight, step) * accelqw + step * (1 - accelqw);\r
448 \r
449         if(sidefric < 0 && (vel_perpend*vel_perpend))\r
450                 // negative: only apply so much sideways friction to stay below the speed you could get by "braking"\r
451         {\r
452                 float f, fminimum;\r
453                 f = max(0, 1 + frametime * wishspeed * sidefric);\r
454                 fminimum = (vel_xy_backward*vel_xy_backward - vel_straight*vel_straight) / (vel_perpend*vel_perpend);\r
455                 // this cannot be > 1\r
456                 if(fminimum <= 0)\r
457                         vel_perpend = vel_perpend * max(0, f);\r
458                 else\r
459                 {\r
460                         fminimum = sqrt(fminimum);\r
461                         vel_perpend = vel_perpend * max(fminimum, f);\r
462                 }\r
463         }\r
464         else\r
465                 vel_perpend = vel_perpend * max(0, 1 - frametime * wishspeed * sidefric);\r
466         \r
467         vel_xy = vel_straight * wishdir + vel_perpend;\r
468         \r
469         if(speedclamp)\r
470         {\r
471                 // ensure we don't get too fast or decelerate faster than we should\r
472                 vel_xy_current = min(vlen(vel_xy), vel_xy_forward);\r
473                 if(vel_xy_current > 0) // prevent division by zero\r
474                         vel_xy = normalize(vel_xy) * vel_xy_current;\r
475         }\r
476 \r
477         self.velocity = vel_xy + vel_z * '0 0 1';\r
478 }\r
479 \r
480 void PM_AirAccelerate(vector wishdir, float wishspeed)\r
481 {\r
482         vector curvel, wishvel, acceldir, curdir;\r
483         float addspeed, accelspeed, curspeed, f;\r
484         float dot;\r
485 \r
486         if(wishspeed == 0)\r
487                 return;\r
488 \r
489         curvel = self.velocity;\r
490         curvel_z = 0;\r
491         curspeed = vlen(curvel);\r
492 \r
493         if(wishspeed > curspeed * 1.01)\r
494         {\r
495                 wishspeed = min(wishspeed, curspeed + sv_warsowbunny_airforwardaccel * sv_maxspeed * frametime);\r
496         }\r
497         else\r
498         {\r
499                 f = max(0, (sv_warsowbunny_topspeed - curspeed) / (sv_warsowbunny_topspeed - sv_maxspeed));\r
500                 wishspeed = max(curspeed, sv_maxspeed) + sv_warsowbunny_accel * f * sv_maxspeed * frametime;\r
501         }\r
502         wishvel = wishdir * wishspeed;\r
503         acceldir = wishvel - curvel;\r
504         addspeed = vlen(acceldir);\r
505         acceldir = normalize(acceldir);\r
506 \r
507         accelspeed = min(addspeed, sv_warsowbunny_turnaccel * sv_maxspeed * frametime);\r
508 \r
509         if(sv_warsowbunny_backtosideratio < 1)\r
510         {\r
511                 curdir = normalize(curvel);\r
512                 dot = acceldir * curdir;\r
513                 if(dot < 0)\r
514                         acceldir = acceldir - (1 - sv_warsowbunny_backtosideratio) * dot * curdir;\r
515         }\r
516 \r
517         self.velocity += accelspeed * acceldir;\r
518 }\r
519 \r
520 .vector movement_old;\r
521 .float buttons_old;\r
522 .vector v_angle_old;\r
523 .string lastclassname;\r
524 \r
525 .float() PlayerPhysplug;\r
526 \r
527 string specialcommand = "xwxwxsxsxaxdxaxdx1x ";\r
528 .float specialcommand_pos;\r
529 void SpecialCommand()\r
530 {\r
531 #ifdef TETRIS\r
532         TetrisImpulse();\r
533 #else\r
534         if(!CheatImpulse(99))\r
535                 print("A hollow voice says \"Plugh\".\n");\r
536 #endif\r
537 }\r
538 \r
539 float speedaward_speed;\r
540 string speedaward_holder;\r
541 void race_send_speedaward(float msg)\r
542 {\r
543         // send the best speed of the round\r
544         WriteByte(msg, SVC_TEMPENTITY);\r
545         WriteByte(msg, TE_CSQC_RACE);\r
546         WriteByte(msg, RACE_NET_SPEED_AWARD);\r
547         WriteInt24_t(msg, floor(speedaward_speed+0.5));\r
548         WriteString(msg, speedaward_holder);\r
549 }\r
550 \r
551 float speedaward_alltimebest;\r
552 string speedaward_alltimebest_holder;\r
553 void race_send_speedaward_alltimebest(float msg)\r
554 {\r
555         // send the best speed\r
556         WriteByte(msg, SVC_TEMPENTITY);\r
557         WriteByte(msg, TE_CSQC_RACE);\r
558         WriteByte(msg, RACE_NET_SPEED_AWARD_BEST);\r
559         WriteInt24_t(msg, floor(speedaward_alltimebest+0.5));\r
560         WriteString(msg, speedaward_alltimebest_holder);\r
561 }\r
562 \r
563 string GetMapname(void);\r
564 float speedaward_lastupdate;\r
565 float speedaward_lastsent;\r
566 .float jumppadusetime;\r
567 void SV_PlayerPhysics()\r
568 {\r
569         local vector wishvel, wishdir, v;\r
570         local float wishspeed, f, maxspd_mod, spd, maxairspd, airaccel, swampspd_mod, buttons;\r
571         string temps;\r
572         float buttons_prev;\r
573         float not_allowed_to_move;\r
574         string c;\r
575 \r
576     if(self.PlayerPhysplug)\r
577         if(self.PlayerPhysplug())\r
578             return;\r
579 \r
580         self.race_movetime_frac += frametime;\r
581         f = floor(self.race_movetime_frac);\r
582         self.race_movetime_frac -= f;\r
583         self.race_movetime_count += f;\r
584         self.race_movetime = self.race_movetime_frac + self.race_movetime_count;\r
585 \r
586         anticheat_physics();\r
587 \r
588         buttons = self.BUTTON_ATCK + 2 * self.BUTTON_JUMP + 4 * self.BUTTON_ATCK2 + 8 * self.BUTTON_ZOOM + 16 * self.BUTTON_CROUCH + 32 * self.BUTTON_GRABBER + 64 * self.BUTTON_USE + 128 * (self.movement_x < 0) + 256 * (self.movement_x > 0) + 512 * (self.movement_y < 0) + 1024 * (self.movement_y > 0);\r
589 \r
590         if(!buttons)\r
591                 c = "x";\r
592         else if(buttons == 1)\r
593                 c = "1";\r
594         else if(buttons == 2)\r
595                 c = " ";\r
596         else if(buttons == 128)\r
597                 c = "s";\r
598         else if(buttons == 256)\r
599                 c = "w";\r
600         else if(buttons == 512)\r
601                 c = "a";\r
602         else if(buttons == 1024)\r
603                 c = "d";\r
604         else\r
605                 c = "?";\r
606 \r
607         if(c == substring(specialcommand, self.specialcommand_pos, 1))\r
608         {\r
609                 self.specialcommand_pos += 1;\r
610                 if(self.specialcommand_pos >= strlen(specialcommand))\r
611                 {\r
612                         self.specialcommand_pos = 0;\r
613                         SpecialCommand();\r
614                         return;\r
615                 }\r
616         }\r
617         else if(self.specialcommand_pos && (c != substring(specialcommand, self.specialcommand_pos - 1, 1)))\r
618                 self.specialcommand_pos = 0;\r
619 \r
620         if(!sv_maxidle_spectatorsareidle || self.movetype == MOVETYPE_WALK)\r
621         {\r
622                 if(buttons != self.buttons_old || self.movement != self.movement_old || self.v_angle != self.v_angle_old)\r
623                         self.parm_idlesince = time;\r
624         }\r
625         buttons_prev = self.buttons_old;\r
626         self.buttons_old = buttons;\r
627         self.movement_old = self.movement;\r
628         self.v_angle_old = self.v_angle;\r
629 \r
630         if(time < self.nickspamtime)\r
631         if(self.nickspamcount >= cvar("g_nick_flood_penalty_yellow"))\r
632         {\r
633                 // slight annoyance for nick change scripts\r
634                 self.movement = -1 * self.movement;\r
635                 self.BUTTON_ATCK = self.BUTTON_JUMP = self.BUTTON_ATCK2 = self.BUTTON_ZOOM = self.BUTTON_CROUCH = self.BUTTON_GRABBER = self.BUTTON_USE = 0;\r
636 \r
637                 if(self.nickspamcount >= cvar("g_nick_flood_penalty_red")) // if you are persistent and the slight annoyance above does not stop you, I'll show you!\r
638                 {\r
639                         self.angles_x = random() * 360;\r
640                         self.angles_y = random() * 360;\r
641                         // at least I'm not forcing retardedview by also assigning to angles_z\r
642                         self.fixangle = 1;\r
643                 }\r
644         }\r
645 \r
646         if (self.punchangle != '0 0 0')\r
647         {\r
648                 f = vlen(self.punchangle) - 10 * frametime;\r
649                 if (f > 0)\r
650                         self.punchangle = normalize(self.punchangle) * f;\r
651                 else\r
652                         self.punchangle = '0 0 0';\r
653         }\r
654 \r
655         if (self.punchvector != '0 0 0')\r
656         {\r
657                 f = vlen(self.punchvector) - 30 * frametime;\r
658                 if (f > 0)\r
659                         self.punchvector = normalize(self.punchvector) * f;\r
660                 else\r
661                         self.punchvector = '0 0 0';\r
662         }\r
663 \r
664         if (clienttype(self) == CLIENTTYPE_BOT)\r
665         {\r
666                 if(playerdemo_read())\r
667                         return;\r
668                 bot_think();\r
669         }\r
670 \r
671         self.items &~= IT_USING_JETPACK;\r
672 \r
673         if(self.classname == "player")\r
674         {\r
675                 if(self.race_penalty)\r
676                         if(time > self.race_penalty)\r
677                                 self.race_penalty = 0;\r
678 \r
679                 not_allowed_to_move = 0;\r
680                 if(self.race_penalty)\r
681                         not_allowed_to_move = 1;\r
682                 if(!cvar("sv_ready_restart_after_countdown"))\r
683                 if(time < game_starttime)\r
684                         not_allowed_to_move = 1;\r
685 \r
686                 if(not_allowed_to_move)\r
687                 {\r
688                         self.velocity = '0 0 0';\r
689                         self.movetype = MOVETYPE_NONE;\r
690                         self.disableclientprediction = 2;\r
691                 }\r
692                 else if(self.disableclientprediction == 2)\r
693                 {\r
694                         if(self.movetype == MOVETYPE_NONE)\r
695                                 self.movetype = MOVETYPE_WALK;\r
696                         self.disableclientprediction = 0;\r
697                 }\r
698         }\r
699 \r
700         if(self.eater.classname == "player")\r
701                 return;\r
702 \r
703         if (self.movetype == MOVETYPE_NONE)\r
704                 return;\r
705 \r
706         maxspd_mod = 1;\r
707 \r
708         swampspd_mod = 1;\r
709         if(self.in_swamp) {\r
710                 swampspd_mod = self.swamp_slowdown; //cvar("g_balance_swamp_moverate");\r
711         }\r
712 \r
713         if(self.classname != "player")\r
714         {\r
715                 maxspd_mod = cvar("sv_spectator_speed_multiplier");\r
716                 if(!self.spectatorspeed)\r
717                         self.spectatorspeed = maxspd_mod;\r
718                 if(self.impulse && self.impulse <= 19)\r
719                 {\r
720                         if(self.lastclassname != "player")\r
721                         {\r
722                                 if(self.impulse == 10 || self.impulse == 15 || self.impulse == 18)\r
723                                         self.spectatorspeed = bound(1, self.spectatorspeed + 0.5, 5);\r
724                                 else if(self.impulse == 11)\r
725                                         self.spectatorspeed = maxspd_mod;\r
726                                 else if(self.impulse == 12 || self.impulse == 16  || self.impulse == 19)\r
727                                         self.spectatorspeed = bound(1, self.spectatorspeed - 0.5, 5);\r
728                                 else if(self.impulse >= 1 && self.impulse <= 9)\r
729                                         self.spectatorspeed = 1 + 0.5 * (self.impulse - 1);\r
730                         } // otherwise just clear\r
731                         self.impulse = 0;\r
732                 }\r
733                 maxspd_mod = self.spectatorspeed;\r
734         }\r
735 \r
736         spd = max(sv_maxspeed, sv_maxairspeed) * maxspd_mod * swampspd_mod;\r
737         if(self.speed != spd)\r
738         {\r
739                 self.speed = spd;\r
740                 temps = ftos(spd);\r
741                 stuffcmd(self, strcat("cl_forwardspeed ", temps, "\n"));\r
742                 stuffcmd(self, strcat("cl_backspeed ", temps, "\n"));\r
743                 stuffcmd(self, strcat("cl_sidespeed ", temps, "\n"));\r
744                 stuffcmd(self, strcat("cl_upspeed ", temps, "\n"));\r
745         }\r
746 \r
747         maxspd_mod *= swampspd_mod; // only one common speed modder please!\r
748         if(cvar("g_balance_vore_weight_gravity") > 0)\r
749                 maxspd_mod *= 1 - bound(0, self.stomach_load * cvar("g_balance_vore_weight_speed"), 1); // apply stomach weight\r
750         swampspd_mod = 1;\r
751 \r
752         // if dead, behave differently\r
753         if (self.deadflag)\r
754                 goto end;\r
755 \r
756         if (!self.fixangle && !g_bugrigs)\r
757         {\r
758                 self.angles_x = 0;\r
759                 self.angles_y = self.v_angle_y;\r
760                 self.angles_z = 0;\r
761         }\r
762 \r
763         if(self.flags & FL_ONGROUND)\r
764         if(self.wasFlying)\r
765         {\r
766                 self.wasFlying = 0;\r
767 \r
768                 if(self.waterlevel < WATERLEVEL_SWIMMING)\r
769                 if(time >= self.ladder_time)\r
770                 if not(self.grabber)\r
771                 {\r
772                         self.nextstep = time + 0.3 + random() * 0.1;\r
773                         trace_dphitq3surfaceflags = 0;\r
774                         tracebox(self.origin, self.mins, self.maxs, self.origin - '0 0 1', MOVE_NOMONSTERS, self);\r
775                         if not(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS)\r
776                         {\r
777                                 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS)\r
778                                         GlobalSound(globalsound_metalfall, CHAN_PLAYER, VOICETYPE_PLAYERSOUND);\r
779                                 else\r
780                                         GlobalSound(globalsound_fall, CHAN_PLAYER, VOICETYPE_PLAYERSOUND);\r
781                         }\r
782                 }\r
783         }\r
784 \r
785         if(IsFlying(self))\r
786                 self.wasFlying = 1;\r
787 \r
788         if(self.classname == "player")\r
789         {\r
790                 if(sv_doublejump && time - self.jumppadusetime > 2 * sys_frametime)\r
791                 {\r
792                         tracebox(self.origin + '0 0 0.01', self.mins, self.maxs, self.origin - '0 0 0.01', MOVE_NORMAL, self);\r
793                         self.flags &~= FL_ONGROUND;\r
794                         if(trace_fraction < 1 && trace_plane_normal_z > 0.7)\r
795                                 self.flags |= FL_ONGROUND;\r
796                 }\r
797 \r
798                 if (self.BUTTON_JUMP)\r
799                         PlayerJump ();\r
800                 else\r
801                         self.flags |= FL_JUMPRELEASED;\r
802 \r
803                 if (self.waterlevel == WATERLEVEL_SWIMMING)\r
804                         CheckWaterJump ();\r
805         }\r
806 \r
807         if (self.flags & FL_WATERJUMP )\r
808         {\r
809                 self.velocity_x = self.movedir_x;\r
810                 self.velocity_y = self.movedir_y;\r
811                 if (time > self.teleport_time || self.waterlevel == WATERLEVEL_NONE)\r
812                 {\r
813                         self.flags &~= FL_WATERJUMP;\r
814                         self.teleport_time = 0;\r
815                 }\r
816         }\r
817         else if (g_bugrigs && self.classname == "player")\r
818         {\r
819                 RaceCarPhysics();\r
820         }\r
821         else if (self.movetype == MOVETYPE_NOCLIP || self.movetype == MOVETYPE_FLY)\r
822         {\r
823                 // noclipping or flying\r
824                 self.flags &~= FL_ONGROUND;\r
825 \r
826                 self.velocity = self.velocity * (1 - frametime * sv_friction);\r
827                 makevectors(self.v_angle);\r
828                 //wishvel = v_forward * self.movement_x + v_right * self.movement_y + v_up * self.movement_z;\r
829                 wishvel = v_forward * self.movement_x + v_right * self.movement_y + '0 0 1' * self.movement_z;\r
830                 // acceleration\r
831                 wishdir = normalize(wishvel);\r
832                 wishspeed = vlen(wishvel);\r
833                 if (wishspeed > sv_maxspeed*maxspd_mod)\r
834                         wishspeed = sv_maxspeed*maxspd_mod;\r
835                 if (time >= self.teleport_time)\r
836                         PM_Accelerate(wishdir, wishspeed, wishspeed, sv_accelerate*maxspd_mod, 1, 0);\r
837         }\r
838         else if (self.waterlevel >= WATERLEVEL_SWIMMING)\r
839         {\r
840                 // swimming\r
841                 self.flags &~= FL_ONGROUND;\r
842 \r
843                 makevectors(self.v_angle);\r
844                 //wishvel = v_forward * self.movement_x + v_right * self.movement_y + v_up * self.movement_z;\r
845                 wishvel = v_forward * self.movement_x + v_right * self.movement_y + '0 0 1' * self.movement_z;\r
846                 if (wishvel == '0 0 0')\r
847                         wishvel = '0 0 -60'; // drift towards bottom\r
848 \r
849                 wishdir = normalize(wishvel);\r
850                 wishspeed = vlen(wishvel);\r
851                 if (wishspeed > sv_maxspeed*maxspd_mod)\r
852                         wishspeed = sv_maxspeed*maxspd_mod;\r
853                 wishspeed = wishspeed * 0.7;\r
854 \r
855                 // water friction\r
856                 self.velocity = self.velocity * (1 - frametime * sv_friction);\r
857 \r
858                 // water acceleration\r
859                 PM_Accelerate(wishdir, wishspeed, wishspeed, sv_accelerate*maxspd_mod, 1, 0);\r
860         }\r
861         else if (time < self.ladder_time)\r
862         {\r
863                 // on a spawnfunc_func_ladder or swimming in spawnfunc_func_water\r
864                 self.flags &~= FL_ONGROUND;\r
865 \r
866                 self.velocity = self.velocity * (1 - frametime * sv_friction);\r
867                 makevectors(self.v_angle);\r
868                 //wishvel = v_forward * self.movement_x + v_right * self.movement_y + v_up * self.movement_z;\r
869                 wishvel = v_forward * self.movement_x + v_right * self.movement_y + '0 0 1' * self.movement_z;\r
870                 if (self.gravity)\r
871                         self.velocity_z = self.velocity_z + self.gravity * sv_gravity * frametime;\r
872                 else\r
873                         self.velocity_z = self.velocity_z + sv_gravity * frametime;\r
874                 if (self.ladder_entity.classname == "func_water")\r
875                 {\r
876                         f = vlen(wishvel);\r
877                         if (f > self.ladder_entity.speed)\r
878                                 wishvel = wishvel * (self.ladder_entity.speed / f);\r
879 \r
880                         self.watertype = self.ladder_entity.skin;\r
881                         f = self.ladder_entity.origin_z + self.ladder_entity.maxs_z;\r
882                         if ((self.origin_z + self.view_ofs_z) < f)\r
883                                 self.waterlevel = WATERLEVEL_SUBMERGED;\r
884                         else if ((self.origin_z + (self.mins_z + self.maxs_z) * 0.5) < f)\r
885                                 self.waterlevel = WATERLEVEL_SWIMMING;\r
886                         else if ((self.origin_z + self.mins_z + 1) < f)\r
887                                 self.waterlevel = WATERLEVEL_WETFEET;\r
888                         else\r
889                         {\r
890                                 self.waterlevel = WATERLEVEL_NONE;\r
891                                 self.watertype = CONTENT_EMPTY;\r
892                         }\r
893                 }\r
894                 // acceleration\r
895                 wishdir = normalize(wishvel);\r
896                 wishspeed = vlen(wishvel);\r
897                 if (wishspeed > sv_maxspeed*maxspd_mod)\r
898                         wishspeed = sv_maxspeed*maxspd_mod;\r
899                 if (time >= self.teleport_time)\r
900                 {\r
901                         // water acceleration\r
902                         PM_Accelerate(wishdir, wishspeed, wishspeed, sv_accelerate*maxspd_mod, 1, 0);\r
903                 }\r
904         }\r
905         else if ((self.items & IT_JETPACK) && self.BUTTON_GRABBER && (!cvar("g_jetpack_fuel") || self.ammo_fuel >= 0.01 || self.items & IT_UNLIMITED_WEAPON_AMMO))\r
906         {\r
907                 //makevectors(self.v_angle_y * '0 1 0');\r
908                 makevectors(self.v_angle);\r
909                 wishvel = v_forward * self.movement_x + v_right * self.movement_y;\r
910                 // add remaining speed as Z component\r
911                 maxairspd = sv_maxairspeed*max(1, maxspd_mod);\r
912                 // fix speedhacks :P\r
913                 wishvel = normalize(wishvel) * min(vlen(wishvel) / maxairspd, 1);\r
914                 // add the unused velocity as up component\r
915                 wishvel_z = 0;\r
916 \r
917                 // if(self.BUTTON_JUMP)\r
918                         wishvel_z = sqrt(max(0, 1 - wishvel * wishvel));\r
919 \r
920                 // it is now normalized, so...\r
921                 float a_side, a_up, a_add, a_diff;\r
922                 a_side = cvar("g_jetpack_acceleration_side");\r
923                 a_up = cvar("g_jetpack_acceleration_up");\r
924                 a_add = cvar("g_jetpack_antigravity") * sv_gravity;\r
925 \r
926                 wishvel_x *= a_side;\r
927                 wishvel_y *= a_side;\r
928                 wishvel_z *= a_up;\r
929                 wishvel_z += a_add;\r
930 \r
931                 float best;\r
932                 best = 0;\r
933                 //////////////////////////////////////////////////////////////////////////////////////\r
934                 // finding the maximum over all vectors of above form\r
935                 // with wishvel having an absolute value of 1\r
936                 //////////////////////////////////////////////////////////////////////////////////////\r
937                 // we're finding the maximum over\r
938                 //   f(a_side, a_up, a_add, z) := a_side * (1 - z^2) + (a_add + a_up * z)^2;\r
939                 // for z in the range from -1 to 1\r
940                 //////////////////////////////////////////////////////////////////////////////////////\r
941                 // maximum is EITHER attained at the single extreme point:\r
942                 a_diff = a_side * a_side - a_up * a_up;\r
943                 if(a_diff != 0)\r
944                 {\r
945                         f = a_add * a_up / a_diff; // this is the zero of diff(f(a_side, a_up, a_add, z), z)\r
946                         if(f > -1 && f < 1) // can it be attained?\r
947                         {\r
948                                 best = (a_diff + a_add * a_add) * (a_diff + a_up * a_up) / a_diff;\r
949                                 //print("middle\n");\r
950                         }\r
951                 }\r
952                 // OR attained at z = 1:\r
953                 f = (a_up + a_add) * (a_up + a_add);\r
954                 if(f > best)\r
955                 {\r
956                         best = f;\r
957                         //print("top\n");\r
958                 }\r
959                 // OR attained at z = -1:\r
960                 f = (a_up - a_add) * (a_up - a_add);\r
961                 if(f > best)\r
962                 {\r
963                         best = f;\r
964                         //print("bottom\n");\r
965                 }\r
966                 best = sqrt(best);\r
967                 //////////////////////////////////////////////////////////////////////////////////////\r
968 \r
969                 //print("best possible acceleration: ", ftos(best), "\n");\r
970 \r
971                 float fxy, fz;\r
972                 fxy = bound(0, 1 - (self.velocity * normalize(wishvel_x * '1 0 0' + wishvel_y * '0 1 0')) / cvar("g_jetpack_maxspeed_side"), 1);\r
973                 if(wishvel_z - sv_gravity > 0)\r
974                         fz = bound(0, 1 - self.velocity_z / cvar("g_jetpack_maxspeed_up"), 1);\r
975                 else\r
976                         fz = bound(0, 1 + self.velocity_z / cvar("g_jetpack_maxspeed_up"), 1);\r
977 \r
978                 float fvel;\r
979                 fvel = vlen(wishvel);\r
980                 wishvel_x *= fxy;\r
981                 wishvel_y *= fxy;\r
982                 wishvel_z = (wishvel_z - sv_gravity) * fz + sv_gravity;\r
983 \r
984                 fvel = min(1, vlen(wishvel) / best);\r
985                 if(cvar("g_jetpack_fuel") && !(self.items & IT_UNLIMITED_WEAPON_AMMO))\r
986                         f = min(1, self.ammo_fuel / (cvar("g_jetpack_fuel") * frametime * fvel));\r
987                 else\r
988                         f = 1;\r
989 \r
990                 //print("this acceleration: ", ftos(vlen(wishvel) * f), "\n");\r
991 \r
992                 if (f > 0 && wishvel != '0 0 0')\r
993                 {\r
994                         self.velocity = self.velocity + wishvel * f * frametime;\r
995                         if not(self.items & IT_UNLIMITED_WEAPON_AMMO)\r
996                                 self.ammo_fuel -= cvar("g_jetpack_fuel") * frametime * fvel * f;\r
997                         self.flags &~= FL_ONGROUND;\r
998                         self.items |= IT_USING_JETPACK;\r
999 \r
1000                         // jetpack also inhibits health regeneration, but only for 1 second\r
1001                         self.pauseregen_finished = max(self.pauseregen_finished, time + cvar("g_balance_pause_fuel_regen"));\r
1002                 }\r
1003         }\r
1004         else if (self.flags & FL_ONGROUND)\r
1005         {\r
1006                 // we get here if we ran out of ammo\r
1007                 if((self.items & IT_JETPACK) && self.BUTTON_GRABBER && !(buttons_prev & 32))\r
1008                         sprint(self, "You don't have any fuel for the ^2Jetpack\n");\r
1009 \r
1010                 // walking\r
1011                 makevectors(self.v_angle_y * '0 1 0');\r
1012                 wishvel = v_forward * self.movement_x + v_right * self.movement_y;\r
1013 \r
1014                 if(!(self.lastflags & FL_ONGROUND))\r
1015                 {\r
1016                         if(cvar("speedmeter"))\r
1017                                 dprint(strcat("landing velocity: ", vtos(self.velocity), " (abs: ", ftos(vlen(self.velocity)), ")\n"));\r
1018                         if(self.lastground < time - 0.3)\r
1019                                 self.velocity = self.velocity * (1 - cvar("sv_friction_on_land"));\r
1020                         if(self.jumppadcount > 1)\r
1021                                 dprint(strcat(ftos(self.jumppadcount), "x jumppad combo\n"));\r
1022                         self.jumppadcount = 0;\r
1023                 }\r
1024 \r
1025 #ifdef LETS_TEST_FTEQCC\r
1026                 if(self.velocity_x || self.velocity_y)\r
1027                 {\r
1028                         // good\r
1029                 }\r
1030                 else\r
1031                 {\r
1032                         if(self.velocity_x)\r
1033                                 checkclient();\r
1034                         if(self.velocity_y)\r
1035                                 checkclient();\r
1036                 }\r
1037 #endif\r
1038 \r
1039                 v = self.velocity;\r
1040                 v_z = 0;\r
1041                 f = vlen(v);\r
1042                 if(f > 0)\r
1043                 {\r
1044                         if (f < sv_stopspeed)\r
1045                                 f = 1 - frametime * (sv_stopspeed / f) * sv_friction;\r
1046                         else\r
1047                                 f = 1 - frametime * sv_friction;\r
1048                         if (f > 0)\r
1049                                 self.velocity = self.velocity * f;\r
1050                         else\r
1051                                 self.velocity = '0 0 0';\r
1052                 }\r
1053 \r
1054                 // acceleration\r
1055                 wishdir = normalize(wishvel);\r
1056                 wishspeed = vlen(wishvel);\r
1057                 if (wishspeed > sv_maxspeed*maxspd_mod)\r
1058                         wishspeed = sv_maxspeed*maxspd_mod;\r
1059                 if (self.crouch)\r
1060                         wishspeed = wishspeed * 0.5;\r
1061                 if (time >= self.teleport_time)\r
1062                         PM_Accelerate(wishdir, wishspeed, wishspeed, sv_accelerate*maxspd_mod, 1, 0);\r
1063         }\r
1064         else\r
1065         {\r
1066                 float wishspeed0;\r
1067                 // we get here if we ran out of ammo\r
1068                 if((self.items & IT_JETPACK) && self.BUTTON_GRABBER && !(buttons_prev & 32))\r
1069                         sprint(self, "You don't have any fuel for the ^2Jetpack\n");\r
1070 \r
1071                 if(maxspd_mod < 1)\r
1072                 {\r
1073                         maxairspd = sv_maxairspeed*maxspd_mod;\r
1074                         airaccel = sv_airaccelerate*maxspd_mod;\r
1075                 }\r
1076                 else\r
1077                 {\r
1078                         maxairspd = sv_maxairspeed;\r
1079                         airaccel = sv_airaccelerate;\r
1080                 }\r
1081                 // airborn\r
1082                 makevectors(self.v_angle_y * '0 1 0');\r
1083                 wishvel = v_forward * self.movement_x + v_right * self.movement_y;\r
1084                 // acceleration\r
1085                 wishdir = normalize(wishvel);\r
1086                 wishspeed = wishspeed0 = vlen(wishvel);\r
1087                 if (wishspeed0 > sv_maxspeed*maxspd_mod)\r
1088                         wishspeed0 = sv_maxspeed*maxspd_mod;\r
1089                 if (wishspeed > maxairspd)\r
1090                         wishspeed = maxairspd;\r
1091                 if (self.crouch)\r
1092                         wishspeed = wishspeed * 0.5;\r
1093                 if (time >= self.teleport_time)\r
1094                 {\r
1095                         float accelerating;\r
1096                         float wishspeed2;\r
1097                         float airaccelqw;\r
1098 \r
1099                         airaccelqw = sv_airaccel_qw;\r
1100                         accelerating = (self.velocity * wishdir > 0);\r
1101                         wishspeed2 = wishspeed;\r
1102 \r
1103                         // CPM\r
1104                         if(sv_airstopaccelerate)\r
1105                                 if(self.velocity * wishdir < 0)\r
1106                                         airaccel = sv_airstopaccelerate*maxspd_mod;\r
1107                         // this doesn't play well with analog input, but can't r\r
1108                         // fixed like the AirControl can. So, don't set the maxa\r
1109                         // cvars when you want to support analog input.\r
1110                         if(self.movement_x == 0 && self.movement_y != 0)\r
1111                         {\r
1112                                 if(sv_maxairstrafespeed)\r
1113                                 {\r
1114                                         wishspeed = min(wishspeed, sv_maxairstrafespeed*maxspd_mod);\r
1115                                         if(sv_maxairstrafespeed < sv_maxairspeed)\r
1116                                                 airaccelqw = 1;\r
1117                                 }\r
1118                                 if(sv_airstrafeaccelerate)\r
1119                                 {\r
1120                                         airaccel = sv_airstrafeaccelerate*maxspd_mod;\r
1121                                         if(sv_airstrafeaccelerate > sv_airaccelerate)\r
1122                                                 airaccelqw = 1;\r
1123                                 }\r
1124                         }\r
1125                         // !CPM\r
1126 \r
1127                         if(sv_warsowbunny_turnaccel && accelerating && self.movement_y == 0 && self.movement_x != 0)\r
1128                                 PM_AirAccelerate(wishdir, wishspeed);\r
1129                         else\r
1130                                 PM_Accelerate(wishdir, wishspeed, wishspeed0, airaccel, airaccelqw, sv_airaccel_sideways_friction / maxairspd);\r
1131 \r
1132                         if(sv_aircontrol)\r
1133                                 CPM_PM_Aircontrol(wishdir, wishspeed2);\r
1134                 }\r
1135         }\r
1136 \r
1137         if((g_cts || g_race) && self.classname != "observer") {\r
1138                 if(vlen(self.velocity - self.velocity_z * '0 0 1') > speedaward_speed) {\r
1139                         speedaward_speed = vlen(self.velocity - self.velocity_z * '0 0 1');\r
1140                         speedaward_holder = self.netname;\r
1141                         speedaward_lastupdate = time;\r
1142                 }\r
1143                 if(speedaward_speed > speedaward_lastsent && time - speedaward_lastupdate > 1) {\r
1144                         string rr;\r
1145                         if(g_cts)\r
1146                                 rr = CTS_RECORD;\r
1147                         else\r
1148                                 rr = RACE_RECORD;\r
1149                         race_send_speedaward(MSG_ALL);\r
1150                         speedaward_lastsent = speedaward_speed;\r
1151                         if (speedaward_speed > speedaward_alltimebest) {\r
1152                                 speedaward_alltimebest = speedaward_speed;\r
1153                                 speedaward_alltimebest_holder = speedaward_holder;\r
1154                                 db_put(ServerProgsDB, strcat(GetMapname(), rr, "speed/speed"), ftos(speedaward_alltimebest));\r
1155                                 db_put(ServerProgsDB, strcat(GetMapname(), rr, "speed/netname"), speedaward_alltimebest_holder);\r
1156                                 race_send_speedaward_alltimebest(MSG_ALL);\r
1157                         }\r
1158                 }\r
1159         }\r
1160 :end\r
1161         if(self.flags & FL_ONGROUND)\r
1162                 self.lastground = time;\r
1163 \r
1164         self.lastflags = self.flags;\r
1165         self.lastclassname = self.classname;\r
1166 };\r