]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/physics/movetypes/movetypes.qc
Merge branch 'master' into Mario/qc_camstuff
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / physics / movetypes / movetypes.qc
1 #include "movetypes.qh"
2
3 #ifdef SVQC
4 void set_movetype(entity this, int mt)
5 {
6         this.move_movetype = mt;
7         if (mt == MOVETYPE_PHYSICS || mt == MOVETYPE_PUSH || mt == MOVETYPE_FAKEPUSH) {
8                 this.move_qcphysics = false;
9         }
10         this.movetype = (this.move_qcphysics) ? MOVETYPE_NONE : mt;
11 }
12 #elif defined(CSQC)
13 void set_movetype(entity this, int mt)
14 {
15         this.move_movetype = mt;
16 }
17 #endif
18
19 void _Movetype_WallFriction(entity this, vector stepnormal)  // SV_WallFriction
20 {
21         /*float d, i;
22         vector into, side;
23         makevectors(this.v_angle);
24         d = (stepnormal * v_forward) + 0.5;
25
26         if(d < 0)
27         {
28             i = (stepnormal * this.velocity);
29             into = i * stepnormal;
30             side = this.velocity - into;
31             this.velocity_x = side.x * (1 * d);
32             this.velocity_y = side.y * (1 * d);
33         }*/
34 }
35
36 vector planes[MAX_CLIP_PLANES];
37 int _Movetype_FlyMove(entity this, float dt, bool applygravity, bool applystepnormal, float stepheight) // SV_FlyMove
38 {
39         move_stepnormal = '0 0 0';
40
41         if(dt <= 0)
42                 return 0;
43
44         int blocked = 0;
45         int i, j, numplanes = 0;
46         float time_left = dt, grav = 0;
47         vector push;
48         vector primal_velocity, original_velocity;
49         vector restore_velocity = this.velocity;
50
51         for(i = 0; i < MAX_CLIP_PLANES; ++i)
52                 planes[i] = '0 0 0';
53
54         if(applygravity)
55         {
56                 this.move_didgravity = 1;
57                 grav = dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this);
58
59                 if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !IS_ONGROUND(this))
60                 {
61                         if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
62                                 this.velocity_z -= grav * 0.5;
63                         else
64                                 this.velocity_z -= grav;
65                 }
66         }
67
68         original_velocity = primal_velocity = this.velocity;
69
70         for(int bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
71         {
72                 if(this.velocity == '0 0 0')
73                         break;
74
75                 push = this.velocity * time_left;
76                 if(!_Movetype_PushEntity(this, push, true, false))
77                 {
78                         // we got teleported by a touch function
79                         // let's abort the move
80                         blocked |= 8;
81                         break;
82                 }
83
84                 // this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity
85                 // abort move if we're stuck in the world (and didn't make it out)
86                 if(trace_startsolid && trace_allsolid)
87                 {
88                         this.velocity = restore_velocity;
89                         return 3;
90                 }
91
92                 if(trace_fraction == 1)
93                         break;
94
95                 float my_trace_fraction = trace_fraction;
96                 vector my_trace_plane_normal = trace_plane_normal;
97
98                 if(trace_plane_normal.z)
99                 {
100                         if(trace_plane_normal.z > 0.7)
101                         {
102                                 // floor
103                                 blocked |= 1;
104
105                                 if(!trace_ent)
106                                 {
107                                         //dprint("_Movetype_FlyMove: !trace_ent\n");
108                                         trace_ent = NULL;
109                                 }
110
111                                 SET_ONGROUND(this);
112                                 this.groundentity = trace_ent;
113                         }
114                 }
115                 else if(stepheight)
116                 {
117                         // step - handle it immediately
118                         vector org = this.origin;
119                         vector steppush = '0 0 1' * stepheight;
120
121                         if(!_Movetype_PushEntity(this, steppush, true, false))
122                         {
123                                 blocked |= 8;
124                                 break;
125                         }
126                         if(!_Movetype_PushEntity(this, push, true, false))
127                         {
128                                 blocked |= 8;
129                                 break;
130                         }
131                         float trace2_fraction = trace_fraction;
132                         steppush = vec3(0, 0, org.z - this.origin_z);
133                         if(!_Movetype_PushEntity(this, steppush, true, false))
134                         {
135                                 blocked |= 8;
136                                 break;
137                         }
138
139                         // accept the new position if it made some progress...
140                         if(fabs(this.origin_x - org.x) >= 0.03125 || fabs(this.origin_y - org.y) >= 0.03125)
141                         {
142                                 trace_endpos = this.origin;
143                                 time_left *= 1 - trace2_fraction;
144                                 numplanes = 0;
145                                 continue;
146                         }
147                         else
148                                 this.origin = org;
149                 }
150                 else
151                 {
152                         // step - return it to caller
153                         blocked |= 2;
154                         // save the trace for player extrafriction
155                         if(applystepnormal)
156                                 move_stepnormal = trace_plane_normal;
157                 }
158
159                 if(my_trace_fraction >= 0.001)
160                 {
161                         // actually covered some distance
162                         original_velocity = this.velocity;
163                         numplanes = 0;
164                 }
165
166                 time_left *= 1 - my_trace_fraction;
167
168                 // clipped to another plane
169                 if(numplanes >= MAX_CLIP_PLANES)
170                 {
171                         // this shouldn't really happen
172                         this.velocity = '0 0 0';
173                         blocked = 3;
174                         break;
175                 }
176
177                 planes[numplanes] = my_trace_plane_normal;
178                 numplanes++;
179
180                 // modify original_velocity so it parallels all of the clip planes
181                 vector new_velocity = '0 0 0';
182                 for (i = 0;i < numplanes;i++)
183                 {
184                         new_velocity = _Movetype_ClipVelocity(original_velocity, planes[i], 1);
185                         for (j = 0;j < numplanes;j++)
186                         {
187                                 if(j != i)
188                                 {
189                                         // not ok
190                                         if((new_velocity * planes[j]) < 0)
191                                                 break;
192                                 }
193                         }
194                         if(j == numplanes)
195                                 break;
196                 }
197
198                 if(i != numplanes)
199                 {
200                         // go along this plane
201                         this.velocity = new_velocity;
202                 }
203                 else
204                 {
205                         // go along the crease
206                         if(numplanes != 2)
207                         {
208                                 this.velocity = '0 0 0';
209                                 blocked = 7;
210                                 break;
211                         }
212                         vector dir = cross(planes[0], planes[1]);
213                         // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
214                         float ilength = sqrt((dir * dir));
215                         if(ilength)
216                                 ilength = 1.0 / ilength;
217                         dir.x *= ilength;
218                         dir.y *= ilength;
219                         dir.z *= ilength;
220                         float d = (dir * this.velocity);
221                         this.velocity = dir * d;
222                 }
223
224                 // if current velocity is against the original velocity,
225                 // stop dead to avoid tiny occilations in sloping corners
226                 if((this.velocity * primal_velocity) <= 0)
227                 {
228                         this.velocity = '0 0 0';
229                         break;
230                 }
231         }
232
233         // LordHavoc: this came from QW and allows you to get out of water more easily
234         if(GAMEPLAYFIX_EASIERWATERJUMP(this) && (this.flags & FL_WATERJUMP) && !(blocked & 8))
235                 this.velocity = primal_velocity;
236
237         if(PHYS_WALLCLIP(this) && this.pm_time && !(this.flags & FL_WATERJUMP) && !(blocked & 8))
238                 this.velocity = primal_velocity;
239
240         if(applygravity)
241         {
242                 if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !IS_ONGROUND(this))
243                 {
244                         if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
245                                 this.velocity_z -= grav * 0.5f;
246                 }
247         }
248
249         return blocked;
250 }
251
252 void _Movetype_CheckVelocity(entity this)  // SV_CheckVelocity
253 {
254         // if(vlen(this.velocity) < 0.0001)
255         // this.velocity = '0 0 0';
256 }
257
258 bool _Movetype_CheckWater(entity this)  // SV_CheckWater
259 {
260         vector point = this.origin;
261         point.z += this.mins.z + 1;
262
263         int nativecontents = pointcontents(point);
264         if(this.watertype && this.watertype != nativecontents)
265         {
266                 // dprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", this.watertype, nativecontents);
267                 if(this.contentstransition)
268                         this.contentstransition(this.watertype, nativecontents);
269         }
270
271         this.waterlevel = WATERLEVEL_NONE;
272         this.watertype = CONTENT_EMPTY;
273
274         int supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents);
275         if(supercontents & DPCONTENTS_LIQUIDSMASK)
276         {
277                 this.watertype = nativecontents;
278                 this.waterlevel = WATERLEVEL_WETFEET;
279                 point.z = this.origin.z + (this.mins.z + this.maxs.z) * 0.5;
280                 if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
281                 {
282                         this.waterlevel = WATERLEVEL_SWIMMING;
283                         point.z = this.origin.z + this.view_ofs.z;
284                         if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
285                                 this.waterlevel = WATERLEVEL_SUBMERGED;
286                 }
287         }
288
289         return this.waterlevel > 1;
290 }
291
292 void _Movetype_CheckWaterTransition(entity ent)  // SV_CheckWaterTransition
293 {
294         int contents = pointcontents(ent.origin);
295
296         if(!ent.watertype)
297         {
298                 // just spawned here
299                 if(!GAMEPLAYFIX_WATERTRANSITION(ent))
300                 {
301                         ent.watertype = contents;
302                         ent.waterlevel = 1;
303                         return;
304                 }
305         }
306         else if(ent.watertype != contents)
307         {
308                 // dprintf("_Movetype_CheckWaterTransition(): Origin: %s, Direct: '%d', Original: '%d', New: '%d'\n", vtos(ent.origin), pointcontents(ent.origin), ent.watertype, contents);
309                 if(ent.contentstransition)
310                         ent.contentstransition(ent.watertype, contents);
311         }
312
313         if(contents <= CONTENT_WATER)
314         {
315                 ent.watertype = contents;
316                 ent.waterlevel = 1;
317         }
318         else
319         {
320                 ent.watertype = CONTENT_EMPTY;
321                 ent.waterlevel = (GAMEPLAYFIX_WATERTRANSITION(ent) ? 0 : contents);
322         }
323 }
324
325 void _Movetype_Impact(entity this, entity oth)  // SV_Impact
326 {
327         if(!this && !oth)
328                 return;
329
330         if(this.solid != SOLID_NOT && gettouch(this))
331                 gettouch(this)(this, oth);
332
333         if(oth.solid != SOLID_NOT && gettouch(oth))
334         {
335                 trace_endpos = oth.origin;
336                 trace_plane_normal = -trace_plane_normal;
337                 trace_plane_dist = -trace_plane_dist;
338                 trace_ent = this;
339                 trace_dpstartcontents = 0;
340                 trace_dphitcontents = 0;
341                 trace_dphitq3surfaceflags = 0;
342                 trace_dphittexturename = string_null;
343                 gettouch(oth)(oth, this);
344         }
345 }
346
347 void _Movetype_LinkEdict_TouchAreaGrid(entity this)  // SV_LinkEdict_TouchAreaGrid
348 {
349         if(this.solid == SOLID_NOT)
350                 return;
351
352     FOREACH_ENTITY_RADIUS(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin), true, {
353                 if (it.solid == SOLID_TRIGGER && it != this)
354                 if (it.move_nomonsters != MOVE_NOMONSTERS && it.move_nomonsters != MOVE_WORLDONLY)
355                 if (gettouch(it) && boxesoverlap(it.absmin, it.absmax, this.absmin, this.absmax))
356                 {
357                         trace_allsolid = false;
358                         trace_startsolid = false;
359                         trace_fraction = 1;
360                         trace_inwater = false;
361                         trace_inopen = true;
362                         trace_endpos = it.origin;
363                         trace_plane_normal = '0 0 1';
364                         trace_plane_dist = 0;
365                         trace_ent = this;
366                         trace_dpstartcontents = 0;
367                         trace_dphitcontents = 0;
368                         trace_dphitq3surfaceflags = 0;
369                         trace_dphittexturename = string_null;
370
371                         gettouch(it)(it, this);
372                 }
373     });
374 }
375
376 bool autocvar__movetype_debug = false;
377 void _Movetype_LinkEdict(entity this, bool touch_triggers)  // SV_LinkEdict
378 {
379         if(autocvar__movetype_debug)
380         {
381                 vector mi, ma;
382                 if(this.solid == SOLID_BSP)
383                 {
384                         // TODO set the absolute bbox
385                         mi = this.mins;
386                         ma = this.maxs;
387                 }
388                 else
389                 {
390                         mi = this.mins;
391                         ma = this.maxs;
392                 }
393                 mi += this.origin;
394                 ma += this.origin;
395
396                 if(this.flags & FL_ITEM)
397                 {
398                         mi -= '15 15 1';
399                         ma += '15 15 1';
400                 }
401                 else
402                 {
403                         mi -= '1 1 1';
404                         ma += '1 1 1';
405                 }
406
407                 this.absmin = mi;
408                 this.absmax = ma;
409         }
410         else
411         {
412                 setorigin(this, this.origin); // calls SV_LinkEdict
413         #ifdef CSQC
414                 // NOTE: CSQC's version of setorigin doesn't expand
415                 this.absmin -= '1 1 1';
416                 this.absmax += '1 1 1';
417         #endif
418         }
419
420         if(touch_triggers)
421                 _Movetype_LinkEdict_TouchAreaGrid(this);
422 }
423
424 entity _Movetype_TestEntityPosition_ent;
425 bool _Movetype_TestEntityPosition(vector ofs)  // SV_TestEntityPosition
426 {
427     entity this = _Movetype_TestEntityPosition_ent;
428         vector org = this.origin + ofs;
429
430         int cont = this.dphitcontentsmask;
431         this.dphitcontentsmask = DPCONTENTS_SOLID;
432         tracebox(org, this.mins, this.maxs, org, ((this.move_movetype == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), this);
433         this.dphitcontentsmask = cont;
434
435         if(trace_startsolid)
436                 return true;
437
438         if(vdist(trace_endpos - this.origin, >, 0.0001))
439                 this.origin = trace_endpos;
440         return false;
441 }
442
443 int _Movetype_UnstickEntity(entity this)  // SV_UnstickEntity
444 {
445     _Movetype_TestEntityPosition_ent = this;
446         if (!_Movetype_TestEntityPosition(' 0  0  0')) {
447             return UNSTICK_FINE;
448         }
449         #define X(v) if (_Movetype_TestEntityPosition(v))
450         X('-1  0  0') X(' 1  0  0')
451         X(' 0 -1  0') X(' 0  1  0')
452         X('-1 -1  0') X(' 1 -1  0')
453         X('-1  1  0') X(' 1  1  0')
454         #undef X
455         {
456         #define X(i) \
457             if (_Movetype_TestEntityPosition('0 0 -1' * i)) \
458             if (_Movetype_TestEntityPosition('0 0 1' * i))
459         X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8)
460         X(9) X(10) X(11) X(12) X(13) X(14) X(15) X(16)
461         X(17)
462         #undef X
463         {
464             LOG_DEBUGF("Can't unstick an entity (edict: %d, classname: %s, origin: %s)",
465                 etof(this), this.classname, vtos(this.origin));
466             return UNSTICK_STUCK;
467         }
468         }
469         LOG_DEBUGF("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)",
470                 etof(this), this.classname, vtos(this.origin));
471         _Movetype_LinkEdict(this, true);
472         return UNSTICK_FIXED;
473 }
474
475 void _Movetype_CheckStuck(entity this)  // SV_CheckStuck
476 {
477         int unstick = _Movetype_UnstickEntity(this); // sets test position entity
478         switch(unstick)
479         {
480                 case UNSTICK_FINE:
481                         this.oldorigin = this.origin;
482                         break;
483                 case UNSTICK_FIXED:
484                         break; // already sorted
485                 case UNSTICK_STUCK:
486                         vector offset = this.oldorigin - this.origin;
487                         if(!_Movetype_TestEntityPosition(offset))
488                                 _Movetype_LinkEdict(this, false);
489                         // couldn't unstick, should we warn about this?
490                         break;
491         }
492 }
493
494 vector _Movetype_ClipVelocity(vector vel, vector norm, float f)  // SV_ClipVelocity
495 {
496         vel -= ((vel * norm) * norm) * f;
497
498         if(vel.x > -0.1 && vel.x < 0.1) vel.x = 0;
499         if(vel.y > -0.1 && vel.y < 0.1) vel.y = 0;
500         if(vel.z > -0.1 && vel.z < 0.1) vel.z = 0;
501
502         return vel;
503 }
504
505 void _Movetype_PushEntityTrace(entity this, vector push)
506 {
507         vector end = this.origin + push;
508         int type;
509         if(this.move_nomonsters)
510                 type = max(0, this.move_nomonsters);
511         else if(this.move_movetype == MOVETYPE_FLYMISSILE)
512                 type = MOVE_MISSILE;
513         else if(this.move_movetype == MOVETYPE_FLY_WORLDONLY)
514                 type = MOVE_WORLDONLY;
515         else if(this.solid == SOLID_TRIGGER || this.solid == SOLID_NOT)
516                 type = MOVE_NOMONSTERS;
517         else
518                 type = MOVE_NORMAL;
519
520         tracebox(this.origin, this.mins, this.maxs, end, type, this);
521 }
522
523 bool _Movetype_PushEntity(entity this, vector push, bool failonstartsolid, bool dolink)  // SV_PushEntity
524 {
525         _Movetype_PushEntityTrace(this, push);
526
527         // NOTE: this is a workaround for the QC's lack of a worldstartsolid trace parameter
528         if(trace_startsolid && failonstartsolid)
529         {
530                 int oldtype = this.move_nomonsters;
531                 this.move_nomonsters = MOVE_WORLDONLY;
532                 _Movetype_PushEntityTrace(this, push);
533                 this.move_nomonsters = oldtype;
534                 if(trace_startsolid)
535                         return true;
536         }
537
538         this.origin = trace_endpos;
539
540         vector last_origin = this.origin;
541
542         _Movetype_LinkEdict(this, dolink);
543
544         if((this.solid >= SOLID_TRIGGER && trace_fraction < 1 && (!IS_ONGROUND(this) || this.groundentity != trace_ent)))
545                 _Movetype_Impact(this, trace_ent);
546
547         return (this.origin == last_origin); // false if teleported by touch
548 }
549
550
551 .float ltime;
552 .void() blocked;
553
554 void _Movetype_Physics_Frame(entity this, float movedt)
555 {
556         this.move_didgravity = -1;
557         switch (this.move_movetype)
558         {
559                 case MOVETYPE_PUSH:
560                 case MOVETYPE_FAKEPUSH:
561                         LOG_DEBUG("Physics: Lacking QuakeC support for Push movetype, FIX ME by using engine physics!");
562                         break;
563                 case MOVETYPE_NONE:
564                         break;
565                 case MOVETYPE_FOLLOW:
566                         _Movetype_Physics_Follow(this);
567                         break;
568                 case MOVETYPE_NOCLIP:
569                         _Movetype_CheckWater(this);
570                         this.origin = this.origin + movedt * this.velocity;
571                         this.angles = this.angles + movedt * this.avelocity;
572                         _Movetype_LinkEdict(this, false);
573                         break;
574                 case MOVETYPE_STEP:
575                         _Movetype_Physics_Step(this, movedt);
576                         break;
577                 case MOVETYPE_WALK:
578                         _Movetype_Physics_Walk(this, movedt);
579                         break;
580                 case MOVETYPE_TOSS:
581                 case MOVETYPE_BOUNCE:
582                 case MOVETYPE_BOUNCEMISSILE:
583                 case MOVETYPE_FLYMISSILE:
584                 case MOVETYPE_FLY:
585                 case MOVETYPE_FLY_WORLDONLY:
586                         _Movetype_Physics_Toss(this, movedt);
587                         if(wasfreed(this))
588                                 return;
589                         _Movetype_LinkEdict(this, true);
590                         break;
591                 case MOVETYPE_PHYSICS:
592                         break;
593         }
594 }
595
596 void _Movetype_Physics_ClientFrame(entity this, float movedt)
597 {
598         this.move_didgravity = -1;
599         switch (this.move_movetype)
600         {
601                 case MOVETYPE_PUSH:
602                 case MOVETYPE_FAKEPUSH:
603                         LOG_DEBUG("Physics: Lacking QuakeC support for Push movetype, FIX ME by using engine physics!");
604                         break;
605                 case MOVETYPE_NONE:
606                         break;
607                 case MOVETYPE_FOLLOW:
608                         _Movetype_Physics_Follow(this);
609                         break;
610                 case MOVETYPE_NOCLIP:
611                         _Movetype_CheckWater(this);
612                         this.origin = this.origin + movedt * this.velocity;
613                         this.angles = this.angles + movedt * this.avelocity;
614                         break;
615                 case MOVETYPE_STEP:
616                         _Movetype_Physics_Step(this, movedt);
617                         break;
618                 case MOVETYPE_WALK:
619                 case MOVETYPE_FLY:
620                 case MOVETYPE_FLY_WORLDONLY:
621                         _Movetype_Physics_Walk(this, movedt);
622                         break;
623                 case MOVETYPE_TOSS:
624                 case MOVETYPE_BOUNCE:
625                 case MOVETYPE_BOUNCEMISSILE:
626                 case MOVETYPE_FLYMISSILE:
627                         _Movetype_Physics_Toss(this, movedt);
628                         break;
629                 case MOVETYPE_PHYSICS:
630                         break;
631         }
632
633         //_Movetype_CheckVelocity(this);
634
635         _Movetype_LinkEdict(this, true);
636
637         //_Movetype_CheckVelocity(this);
638 }
639
640 void Movetype_Physics_NoMatchTicrate(entity this, float movedt, bool isclient)  // to be run every move frame
641 {
642         this.move_time = time;
643
644         if(isclient)
645                 _Movetype_Physics_ClientFrame(this, movedt);
646         else
647                 _Movetype_Physics_Frame(this, movedt);
648         if(wasfreed(this))
649                 return;
650
651         setorigin(this, this.origin);
652 }
653
654 void Movetype_Physics_NoMatchServer(entity this)  // optimized
655 {
656         float movedt = time - this.move_time;
657         this.move_time = time;
658
659         _Movetype_Physics_Frame(this, movedt);
660         if(wasfreed(this))
661                 return;
662
663         setorigin(this, this.origin);
664 }
665
666 void Movetype_Physics_MatchServer(entity this, bool sloppy)
667 {
668         Movetype_Physics_MatchTicrate(this, TICRATE, sloppy);
669 }
670
671 // saved .move_*
672 .vector tic_origin;
673 .vector tic_velocity;
674 .int tic_flags;
675 .vector tic_avelocity;
676 .vector tic_angles;
677
678 // saved .*
679 .vector tic_saved_origin;
680 .vector tic_saved_velocity;
681 .int tic_saved_flags;
682 .vector tic_saved_avelocity;
683 .vector tic_saved_angles;
684 void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy)  // SV_Physics_Entity
685 {
686         // this hack exists to contain the physics feature
687         // (so entities can place themselves in the world and not need to update .tic_* themselves)
688 #define X(s) \
689         if(this.(s) != this.tic_saved_##s) \
690                 this.tic_##s = this.(s)
691
692         X(origin);
693         X(velocity);
694         X(flags);
695         X(avelocity);
696         X(angles);
697 #undef X
698
699         this.flags = this.tic_flags;
700         this.velocity = this.tic_velocity;
701         setorigin(this, this.tic_origin);
702         this.avelocity = this.tic_avelocity;
703         this.angles = this.tic_angles;
704
705         if(tr <= 0)
706         {
707                 Movetype_Physics_NoMatchServer(this);
708
709                 this.tic_saved_flags = this.flags;
710                 this.tic_saved_velocity = this.velocity;
711                 this.tic_saved_origin = this.origin;
712                 this.tic_saved_avelocity = this.avelocity;
713                 this.tic_saved_angles = this.angles;
714                 return;
715         }
716
717         float dt = time - this.move_time;
718
719         int n = max(0, floor(dt / tr));
720         dt -= n * tr;
721         this.move_time += n * tr;
722
723         if(!this.move_didgravity)
724                 this.move_didgravity = ((this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS) && !(this.tic_flags & FL_ONGROUND));
725
726         for (int j = 0; j < n; ++j)
727         {
728                 _Movetype_Physics_Frame(this, tr);
729                 if(wasfreed(this))
730                         return;
731         }
732
733         // update the physics fields
734         this.tic_origin = this.origin;
735         this.tic_velocity = this.velocity;
736         this.tic_avelocity = this.avelocity;
737         this.tic_angles = this.angles;
738         this.tic_flags = this.flags;
739
740         // restore their actual values
741         this.flags = this.tic_saved_flags;
742         this.velocity = this.tic_saved_velocity;
743         setorigin(this, this.tic_saved_origin);
744         //this.avelocity = this.tic_saved_avelocity;
745         this.angles = this.tic_saved_angles;
746
747         this.avelocity = this.tic_avelocity;
748
749         if(dt > 0 && this.move_movetype != MOVETYPE_NONE && !(this.tic_flags & FL_ONGROUND))
750         {
751                 // now continue the move from move_time to time
752                 this.velocity = this.tic_velocity;
753
754                 if(this.move_didgravity > 0)
755                 {
756                         this.velocity_z -= (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1)
757                             * dt
758                             * ((this.gravity) ? this.gravity : 1)
759                             * PHYS_GRAVITY(this);
760                 }
761
762                 this.angles = this.tic_angles + dt * this.avelocity;
763
764                 if(sloppy || this.move_movetype == MOVETYPE_NOCLIP)
765                 {
766                         setorigin(this, this.tic_origin + dt * this.velocity);
767                 }
768                 else
769                 {
770                         setorigin(this, this.tic_origin);
771                         _Movetype_PushEntityTrace(this, dt * this.velocity);
772                         if(!trace_startsolid)
773                                 setorigin(this, trace_endpos);
774                         else
775                                 setorigin(this, this.tic_saved_origin);
776                 }
777
778                 if(this.move_didgravity > 0 && GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
779                         this.velocity_z -= 0.5 * dt * ((this.gravity) ? this.gravity : 1) * PHYS_GRAVITY(this);
780         }
781         else
782         {
783                 this.velocity = this.tic_velocity;
784                 this.angles = this.tic_angles;
785                 setorigin(this, this.tic_origin);
786         }
787
788         this.flags = this.tic_flags;
789
790         this.tic_saved_flags = this.flags;
791         this.tic_saved_velocity = this.velocity;
792         this.tic_saved_origin = this.origin;
793         this.tic_saved_avelocity = this.avelocity;
794         this.tic_saved_angles = this.angles;
795 }