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