]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/movetypes/movetypes.qc
Replace all direct assignments to self with setself(e)
[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                 SELFCALL(oth, oth.move_touch());
341                 SELFCALL_DONE();
342
343                 other = oldother;
344         }
345 }
346
347 void _Movetype_LinkEdict_TouchAreaGrid()  // SV_LinkEdict_TouchAreaGrid
348 {SELFPARAM();
349         entity oldself = self;
350         entity oldother = other;
351
352         for (entity e = findradius(0.5 * (self.absmin + self.absmax), 0.5 * vlen(self.absmax - self.absmin)); e; e = e.chain)
353         {
354                 if(e.move_touch && boxesoverlap(e.absmin, e.absmax, oldself.absmin, oldself.absmax))
355                 {
356                         setself(e);
357                         other = oldself;
358
359                         trace_allsolid = false;
360                         trace_startsolid = false;
361                         trace_fraction = 1;
362                         trace_inwater = false;
363                         trace_inopen = true;
364                         trace_endpos = e.origin;
365                         trace_plane_normal = '0 0 1';
366                         trace_plane_dist = 0;
367                         trace_ent = oldself;
368
369                         e.move_touch();
370                 }
371         }
372
373         other = oldother;
374         setself(oldself);
375 }
376
377 void _Movetype_LinkEdict(bool touch_triggers)  // SV_LinkEdict
378 {SELFPARAM();
379         vector mi, ma;
380         if(self.solid == SOLID_BSP)
381         {
382                 // TODO set the absolute bbox
383                 mi = self.mins;
384                 ma = self.maxs;
385         }
386         else
387         {
388                 mi = self.mins;
389                 ma = self.maxs;
390         }
391         mi += self.move_origin;
392         ma += self.move_origin;
393
394         if(self.move_flags & FL_ITEM)
395         {
396                 mi.x -= 15;
397                 mi.y -= 15;
398                 mi.z -= 1;
399                 ma.x += 15;
400                 ma.y += 15;
401                 ma.z += 1;
402         }
403         else
404         {
405                 mi.x -= 1;
406                 mi.y -= 1;
407                 mi.z -= 1;
408                 ma.x += 1;
409                 ma.y += 1;
410                 ma.z += 1;
411         }
412
413         self.absmin = mi;
414         self.absmax = ma;
415
416         if(touch_triggers)
417                 _Movetype_LinkEdict_TouchAreaGrid();
418 }
419
420 bool _Movetype_TestEntityPosition(vector ofs)  // SV_TestEntityPosition
421 {SELFPARAM();
422 //      vector org = self.move_origin + ofs;
423
424         int cont = self.dphitcontentsmask;
425         self.dphitcontentsmask = DPCONTENTS_SOLID;
426         tracebox(self.move_origin, self.mins, self.maxs, self.move_origin, MOVE_NOMONSTERS, self);
427         self.dphitcontentsmask = cont;
428
429         if(trace_startsolid)
430                 return true;
431
432         if(vlen(trace_endpos - self.move_origin) > 0.0001)
433                 self.move_origin = trace_endpos;
434         return false;
435 }
436
437 bool _Movetype_UnstickEntity()  // SV_UnstickEntity
438 {SELFPARAM();
439         if(!_Movetype_TestEntityPosition('0 0 0')) return true;
440         if(!_Movetype_TestEntityPosition('-1 0 0')) goto success;
441         if(!_Movetype_TestEntityPosition('1 0 0')) goto success;
442         if(!_Movetype_TestEntityPosition('0 -1 0')) goto success;
443         if(!_Movetype_TestEntityPosition('0 1 0')) goto success;
444         if(!_Movetype_TestEntityPosition('-1 -1 0')) goto success;
445         if(!_Movetype_TestEntityPosition('1 -1 0')) goto success;
446         if(!_Movetype_TestEntityPosition('-1 1 0')) goto success;
447         if(!_Movetype_TestEntityPosition('1 1 0')) goto success;
448         for (int i = 1; i <= 17; ++i)
449         {
450                 if(!_Movetype_TestEntityPosition('0 0 -1' * i)) goto success;
451                 if(!_Movetype_TestEntityPosition('0 0 1' * i)) goto success;
452         }
453         LOG_TRACEF("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n",
454                 num_for_edict(self), self.classname, vtos(self.move_origin));
455         return false;
456         : success;
457         LOG_TRACEF("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n",
458                 num_for_edict(self), self.classname, vtos(self.move_origin));
459         _Movetype_LinkEdict(true);
460         return true;
461 }
462
463 vector _Movetype_ClipVelocity(vector vel, vector norm, float f)  // SV_ClipVelocity
464 {
465         vel -= ((vel * norm) * norm) * f;
466
467         if(vel.x > -0.1 && vel.x < 0.1) vel.x = 0;
468         if(vel.y > -0.1 && vel.y < 0.1) vel.y = 0;
469         if(vel.z > -0.1 && vel.z < 0.1) vel.z = 0;
470
471         return vel;
472 }
473
474 void _Movetype_PushEntityTrace(vector push)
475 {SELFPARAM();
476         vector end = self.move_origin + push;
477         int type;
478         if(self.move_nomonsters)
479                 type = max(0, self.move_nomonsters);
480         else if(self.move_movetype == MOVETYPE_FLYMISSILE)
481                 type = MOVE_MISSILE;
482         else if(self.solid == SOLID_TRIGGER || self.solid == SOLID_NOT)
483                 type = MOVE_NOMONSTERS;
484         else
485                 type = MOVE_NORMAL;
486
487         tracebox(self.move_origin, self.mins, self.maxs, end, type, self);
488 }
489
490 float _Movetype_PushEntity(vector push, bool failonstartsolid)  // SV_PushEntity
491 {SELFPARAM();
492         _Movetype_PushEntityTrace(push);
493
494         if(trace_startsolid && failonstartsolid)
495                 return trace_fraction;
496
497         self.move_origin = trace_endpos;
498
499         if(trace_fraction < 1)
500                 if(self.solid >= SOLID_TRIGGER && (!(self.move_flags & FL_ONGROUND) || (self.move_groundentity != trace_ent)))
501                         _Movetype_Impact(trace_ent);
502
503         return trace_fraction;
504 }
505
506
507 .float ltime;
508 .void() blocked;
509 // matrix version of makevectors, sets v_forward, v_right and v_up
510 void makevectors_matrix(vector myangles)  // AngleVectorsFLU
511 {
512         v_forward = v_right = v_up = '0 0 0';
513
514         float y = myangles.y * (M_PI * 2 / 360);
515         float sy = sin(y);
516         float cy = cos(y);
517         float p = myangles.x * (M_PI * 2 / 360);
518         float sp = sin(p);
519         float cp = cos(p);
520         if(v_forward)
521         {
522                 v_forward.x = cp * cy;
523                 v_forward.y = cp * sy;
524                 v_forward.z = -sp;
525         }
526         if(v_right || v_up)
527         {
528                 if(myangles.z)
529                 {
530                         float r = myangles.z * (M_PI * 2 / 360);
531                         float sr = sin(r);
532                         float cr = cos(r);
533                         if(v_right)
534                         {
535                                 v_right.x = sr * sp * cy + cr * -sy;
536                                 v_right.y = sr * sp * sy + cr * cy;
537                                 v_right.z = sr * cp;
538                         }
539                         if(v_up)
540                         {
541                                 v_up.x = cr * sp * cy + -sr * -sy;
542                                 v_up.y = cr * sp * sy + -sr * cy;
543                                 v_up.z = cr * cp;
544                         }
545                 }
546                 else
547                 {
548                         if(v_right)
549                         {
550                                 v_right.x = -sy;
551                                 v_right.y = cy;
552                                 v_right.z = 0;
553                         }
554                         if(v_up)
555                         {
556                                 v_up.x = sp * cy;
557                                 v_up.y = sp * sy;
558                                 v_up.z = cp;
559                         }
560                 }
561         }
562 }
563
564 void _Movetype_Physics_Frame(float movedt)
565 {SELFPARAM();
566         self.move_didgravity = -1;
567         switch (self.move_movetype)
568         {
569                 case MOVETYPE_PUSH:
570                 case MOVETYPE_FAKEPUSH:
571                         _Movetype_Physics_Pusher(movedt);
572                         break;
573                 case MOVETYPE_NONE:
574                         break;
575                 case MOVETYPE_FOLLOW:
576                         _Movetype_Physics_Follow();
577                         break;
578                 case MOVETYPE_NOCLIP:
579                         _Movetype_CheckWater(self);
580                         self.move_origin = self.move_origin + TICRATE * self.move_velocity;
581                         self.move_angles = self.move_angles + TICRATE * self.move_avelocity;
582                         _Movetype_LinkEdict(false);
583                         break;
584                 case MOVETYPE_STEP:
585                         _Movetype_Physics_Step(movedt);
586                         break;
587                 case MOVETYPE_WALK:
588                         _Movetype_Physics_Walk(movedt);
589                         break;
590                 case MOVETYPE_TOSS:
591                 case MOVETYPE_BOUNCE:
592                 case MOVETYPE_BOUNCEMISSILE:
593                 case MOVETYPE_FLYMISSILE:
594                 case MOVETYPE_FLY:
595                         _Movetype_Physics_Toss(movedt);
596                         break;
597         }
598 }
599
600 void Movetype_Physics_NoMatchServer()  // optimized
601 {SELFPARAM();
602         float movedt = time - self.move_time;
603         self.move_time = time;
604
605         _Movetype_Physics_Frame(movedt);
606         if(wasfreed(self))
607                 return;
608
609         self.avelocity = self.move_avelocity;
610         self.velocity = self.move_velocity;
611         self.angles = self.move_angles;
612         setorigin(self, self.move_origin);
613 }
614
615 void Movetype_Physics_MatchServer(bool sloppy)
616 {
617         Movetype_Physics_MatchTicrate(TICRATE, sloppy);
618 }
619
620 void Movetype_Physics_MatchTicrate(float tr, bool sloppy)  // SV_Physics_Entity
621 {SELFPARAM();
622         if(tr <= 0)
623         {
624                 Movetype_Physics_NoMatchServer();
625                 return;
626         }
627
628         float dt = time - self.move_time;
629
630         int n = max(0, floor(dt / tr));
631         dt -= n * tr;
632         self.move_time += n * tr;
633
634         if(!self.move_didgravity)
635                 self.move_didgravity = ((self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS) && !(self.move_flags & FL_ONGROUND));
636
637         for (int i = 0; i < n; ++i)
638         {
639                 _Movetype_Physics_Frame(tr);
640                 if(wasfreed(self))
641                         return;
642         }
643
644         self.avelocity = self.move_avelocity;
645
646         if(dt > 0 && self.move_movetype != MOVETYPE_NONE && !(self.move_flags & FL_ONGROUND))
647         {
648                 // now continue the move from move_time to time
649                 self.velocity = self.move_velocity;
650
651                 if(self.move_didgravity > 0)
652                 {
653                         self.velocity_z -= (GRAVITY_UNAFFECTED_BY_TICRATE ? 0.5 : 1)
654                             * dt
655                             * (self.gravity ? self.gravity : 1)
656                             * PHYS_GRAVITY;
657                 }
658
659                 self.angles = self.move_angles + dt * self.avelocity;
660
661                 if(sloppy || self.move_movetype == MOVETYPE_NOCLIP)
662                 {
663                         setorigin(self, self.move_origin + dt * self.velocity);
664                 }
665                 else
666                 {
667                         _Movetype_PushEntityTrace(dt * self.velocity);
668                         if(!trace_startsolid)
669                                 setorigin(self, trace_endpos);
670                 }
671
672                 if(self.move_didgravity > 0 && GRAVITY_UNAFFECTED_BY_TICRATE)
673                         self.velocity_z -= 0.5 * dt * (self.gravity ? self.gravity : 1) * PHYS_GRAVITY;
674         }
675         else
676         {
677                 self.velocity = self.move_velocity;
678                 self.angles = self.move_angles;
679                 setorigin(self, self.move_origin);
680         }
681 }