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