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