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