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