]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/movetypes.qc
Network dphitcontentsmask for accurate prediction on clips, almost fix stairs prediction
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / movetypes.qc
1 #include "../common/physics.qh"
2
3 #if defined(CSQC)
4         #include "../dpdefs/csprogsdefs.qh"
5         #include "defs.qh"
6         #include "../common/stats.qh"
7         #include "../common/util.qh"
8         #include "movetypes.qh"
9         #include "../csqcmodellib/common.qh"
10         #include "../server/t_items.qh"
11 #elif defined(MENUQC)
12 #elif defined(SVQC)
13         #include "../server/autocvars.qh"
14 #endif
15
16
17 #ifdef SVQC
18 #define GRAVITY_UNAFFECTED_BY_TICRATE autocvar_sv_gameplayfix_gravityunaffectedbyticrate
19
20 #define TICRATE sys_frametime
21 #elif defined(CSQC)
22 #define GRAVITY_UNAFFECTED_BY_TICRATE (getstati(STAT_MOVEFLAGS) & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
23
24 #define TICRATE ticrate
25 #endif
26
27 .entity move_groundentity; // FIXME add move_groundnetworkentity?
28 .float move_suspendedinair;
29 .float move_didgravity;
30
31 void _Movetype_CheckVelocity() // SV_CheckVelocity
32 {
33 }
34
35 float _Movetype_CheckWater(entity ent) // SV_CheckWater
36 {
37         vector point = ent.move_origin;
38         point_z += (ent.mins_z + 1);
39
40         int nativecontents = pointcontents(point);
41
42         if(ent.move_watertype)
43         if(ent.move_watertype != nativecontents)
44         {
45                 //print(sprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", ent.move_watertype, nativecontents));
46                 if(ent.contentstransition)
47                         ent.contentstransition(ent.move_watertype, nativecontents);
48         }
49
50         ent.move_waterlevel = 0;
51         ent.move_watertype = CONTENT_EMPTY;
52
53         int supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents);
54         if(supercontents & DPCONTENTS_LIQUIDSMASK)
55         {
56                 ent.move_watertype = nativecontents;
57                 ent.move_waterlevel = 1;
58                 point_y = (ent.origin_y + ((ent.mins_z + ent.maxs_y) * 0.5));
59                 if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
60                 {
61                         ent.move_waterlevel = 2;
62                         point_y = ent.origin_y + ent.view_ofs_y;
63                         if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
64                                 ent.move_waterlevel = 3;
65                 }
66         }
67
68         return (ent.move_waterlevel > 1);
69 }
70
71 void _Movetype_CheckWaterTransition(entity ent) // SV_CheckWaterTransition
72 {
73         float contents = pointcontents(ent.move_origin);
74
75         if(!ent.move_watertype)
76         {
77                 // just spawned here
78                 if(!autocvar_cl_gameplayfix_fixedcheckwatertransition)
79                 {
80                         ent.move_watertype = contents;
81                         ent.move_waterlevel = 1;
82                         return;
83                 }
84         }
85         else if(ent.move_watertype != contents)
86         {
87                 //print(sprintf("_Movetype_CheckWaterTransition(): Origin: %s, Direct: '%d', Original: '%d', New: '%d'\n", vtos(ent.move_origin), pointcontents(ent.move_origin), ent.move_watertype, contents));
88                 if(ent.contentstransition)
89                         ent.contentstransition(ent.move_watertype, contents);
90         }
91
92         if(contents <= CONTENT_WATER)
93         {
94                 ent.move_watertype = contents;
95                 ent.move_waterlevel = 1;
96         }
97         else
98         {
99                 ent.move_watertype = CONTENT_EMPTY;
100                 ent.move_waterlevel = (autocvar_cl_gameplayfix_fixedcheckwatertransition ? 0 : contents);
101         }
102 }
103
104 void _Movetype_Impact(entity oth) // SV_Impact
105 {
106         entity oldother, oldself;
107
108         oldself = self;
109         oldother = other;
110
111         if(self.move_touch)
112         {
113                 other = oth;
114
115                 self.move_touch();
116
117                 other = oldother;
118         }
119
120         if(oth.move_touch)
121         {
122                 other = self;
123                 self = oth;
124
125                 self.move_touch();
126
127                 self = oldself;
128                 other = oldother;
129         }
130 }
131
132 void _Movetype_LinkEdict_TouchAreaGrid() // SV_LinkEdict_TouchAreaGrid
133 {
134         entity e, oldself, oldother;
135
136         oldself = self;
137         oldother = other;
138
139         for(e = findradius(0.5 * (self.absmin + self.absmax), 0.5 * vlen(self.absmax - self.absmin)); e; e = e.chain)
140         {
141                 if(e.move_touch)
142                 if(boxesoverlap(e.absmin, e.absmax, oldself.absmin, oldself.absmax))
143                 {
144                         self = e;
145                         other = oldself;
146
147                         trace_allsolid = false;
148                         trace_startsolid = false;
149                         trace_fraction = 1;
150                         trace_inwater = false;
151                         trace_inopen = true;
152                         trace_endpos = e.origin;
153                         trace_plane_normal = '0 0 1';
154                         trace_plane_dist = 0;
155                         trace_ent = oldself;
156
157                         e.move_touch();
158                 }
159         }
160
161         other = oldother;
162         self = oldself;
163 }
164
165 void _Movetype_LinkEdict(float touch_triggers) // SV_LinkEdict
166 {
167         vector mi, ma;
168         if(self.solid == SOLID_BSP)
169         {
170                 // TODO set the absolute bbox
171                 mi = self.mins;
172                 ma = self.maxs;
173         }
174         else
175         {
176                 mi = self.mins;
177                 ma = self.maxs;
178         }
179         mi = mi + self.origin;
180         ma = ma + self.origin;
181
182         if(self.move_flags & FL_ITEM)
183         {
184                 mi_x -= 15;
185                 mi_y -= 15;
186                 mi_z -= 1;
187                 ma_x += 15;
188                 ma_y += 15;
189                 ma_z += 1;
190         }
191         else
192         {
193                 mi_x -= 1;
194                 mi_y -= 1;
195                 mi_z -= 1;
196                 ma_x += 1;
197                 ma_y += 1;
198                 ma_z += 1;
199         }
200
201         self.absmin = mi;
202         self.absmax = ma;
203
204         if(touch_triggers)
205                 _Movetype_LinkEdict_TouchAreaGrid();
206 }
207
208 float _Movetype_TestEntityPosition(vector ofs) // SV_TestEntityPosition
209 {
210         vector org;
211         org = self.move_origin + ofs;
212
213         int cont = self.dphitcontentsmask;
214         self.dphitcontentsmask = DPCONTENTS_SOLID;
215         tracebox(self.move_origin, self.mins, self.maxs, self.move_origin, MOVE_NOMONSTERS, self);
216         self.dphitcontentsmask = cont;
217
218         if(trace_startsolid)
219                 return true;
220
221         if(vlen(trace_endpos - self.move_origin) > 0.0001)
222                 self.move_origin = trace_endpos;
223         return false;
224 }
225
226 float _Movetype_UnstickEntity() // SV_UnstickEntity
227 {
228         if(!_Movetype_TestEntityPosition('0 0 0'))
229                 return true;
230         if(!_Movetype_TestEntityPosition('-1 0 0')) goto success;
231         if(!_Movetype_TestEntityPosition('1 0 0')) goto success;
232         if(!_Movetype_TestEntityPosition('0 -1 0')) goto success;
233         if(!_Movetype_TestEntityPosition('0 1 0')) goto success;
234         if(!_Movetype_TestEntityPosition('-1 -1 0')) goto success;
235         if(!_Movetype_TestEntityPosition('1 -1 0')) goto success;
236         if(!_Movetype_TestEntityPosition('-1 1 0')) goto success;
237         if(!_Movetype_TestEntityPosition('1 1 0')) goto success;
238         float i;
239         for(i = 1; i <= 17; ++i)
240         {
241                 if(!_Movetype_TestEntityPosition('0 0 -1' * i)) goto success;
242                 if(!_Movetype_TestEntityPosition('0 0 1' * i)) goto success;
243         }
244         dprintf("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n", num_for_edict(self), self.classname, vtos(self.move_origin));
245         return false;
246 :success
247         dprintf("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n", num_for_edict(self), self.classname, vtos(self.move_origin));
248         _Movetype_LinkEdict(true);
249         return true;
250 }
251
252 vector _Movetype_ClipVelocity(vector vel, vector norm, float f) // SV_ClipVelocity
253 {
254         vel = vel - ((vel * norm) * norm) * f;
255
256         if(vel_x > -0.1 && vel_x < 0.1) vel_x = 0;
257         if(vel_y > -0.1 && vel_y < 0.1) vel_y = 0;
258         if(vel_z > -0.1 && vel_z < 0.1) vel_z = 0;
259
260         return vel;
261 }
262
263 void _Movetype_PushEntityTrace(vector push)
264 {
265         vector end;
266         float type;
267
268         end = self.move_origin + push;
269
270         if(self.move_nomonsters)
271                 type = max(0, self.move_nomonsters);
272         else if(self.move_movetype == MOVETYPE_FLYMISSILE)
273                 type = MOVE_MISSILE;
274         else if(self.solid == SOLID_TRIGGER || self.solid == SOLID_NOT)
275                 type = MOVE_NOMONSTERS;
276         else
277                 type = MOVE_NORMAL;
278
279         tracebox(self.move_origin, self.mins, self.maxs, end, type, self);
280 }
281
282 float _Movetype_PushEntity(vector push, float failonstartsolid) // SV_PushEntity
283 {
284         _Movetype_PushEntityTrace(push);
285
286         if(trace_startsolid && failonstartsolid)
287                 return trace_fraction;
288
289         self.move_origin = trace_endpos;
290
291         if(trace_fraction < 1)
292                 if(self.solid >= SOLID_TRIGGER && (!(self.move_flags & FL_ONGROUND) || (self.move_groundentity != trace_ent)))
293                         _Movetype_Impact(trace_ent);
294
295         return trace_fraction;
296 }
297
298
299 .float ltime;
300 .void() blocked;
301 void _Movetype_AngleVectorsFLU(vector myangles) // AngleVectorsFLU
302 {
303         float angle, sr, sp, sy, cr, cp, cy;
304
305         v_forward = v_right = v_up = '0 0 0';
306
307         angle = myangles_y * (M_PI*2 / 360);
308         sy = sin(angle);
309         cy = cos(angle);
310         angle = myangles_x * (M_PI*2 / 360);
311         sp = sin(angle);
312         cp = cos(angle);
313         if(v_forward)
314         {
315                 v_forward_x = cp*cy;
316                 v_forward_y = cp*sy;
317                 v_forward_z = -sp;
318         }
319         if(v_right || v_up)
320         {
321                 if(myangles_z)
322                 {
323                         angle = myangles_z * (M_PI*2 / 360);
324                         sr = sin(angle);
325                         cr = cos(angle);
326                         if(v_right)
327                         {
328                                 v_right_x = sr*sp*cy+cr*-sy;
329                                 v_right_y = sr*sp*sy+cr*cy;
330                                 v_right_z = sr*cp;
331                         }
332                         if(v_up)
333                         {
334                                 v_up_x = cr*sp*cy+-sr*-sy;
335                                 v_up_y = cr*sp*sy+-sr*cy;
336                                 v_up_z = cr*cp;
337                         }
338                 }
339                 else
340                 {
341                         if(v_right)
342                         {
343                                 v_right_x = -sy;
344                                 v_right_y = cy;
345                                 v_right_z = 0;
346                         }
347                         if(v_up)
348                         {
349                                 v_up_x = sp*cy;
350                                 v_up_y = sp*sy;
351                                 v_up_z = cp;
352                         }
353                 }
354         }
355 }
356
357 void _Movetype_PushMove(float dt) // SV_PushMove
358 {
359         bool rotated;
360         int savesolid;
361         float movetime2, pushltime;
362         vector move, move1, moveangle, pushorig, pushang;
363         vector a;
364         vector pivot;
365         entity oldself;
366         entity check;
367
368         if(self.move_velocity == '0 0 0' && self.move_avelocity == '0 0 0')
369         {
370                 self.move_ltime += dt;
371                 return;
372         }
373
374         switch(self.solid)
375         {
376         // LordHavoc: valid pusher types
377         case SOLID_BSP:
378         case SOLID_BBOX:
379         case SOLID_SLIDEBOX:
380         case SOLID_CORPSE: // LordHavoc: this would be weird...
381                 break;
382         // LordHavoc: no collisions
383         case SOLID_NOT:
384         case SOLID_TRIGGER:
385                 self.move_origin = self.move_origin + dt * self.move_velocity;
386                 self.move_angles = self.move_angles + dt * self.move_avelocity;
387                 self.move_angles_x -= 360.0 * floor(self.move_angles_x * (1.0 / 360.0));
388                 self.move_angles_y -= 360.0 * floor(self.move_angles_y * (1.0 / 360.0));
389                 self.move_angles_z -= 360.0 * floor(self.move_angles_z * (1.0 / 360.0));
390                 self.move_ltime += dt;
391                 _Movetype_LinkEdict(true);
392                 return;
393         default:
394                 dprintf("_Movetype_PushMove: entity %e, unrecognized solid type %d\n", self, self.solid);
395                 return;
396         }
397
398         rotated = dotproduct(self.move_angles, self.move_angles) + dotproduct(self.move_avelocity, self.move_avelocity) > 0;
399
400         movetime2 = dt;
401
402         move1 = self.move_velocity * movetime2;
403         moveangle = self.move_avelocity * movetime2;
404
405         a = -moveangle;
406         // sets v_forward, v_right and v_up
407         _Movetype_AngleVectorsFLU(a);
408
409         pushorig = self.move_origin;
410         pushang = self.move_angles;
411         pushltime = self.move_ltime;
412
413 // move the pusher to its final position
414
415         self.move_origin = self.move_origin + dt * self.move_velocity;
416         self.move_angles = self.move_angles + dt * self.move_avelocity;
417         
418         self.move_ltime += dt;
419         _Movetype_LinkEdict(true);
420
421         savesolid = self.solid;
422
423         if(self.move_movetype != MOVETYPE_FAKEPUSH)
424         for(check = findradius(0.5 * (self.absmin + self.absmax), 0.5 * vlen(self.absmax - self.absmin)); check; check = check.chain)
425         {
426                 switch(check.move_movetype)
427                 {
428                 case MOVETYPE_NONE:
429                 case MOVETYPE_PUSH:
430                 case MOVETYPE_FOLLOW:
431                 case MOVETYPE_NOCLIP:
432                 case MOVETYPE_FLY_WORLDONLY:
433                         continue;
434                 default:
435                         break;
436                 }
437
438                 if(check.owner == self)
439                         continue;
440
441                 if(self.owner == check)
442                         continue;
443
444                 pivot = check.mins + 0.5 * (check.maxs - check.mins);
445                 //VectorClear(pivot);
446
447                 if (rotated)
448                 {
449                         vector org2;
450                         vector org = check.move_origin - self.move_origin;
451                         org = org + pivot;
452                         org2_x = dotproduct(org, v_forward);
453                         org2_y = dotproduct(org, v_right);
454                         org2_z = dotproduct(org, v_up);
455                         move = org2 - org;
456                         move = move + move1;
457                 }
458                 else
459                         move = move1;
460
461                 // physics objects need better collisions than this code can do
462                 if(check.move_movetype == 32) // MOVETYPE_PHYSICS
463                 {
464                         check.move_origin = check.move_origin + move;
465                         oldself = self;
466                         self = check;
467                         _Movetype_LinkEdict(true);
468                         self = oldself;
469                         continue;
470                 }
471
472                 // try moving the contacted entity
473                 self.solid = SOLID_NOT;
474                 oldself = self;
475                 self = check;
476                 if(!_Movetype_PushEntity(move, true))
477                 {
478                         self = oldself;
479                         // entity "check" got teleported
480                         check.move_angles_y += trace_fraction * moveangle_y;
481                         self.solid = savesolid;
482                         continue; // pushed enough
483                 }
484                 self = oldself;
485                 // FIXME: turn players specially
486                 check.move_angles_y += trace_fraction * moveangle_y;
487                 self.solid = savesolid;
488
489                 // this trace.fraction < 1 check causes items to fall off of pushers
490                 // if they pass under or through a wall
491                 // the groundentity check causes items to fall off of ledges
492                 if(check.move_movetype != MOVETYPE_WALK && (trace_fraction < 1 || check.move_groundentity != self))
493                         check.move_flags &= ~FL_ONGROUND;
494         }
495
496         self.move_angles_x -= 360.0 * floor(self.move_angles_x * (1.0 / 360.0));
497         self.move_angles_y -= 360.0 * floor(self.move_angles_y * (1.0 / 360.0));
498         self.move_angles_z -= 360.0 * floor(self.move_angles_z * (1.0 / 360.0));
499 }
500
501 void _Movetype_Physics_Pusher(float dt) // SV_Physics_Pusher
502 {
503         float thinktime, oldltime, movetime;
504
505         oldltime = self.move_ltime;
506
507         thinktime = self.move_nextthink;
508         if(thinktime < self.move_ltime + dt)
509         {
510                 movetime = thinktime - self.move_ltime;
511                 if(movetime < 0)
512                         movetime = 0;
513         }
514         else
515                 movetime = dt;
516
517         if(movetime)
518                 // advances self.move_ltime if not blocked
519                 _Movetype_PushMove(movetime);
520
521         if(thinktime > oldltime && thinktime <= self.move_ltime)
522         {
523                 self.move_nextthink = 0;
524                 self.move_time = time;
525                 other = world;
526                 if(self.move_think)
527                         self.move_think();
528         }
529 }
530
531 void _Movetype_Physics_Toss(float dt) // SV_Physics_Toss
532 {
533         if(self.move_flags & FL_ONGROUND)
534         {
535                 if(self.move_velocity_z >= 1/32)
536                         self.move_flags &= ~FL_ONGROUND;
537                 else if(!self.move_groundentity)
538                         return;
539                 else if(self.move_suspendedinair && wasfreed(self.move_groundentity))
540                 {
541                         self.move_groundentity = world;
542                         return;
543                 }
544         }
545
546         self.move_suspendedinair = false;
547
548         _Movetype_CheckVelocity();
549
550         if(self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS)
551         {
552                 self.move_didgravity = 1;
553                 if(GRAVITY_UNAFFECTED_BY_TICRATE)
554                 {
555                         if(self.gravity)
556                                 self.move_velocity_z -= 0.5 * dt * self.gravity * PHYS_GRAVITY;
557                         else
558                                 self.move_velocity_z -= 0.5 * dt * PHYS_GRAVITY;
559                 }
560                 else
561                 {
562                         if(self.gravity)
563                                 self.move_velocity_z -= dt * self.gravity * PHYS_GRAVITY;
564                         else
565                                 self.move_velocity_z -= dt * PHYS_GRAVITY;
566                 }
567         }
568
569         self.move_angles = self.move_angles + self.move_avelocity * dt;
570
571         float movetime, bump;
572         movetime = dt;
573         for(bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; ++bump)
574         {
575                 vector move;
576                 move = self.move_velocity * movetime;
577                 _Movetype_PushEntity(move, true);
578                 if(wasfreed(self))
579                         return;
580
581                 if(trace_startsolid)
582                 {
583                         _Movetype_UnstickEntity();
584                         _Movetype_PushEntity(move, false);
585                         if(wasfreed(self))
586                                 return;
587                 }
588
589                 if(trace_fraction == 1)
590                         break;
591
592                 movetime *= 1 - min(1, trace_fraction);
593
594                 if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
595                 {
596                         self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 2.0);
597                         self.move_flags &= ~FL_ONGROUND;
598                 }
599                 else if(self.move_movetype == MOVETYPE_BOUNCE)
600                 {
601                         float d, bouncefac, bouncestop;
602
603                         bouncefac = self.move_bounce_factor;     if(!bouncefac)  bouncefac = 0.5;
604                         bouncestop = self.move_bounce_stopspeed; if(!bouncestop) bouncestop = 60 / 800;
605                         if(self.gravity)
606                                 bouncestop *= self.gravity * PHYS_GRAVITY;
607                         else
608                                 bouncestop *= PHYS_GRAVITY;
609
610                         self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1 + bouncefac);
611
612                         d = trace_plane_normal * self.move_velocity;
613                         if(trace_plane_normal_z > 0.7 && d < bouncestop && d > -bouncestop)
614                         {
615                                 self.move_flags |= FL_ONGROUND;
616                                 self.move_groundentity = trace_ent;
617                                 self.move_velocity = '0 0 0';
618                                 self.move_avelocity = '0 0 0';
619                         }
620                         else
621                                 self.move_flags &= ~FL_ONGROUND;
622                 }
623                 else
624                 {
625                         self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1.0);
626                         if(trace_plane_normal_z > 0.7)
627                         {
628                                 self.move_flags |= FL_ONGROUND;
629                                 self.move_groundentity = trace_ent;
630                                 if(trace_ent.solid == SOLID_BSP)
631                                         self.move_suspendedinair = true;
632                                 self.move_velocity = '0 0 0';
633                                 self.move_avelocity = '0 0 0';
634                         }
635                         else
636                                 self.move_flags &= ~FL_ONGROUND;
637                 }
638
639                 // DP revision 8905 (just, WHY...)
640                 if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
641                         break;
642
643                 // DP revision 8918 (WHY...)
644                 if(self.move_flags & FL_ONGROUND)
645                         break;
646         }
647
648         if(GRAVITY_UNAFFECTED_BY_TICRATE)
649         if(self.move_didgravity > 0)
650         if(!(self.move_flags & FL_ONGROUND))
651         {
652                 if(self.gravity)
653                         self.move_velocity_z -= 0.5 * dt * self.gravity * PHYS_GRAVITY;
654                 else
655                         self.move_velocity_z -= 0.5 * dt * PHYS_GRAVITY;
656         }
657
658         _Movetype_CheckWaterTransition(self);
659 }
660
661 void _Movetype_Physics_Frame(float movedt)
662 {
663         self.move_didgravity = -1;
664         switch(self.move_movetype)
665         {
666                 case MOVETYPE_PUSH:
667                 case MOVETYPE_FAKEPUSH:
668                         _Movetype_Physics_Pusher(movedt);
669                         break;
670                 case MOVETYPE_NONE:
671                         break;
672                 case MOVETYPE_FOLLOW:
673                         error("SV_Physics_Follow not implemented");
674                         break;
675                 case MOVETYPE_NOCLIP:
676                         _Movetype_CheckWater(self);
677                         self.move_origin = self.move_origin + TICRATE * self.move_velocity;
678                         self.move_angles = self.move_angles + TICRATE * self.move_avelocity;
679                         _Movetype_LinkEdict(false);
680                         break;
681                 case MOVETYPE_STEP:
682                         error("SV_Physics_Step not implemented");
683                         break;
684                 case MOVETYPE_WALK:
685                         error("SV_Physics_Walk not implemented");
686                         break;
687                 case MOVETYPE_TOSS:
688                 case MOVETYPE_BOUNCE:
689                 case MOVETYPE_BOUNCEMISSILE:
690                 case MOVETYPE_FLYMISSILE:
691                 case MOVETYPE_FLY:
692                         _Movetype_Physics_Toss(movedt);
693                         break;
694         }
695 }
696
697 void Movetype_Physics_NoMatchServer() // optimized
698 {
699         float movedt;
700
701         movedt = time - self.move_time;
702         self.move_time = time;
703
704         _Movetype_Physics_Frame(movedt);
705         if(wasfreed(self))
706                 return;
707
708         self.avelocity = self.move_avelocity;
709         self.velocity = self.move_velocity;
710         self.angles = self.move_angles;
711         setorigin(self, self.move_origin);
712 }
713
714 void Movetype_Physics_MatchServer(bool sloppy)
715 {
716         Movetype_Physics_MatchTicrate(TICRATE, sloppy);
717 }
718
719 void Movetype_Physics_MatchTicrate(float tr, bool sloppy) // SV_Physics_Entity
720 {
721         float n, i, dt, movedt;
722
723         if(tr <= 0)
724         {
725                 Movetype_Physics_NoMatchServer();
726                 return;
727         }
728
729         dt = time - self.move_time;
730
731         movedt = tr;
732         n = max(0, floor(dt / tr));
733         dt -= n * tr;
734         self.move_time += n * tr;
735
736         if(!self.move_didgravity)
737                 self.move_didgravity = ((self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS) && !(self.move_flags & FL_ONGROUND));
738
739         for(i = 0; i < n; ++i)
740         {
741                 _Movetype_Physics_Frame(movedt);
742                 if(wasfreed(self))
743                         return;
744         }
745
746         self.avelocity = self.move_avelocity;
747
748         if(dt > 0 && self.move_movetype != MOVETYPE_NONE && !(self.move_flags & FL_ONGROUND))
749         {
750                 // now continue the move from move_time to time
751                 self.velocity = self.move_velocity;
752
753                 if(self.move_didgravity > 0)
754                 {
755                         if(GRAVITY_UNAFFECTED_BY_TICRATE)
756                         {
757                                 if(self.gravity)
758                                         self.velocity_z -= 0.5 * dt * self.gravity * PHYS_GRAVITY;
759                                 else
760                                         self.velocity_z -= 0.5 * dt * PHYS_GRAVITY;
761                         }
762                         else
763                         {
764                                 if(self.gravity)
765                                         self.velocity_z -= dt * self.gravity * PHYS_GRAVITY;
766                                 else
767                                         self.velocity_z -= dt * PHYS_GRAVITY;
768                         }
769                 }
770
771                 self.angles = self.move_angles + dt * self.avelocity;
772
773                 if(sloppy || self.movetype == MOVETYPE_NOCLIP)
774                 {
775                         setorigin(self, self.move_origin + dt * self.velocity);
776                 }
777                 else
778                 {
779                         _Movetype_PushEntityTrace(dt * self.velocity);
780                         if(!trace_startsolid)
781                                 setorigin(self, trace_endpos);
782                 }
783
784                 if(self.move_didgravity > 0)
785                 {
786                         if(GRAVITY_UNAFFECTED_BY_TICRATE)
787                         {
788                                 if(self.gravity)
789                                         self.velocity_z -= 0.5 * dt * self.gravity * PHYS_GRAVITY;
790                                 else
791                                         self.velocity_z -= 0.5 * dt * PHYS_GRAVITY;
792                         }
793                 }
794         }
795         else
796         {
797                 self.velocity = self.move_velocity;
798                 self.angles = self.move_angles;
799                 setorigin(self, self.move_origin);
800         }
801 }