]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/physics/movetypes/movetypes.qc
Merge branch 'martin-t/AMMO' into 'master'
[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 * (PHYS_ENTGRAVITY(this) ? PHYS_ENTGRAVITY(this) : 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(gettouch(this))
328                 gettouch(this)(this, oth);
329
330         if(gettouch(oth))
331                 gettouch(oth)(oth, this);
332 }
333
334 void _Movetype_LinkEdict_TouchAreaGrid(entity this)  // SV_LinkEdict_TouchAreaGrid
335 {
336         if(this.solid == SOLID_NOT)
337                 return;
338
339     FOREACH_ENTITY_RADIUS(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin), true, {
340                 if (it.solid == SOLID_TRIGGER && it != this)
341                 if (it.move_nomonsters != MOVE_NOMONSTERS && it.move_nomonsters != MOVE_WORLDONLY)
342                 if (gettouch(it) && boxesoverlap(it.absmin, it.absmax, this.absmin, this.absmax))
343                 {
344                         trace_allsolid = false;
345                         trace_startsolid = false;
346                         trace_fraction = 1;
347                         trace_inwater = false;
348                         trace_inopen = true;
349                         trace_endpos = it.origin;
350                         trace_plane_normal = '0 0 1';
351                         trace_plane_dist = 0;
352                         trace_ent = this;
353                         trace_dpstartcontents = 0;
354                         trace_dphitcontents = 0;
355                         trace_dphitq3surfaceflags = 0;
356                         trace_dphittexturename = string_null;
357
358                         gettouch(it)(it, this);
359                 }
360     });
361 }
362
363 bool autocvar__movetype_debug = false;
364 void _Movetype_LinkEdict(entity this, bool touch_triggers)  // SV_LinkEdict
365 {
366         if(autocvar__movetype_debug)
367         {
368                 vector mi, ma;
369                 if(this.solid == SOLID_BSP)
370                 {
371                         // TODO set the absolute bbox
372                         mi = this.mins;
373                         ma = this.maxs;
374                 }
375                 else
376                 {
377                         mi = this.mins;
378                         ma = this.maxs;
379                 }
380                 mi += this.origin;
381                 ma += this.origin;
382
383                 if(this.flags & FL_ITEM)
384                 {
385                         mi -= '15 15 1';
386                         ma += '15 15 1';
387                 }
388                 else
389                 {
390                         mi -= '1 1 1';
391                         ma += '1 1 1';
392                 }
393
394                 this.absmin = mi;
395                 this.absmax = ma;
396         }
397         else
398         {
399                 setorigin(this, this.origin); // calls SV_LinkEdict
400         #ifdef CSQC
401                 // NOTE: CSQC's version of setorigin doesn't expand
402                 this.absmin -= '1 1 1';
403                 this.absmax += '1 1 1';
404         #endif
405         }
406
407         if(touch_triggers)
408                 _Movetype_LinkEdict_TouchAreaGrid(this);
409 }
410
411 entity _Movetype_TestEntityPosition_ent;
412 bool _Movetype_TestEntityPosition(vector ofs)  // SV_TestEntityPosition
413 {
414     entity this = _Movetype_TestEntityPosition_ent;
415         vector org = this.origin + ofs;
416
417         int cont = this.dphitcontentsmask;
418         this.dphitcontentsmask = DPCONTENTS_SOLID;
419         tracebox(org, this.mins, this.maxs, org, ((this.move_movetype == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), this);
420         this.dphitcontentsmask = cont;
421
422         if(trace_startsolid)
423                 return true;
424
425         if(vdist(trace_endpos - this.origin, >, 0.0001))
426                 this.origin = trace_endpos;
427         return false;
428 }
429
430 int _Movetype_UnstickEntity(entity this)  // SV_UnstickEntity
431 {
432     _Movetype_TestEntityPosition_ent = this;
433         if (!_Movetype_TestEntityPosition(' 0  0  0')) {
434             return UNSTICK_FINE;
435         }
436         #define X(v) if (_Movetype_TestEntityPosition(v))
437         X('-1  0  0') X(' 1  0  0')
438         X(' 0 -1  0') X(' 0  1  0')
439         X('-1 -1  0') X(' 1 -1  0')
440         X('-1  1  0') X(' 1  1  0')
441         #undef X
442         {
443         #define X(i) \
444             if (_Movetype_TestEntityPosition('0 0 -1' * i)) \
445             if (_Movetype_TestEntityPosition('0 0 1' * i))
446         X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8)
447         X(9) X(10) X(11) X(12) X(13) X(14) X(15) X(16)
448         X(17)
449         #undef X
450         {
451             LOG_DEBUGF("Can't unstick an entity (edict: %d, classname: %s, origin: %s)",
452                 etof(this), this.classname, vtos(this.origin));
453             return UNSTICK_STUCK;
454         }
455         }
456         LOG_DEBUGF("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)",
457                 etof(this), this.classname, vtos(this.origin));
458         _Movetype_LinkEdict(this, true);
459         return UNSTICK_FIXED;
460 }
461
462 void _Movetype_CheckStuck(entity this)  // SV_CheckStuck
463 {
464         int unstick = _Movetype_UnstickEntity(this); // sets test position entity
465         switch(unstick)
466         {
467                 case UNSTICK_FINE:
468                         this.oldorigin = this.origin;
469                         break;
470                 case UNSTICK_FIXED:
471                         break; // already sorted
472                 case UNSTICK_STUCK:
473                         vector offset = this.oldorigin - this.origin;
474                         if(!_Movetype_TestEntityPosition(offset))
475                                 _Movetype_LinkEdict(this, false);
476                         // couldn't unstick, should we warn about this?
477                         break;
478         }
479 }
480
481 vector _Movetype_ClipVelocity(vector vel, vector norm, float f)  // SV_ClipVelocity
482 {
483         vel -= ((vel * norm) * norm) * f;
484
485         if(vel.x > -0.1 && vel.x < 0.1) vel.x = 0;
486         if(vel.y > -0.1 && vel.y < 0.1) vel.y = 0;
487         if(vel.z > -0.1 && vel.z < 0.1) vel.z = 0;
488
489         return vel;
490 }
491
492 void _Movetype_PushEntityTrace(entity this, vector push)
493 {
494         vector end = this.origin + push;
495         int type;
496         if(this.move_nomonsters)
497                 type = max(0, this.move_nomonsters);
498         else if(this.move_movetype == MOVETYPE_FLYMISSILE)
499                 type = MOVE_MISSILE;
500         else if(this.move_movetype == MOVETYPE_FLY_WORLDONLY)
501                 type = MOVE_WORLDONLY;
502         else if(this.solid == SOLID_TRIGGER || this.solid == SOLID_NOT)
503                 type = MOVE_NOMONSTERS;
504         else
505                 type = MOVE_NORMAL;
506
507         tracebox(this.origin, this.mins, this.maxs, end, type, this);
508 }
509
510 bool _Movetype_PushEntity(entity this, vector push, bool failonstartsolid, bool dolink)  // SV_PushEntity
511 {
512         _Movetype_PushEntityTrace(this, push);
513
514         // NOTE: this is a workaround for the QC's lack of a worldstartsolid trace parameter
515         if(trace_startsolid && failonstartsolid)
516         {
517                 int oldtype = this.move_nomonsters;
518                 this.move_nomonsters = MOVE_WORLDONLY;
519                 _Movetype_PushEntityTrace(this, push);
520                 this.move_nomonsters = oldtype;
521                 if(trace_startsolid)
522                         return true;
523         }
524
525         this.origin = trace_endpos;
526
527         vector last_origin = this.origin;
528
529         if(dolink)
530                 _Movetype_LinkEdict(this, true);
531
532         if(trace_fraction < 1)
533                 if(this.solid >= SOLID_TRIGGER && trace_ent && (!IS_ONGROUND(this) || (this.groundentity != trace_ent)))
534                         _Movetype_Impact(this, trace_ent);
535
536         return (this.origin == last_origin); // false if teleported by touch
537 }
538
539
540 .float ltime;
541 .void() blocked;
542
543 void _Movetype_Physics_Frame(entity this, float movedt)
544 {
545         this.move_didgravity = -1;
546         switch (this.move_movetype)
547         {
548                 case MOVETYPE_PUSH:
549                 case MOVETYPE_FAKEPUSH:
550                         LOG_DEBUG("Physics: Lacking QuakeC support for Push movetype, FIX ME by using engine physics!");
551                         break;
552                 case MOVETYPE_NONE:
553                         break;
554                 case MOVETYPE_FOLLOW:
555                         _Movetype_Physics_Follow(this);
556                         break;
557                 case MOVETYPE_NOCLIP:
558                         _Movetype_CheckWater(this);
559                         this.origin = this.origin + movedt * this.velocity;
560                         this.angles = this.angles + movedt * this.avelocity;
561                         _Movetype_LinkEdict(this, false);
562                         break;
563                 case MOVETYPE_STEP:
564                         _Movetype_Physics_Step(this, movedt);
565                         break;
566                 case MOVETYPE_WALK:
567                         _Movetype_Physics_Walk(this, movedt);
568                         break;
569                 case MOVETYPE_TOSS:
570                 case MOVETYPE_BOUNCE:
571                 case MOVETYPE_BOUNCEMISSILE:
572                 case MOVETYPE_FLYMISSILE:
573                 case MOVETYPE_FLY:
574                 case MOVETYPE_FLY_WORLDONLY:
575                         _Movetype_Physics_Toss(this, movedt);
576                         if(wasfreed(this))
577                                 return;
578                         _Movetype_LinkEdict(this, true);
579                         break;
580                 case MOVETYPE_PHYSICS:
581                         break;
582         }
583 }
584
585 void _Movetype_Physics_ClientFrame(entity this, float movedt)
586 {
587         this.move_didgravity = -1;
588         switch (this.move_movetype)
589         {
590                 case MOVETYPE_PUSH:
591                 case MOVETYPE_FAKEPUSH:
592                         LOG_DEBUG("Physics: Lacking QuakeC support for Push movetype, FIX ME by using engine physics!");
593                         break;
594                 case MOVETYPE_NONE:
595                         break;
596                 case MOVETYPE_FOLLOW:
597                         _Movetype_Physics_Follow(this);
598                         break;
599                 case MOVETYPE_NOCLIP:
600                         _Movetype_CheckWater(this);
601                         this.origin = this.origin + movedt * this.velocity;
602                         this.angles = this.angles + movedt * this.avelocity;
603                         break;
604                 case MOVETYPE_STEP:
605                         _Movetype_Physics_Step(this, movedt);
606                         break;
607                 case MOVETYPE_WALK:
608                 case MOVETYPE_FLY:
609                 case MOVETYPE_FLY_WORLDONLY:
610                         _Movetype_Physics_Walk(this, movedt);
611                         break;
612                 case MOVETYPE_TOSS:
613                 case MOVETYPE_BOUNCE:
614                 case MOVETYPE_BOUNCEMISSILE:
615                 case MOVETYPE_FLYMISSILE:
616                         _Movetype_Physics_Toss(this, movedt);
617                         break;
618                 case MOVETYPE_PHYSICS:
619                         break;
620         }
621
622         //_Movetype_CheckVelocity(this);
623
624         _Movetype_LinkEdict(this, true);
625
626         //_Movetype_CheckVelocity(this);
627 }
628
629 void Movetype_Physics_NoMatchTicrate(entity this, float movedt, bool isclient)  // to be run every move frame
630 {
631         this.move_time = time;
632
633         if(isclient)
634                 _Movetype_Physics_ClientFrame(this, movedt);
635         else
636                 _Movetype_Physics_Frame(this, movedt);
637         if(wasfreed(this))
638                 return;
639
640         setorigin(this, this.origin);
641 }
642
643 void Movetype_Physics_NoMatchServer(entity this)  // optimized
644 {
645         float movedt = time - this.move_time;
646         this.move_time = time;
647
648         _Movetype_Physics_Frame(this, movedt);
649         if(wasfreed(this))
650                 return;
651
652         setorigin(this, this.origin);
653 }
654
655 void Movetype_Physics_MatchServer(entity this, bool sloppy)
656 {
657         Movetype_Physics_MatchTicrate(this, TICRATE, sloppy);
658 }
659
660 .vector tic_origin;
661 .vector tic_velocity;
662 .int tic_flags;
663 .vector tic_avelocity;
664 .vector tic_angles;
665
666 .vector tic_saved_origin;
667 .vector tic_saved_velocity;
668 .int tic_saved_flags;
669 .vector tic_saved_avelocity;
670 .vector tic_saved_angles;
671 void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy)  // SV_Physics_Entity
672 {
673 #define X(s) \
674         if(this.(s) != this.tic_saved_##s) \
675                 this.tic_##s = this.(s)
676
677         X(origin);
678         X(velocity);
679         X(flags);
680         X(avelocity);
681         X(angles);
682 #undef X
683
684         if(tr <= 0)
685         {
686                 this.flags = this.tic_flags;
687                 this.velocity = this.tic_velocity;
688                 this.origin = this.tic_origin;
689                 this.avelocity = this.tic_avelocity;
690                 this.angles = this.tic_angles;
691                 Movetype_Physics_NoMatchServer(this);
692                 this.tic_origin = this.origin;
693                 this.tic_velocity = this.velocity;
694                 this.tic_avelocity = this.avelocity;
695                 this.tic_angles = this.angles;
696                 this.tic_flags = this.flags;
697
698                 this.tic_saved_flags = this.flags;
699                 this.tic_saved_velocity = this.velocity;
700                 this.tic_saved_origin = this.origin;
701                 this.tic_saved_avelocity = this.avelocity;
702                 this.tic_saved_angles = this.angles;
703                 return;
704         }
705
706         float dt = time - this.move_time;
707
708         int n = max(0, floor(dt / tr));
709         dt -= n * tr;
710         this.move_time += n * tr;
711
712         if(!this.move_didgravity)
713                 this.move_didgravity = ((this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS) && !(this.tic_flags & FL_ONGROUND));
714
715         for (int i = 0; i < n; ++i)
716         {
717                 this.flags = this.tic_flags;
718                 this.velocity = this.tic_velocity;
719                 setorigin(this, this.tic_origin);
720                 this.avelocity = this.tic_avelocity;
721                 this.angles = this.tic_angles;
722                 _Movetype_Physics_Frame(this, tr);
723                 this.tic_origin = this.origin;
724                 this.tic_velocity = this.velocity;
725                 this.tic_avelocity = this.avelocity;
726                 this.tic_angles = this.angles;
727                 this.tic_flags = this.flags;
728                 if(wasfreed(this))
729                         return;
730         }
731
732         this.avelocity = this.tic_avelocity;
733
734         if(dt > 0 && this.move_movetype != MOVETYPE_NONE && !(this.tic_flags & FL_ONGROUND))
735         {
736                 // now continue the move from move_time to time
737                 this.velocity = this.tic_velocity;
738
739                 if(this.move_didgravity > 0)
740                 {
741                         this.velocity_z -= (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1)
742                             * dt
743                             * (this.gravity ? this.gravity : 1)
744                             * PHYS_GRAVITY(this);
745                 }
746
747                 this.angles = this.tic_angles + dt * this.avelocity;
748
749                 if(sloppy || this.move_movetype == MOVETYPE_NOCLIP)
750                 {
751                         setorigin(this, this.tic_origin + dt * this.velocity);
752                 }
753                 else
754                 {
755                         vector oldorg = this.origin;
756                         this.origin = this.tic_origin;
757                         _Movetype_PushEntityTrace(this, dt * this.velocity);
758                         this.origin = oldorg;
759                         if(!trace_startsolid)
760                                 setorigin(this, trace_endpos);
761                 }
762
763                 if(this.move_didgravity > 0 && GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
764                         this.velocity_z -= 0.5 * dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this);
765         }
766         else
767         {
768                 this.velocity = this.tic_velocity;
769                 this.angles = this.tic_angles;
770                 setorigin(this, this.tic_origin);
771         }
772
773         this.tic_saved_flags = this.flags;
774         this.tic_saved_velocity = this.velocity;
775         this.tic_saved_origin = this.origin;
776         this.tic_saved_avelocity = this.avelocity;
777         this.tic_saved_angles = this.angles;
778 }