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