]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/movetypes.qc
Remove some debug prints
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / movetypes.qc
1 const float STAT_MOVEFLAGS = 225;
2 const float MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE = 4;
3 #define GRAVITY_UNAFFECTED_BY_TICRATE (getstati(STAT_MOVEFLAGS) & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
4
5 .entity move_groundentity; // FIXME add move_groundnetworkentity?
6 .float move_suspendedinair;
7 .float move_didgravity;
8
9 void _Movetype_CheckVelocity() // SV_CheckVelocity
10 {
11 }
12
13 float Mod_Q1BSP_SuperContentsFromNativeContents(float nativecontents)
14 {
15         switch(nativecontents)
16         {
17                 case CONTENT_EMPTY:
18                         return 0;
19                 case CONTENT_SOLID:
20                         return DPCONTENTS_SOLID | DPCONTENTS_OPAQUE;
21                 case CONTENT_WATER:
22                         return DPCONTENTS_WATER;
23                 case CONTENT_SLIME:
24                         return DPCONTENTS_SLIME;
25                 case CONTENT_LAVA:
26                         return DPCONTENTS_LAVA | DPCONTENTS_NODROP;
27                 case CONTENT_SKY:
28                         return DPCONTENTS_SKY | DPCONTENTS_NODROP | DPCONTENTS_OPAQUE; // to match behaviour of Q3 maps, let sky count as opaque
29         }
30         return 0;
31 }
32
33 float Mod_Q1BSP_NativeContentsFromSuperContents(float supercontents)
34 {
35         if(supercontents & (DPCONTENTS_SOLID | DPCONTENTS_BODY))
36                 return CONTENT_SOLID;
37         if(supercontents & DPCONTENTS_SKY)
38                 return CONTENT_SKY;
39         if(supercontents & DPCONTENTS_LAVA)
40                 return CONTENT_LAVA;
41         if(supercontents & DPCONTENTS_SLIME)
42                 return CONTENT_SLIME;
43         if(supercontents & DPCONTENTS_WATER)
44                 return CONTENT_WATER;
45         return CONTENT_EMPTY;
46 }
47
48 float _Movetype_CheckWater(entity ent) // SV_CheckWater
49 {
50         float supercontents;
51         float nativecontents;
52         vector point;
53
54         point = ent.move_origin;
55         point_z += (ent.mins_z + 1);
56
57         nativecontents = pointcontents(point);
58
59         if(ent.move_watertype)
60         if(ent.move_watertype != nativecontents)
61         {
62                 if(ent.contentstransition)
63                         ent.contentstransition(ent.move_watertype, nativecontents);
64         }
65
66         ent.move_waterlevel = 0;
67         ent.move_watertype = CONTENT_EMPTY;
68
69         supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents);
70         if(supercontents & DPCONTENTS_LIQUIDSMASK)
71         {
72                 ent.move_watertype = nativecontents;
73                 ent.move_waterlevel = 1;
74                 point_y = (ent.origin_y + ((ent.mins_z + ent.maxs_y) * 0.5));
75                 if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
76                 {
77                         ent.move_waterlevel = 2;
78                         point_y = ent.origin_y + ent.view_ofs_y;
79                         if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
80                                 ent.move_waterlevel = 3;
81                 }
82         }
83
84         return (ent.move_waterlevel > 1);
85 }
86
87 void _Movetype_CheckWaterTransition(entity ent) // SV_CheckWaterTransition
88 {
89         float contents = pointcontents(ent.move_origin);
90         
91         if(!ent.move_watertype)
92         {
93                 // just spawned here
94                 if(!autocvar_sv_gameplayfix_fixedcheckwatertransition)
95                 {
96                         ent.move_watertype = contents;
97                         ent.move_waterlevel = 1;
98                         return;
99                 }
100         }
101         else if(ent.move_watertype != contents)
102         {
103                 if(ent.contentstransition)
104                         ent.contentstransition(ent.move_watertype, contents);
105         }
106
107         if(contents <= CONTENT_WATER)
108         {
109                 ent.move_watertype = contents;
110                 ent.move_waterlevel = 1;
111         }
112         else
113         {
114                 ent.move_watertype = CONTENT_EMPTY;
115                 ent.move_waterlevel = (autocvar_sv_gameplayfix_fixedcheckwatertransition ? 0 : contents);
116         }
117 }
118
119 void _Movetype_Impact(entity oth) // SV_Impact
120 {
121         entity oldother, oldself;
122
123         oldself = self;
124         oldother = other;
125
126         if(self.move_touch)
127         {
128                 other = oth;
129
130                 self.move_touch();
131
132                 other = oldother;
133         }
134
135         if(oth.move_touch)
136         {
137                 other = self;
138                 self = oth;
139
140                 self.move_touch();
141
142                 self = oldself;
143                 other = oldother;
144         }
145 }
146
147 void _Movetype_LinkEdict_TouchAreaGrid() // SV_LinkEdict_TouchAreaGrid
148 {
149         entity e, oldself, oldother;
150
151         oldself = self;
152         oldother = other;
153
154         for(e = findradius(0.5 * (self.absmin + self.absmax), 0.5 * vlen(self.absmax - self.absmin)); e; e = e.chain)
155         {
156                 if(e.move_touch)
157                 if(boxesoverlap(e.absmin, e.absmax, oldself.absmin, oldself.absmax))
158                 {
159                         self = e;
160                         other = oldself;
161
162                         trace_allsolid = FALSE;
163                         trace_startsolid = FALSE;
164                         trace_fraction = 1;
165                         trace_inwater = FALSE;
166                         trace_inopen = TRUE;
167                         trace_endpos = e.origin;
168                         trace_plane_normal = '0 0 1';
169                         trace_plane_dist = 0;
170                         trace_ent = oldself;
171
172                         e.move_touch();
173                 }
174         }
175
176         other = oldother;
177         self = oldself;
178 }
179
180 void _Movetype_LinkEdict(float touch_triggers) // SV_LinkEdict
181 {
182         vector mi, ma;
183         if(self.solid == SOLID_BSP)
184         {
185                 // TODO set the absolute bbox
186                 mi = self.mins;
187                 ma = self.maxs;
188         }
189         else
190         {
191                 mi = self.mins;
192                 ma = self.maxs;
193         }
194         mi = mi + self.origin;
195         ma = ma + self.origin;
196
197         if(self.move_flags & FL_ITEM)
198         {
199                 mi_x -= 15;
200                 mi_y -= 15;
201                 mi_z -= 1;
202                 ma_x += 15;
203                 ma_y += 15;
204                 ma_z += 1;
205         }
206         else
207         {
208                 mi_x -= 1;
209                 mi_y -= 1;
210                 mi_z -= 1;
211                 ma_x += 1;
212                 ma_y += 1;
213                 ma_z += 1;
214         }
215
216         self.absmin = mi;
217         self.absmax = ma;
218
219         if(touch_triggers)
220                 _Movetype_LinkEdict_TouchAreaGrid();
221 }
222
223 float _Movetype_TestEntityPosition(vector ofs) // SV_TestEntityPosition
224 {
225         vector org;
226         float cont;
227         org = self.move_origin + ofs;
228
229         cont = self.dphitcontentsmask;
230         self.dphitcontentsmask = DPCONTENTS_SOLID;
231         tracebox(self.move_origin, self.mins, self.maxs, self.move_origin, MOVE_NOMONSTERS, self);
232         self.dphitcontentsmask = cont;
233
234         if(trace_startsolid)
235                 return TRUE;
236
237         if(vlen(trace_endpos - self.move_origin) > 0.0001)
238                 self.move_origin = trace_endpos;
239         return FALSE;
240 }
241
242 float _Movetype_UnstickEntity() // SV_UnstickEntity
243 {
244         if(!_Movetype_TestEntityPosition('0 0 0'))
245                 return TRUE;
246         if(!_Movetype_TestEntityPosition('-1 0 0')) goto success;
247         if(!_Movetype_TestEntityPosition('1 0 0')) goto success;
248         if(!_Movetype_TestEntityPosition('0 -1 0')) goto success;
249         if(!_Movetype_TestEntityPosition('0 1 0')) goto success;
250         if(!_Movetype_TestEntityPosition('-1 -1 0')) goto success;
251         if(!_Movetype_TestEntityPosition('1 -1 0')) goto success;
252         if(!_Movetype_TestEntityPosition('-1 1 0')) goto success;
253         if(!_Movetype_TestEntityPosition('1 1 0')) goto success;
254         float i;
255         for(i = 1; i <= 17; ++i)
256         {
257                 if(!_Movetype_TestEntityPosition('0 0 -1' * i)) goto success;
258                 if(!_Movetype_TestEntityPosition('0 0 1' * i)) goto success;
259         }
260         dprint(sprintf(_("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n"), num_for_edict(self), self.classname, vtos(self.move_origin)));
261         return FALSE;
262 :success
263         dprint(sprintf(_("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n"), num_for_edict(self), self.classname, vtos(self.move_origin)));
264         _Movetype_LinkEdict(TRUE);
265         return TRUE;
266 }
267
268 vector _Movetype_ClipVelocity(vector vel, vector norm, float f) // SV_ClipVelocity
269 {
270         vel = vel - ((vel * norm) * norm) * f;
271
272         if(vel_x > -0.1 && vel_x < 0.1) vel_x = 0;
273         if(vel_y > -0.1 && vel_y < 0.1) vel_y = 0;
274         if(vel_z > -0.1 && vel_z < 0.1) vel_z = 0;
275
276         return vel;
277 }
278
279 void _Movetype_PushEntityTrace(vector push)
280 {
281         vector end;
282         float type;
283
284         end = self.move_origin + push;
285
286         if(self.move_nomonsters)
287                 type = max(0, self.move_nomonsters);
288         else if(self.move_movetype == MOVETYPE_FLYMISSILE)
289                 type = MOVE_MISSILE;
290         else if(self.solid == SOLID_TRIGGER || self.solid == SOLID_NOT)
291                 type = MOVE_NOMONSTERS;
292         else
293                 type = MOVE_NORMAL;
294
295         tracebox(self.move_origin, self.mins, self.maxs, end, type, self);
296 }
297
298 float _Movetype_PushEntity(vector push, float failonstartsolid) // SV_PushEntity
299 {
300         _Movetype_PushEntityTrace(push);
301
302         if(trace_startsolid && failonstartsolid)
303                 return trace_fraction;
304
305         self.move_origin = trace_endpos;
306
307         if(trace_fraction < 1)
308                 if(self.solid >= SOLID_TRIGGER && (!(self.move_flags & FL_ONGROUND) || (self.move_groundentity != trace_ent)))
309                         _Movetype_Impact(trace_ent);
310
311         return trace_fraction;
312 }
313
314 #define MAX_CLIP_PLANES 5
315 void _Movetype_Physics_Toss(float dt) // SV_Physics_Toss
316 {
317         if(self.move_flags & FL_ONGROUND)
318         {
319                 if(self.move_velocity_z >= 1/32)
320                         self.move_flags &= ~FL_ONGROUND;
321                 else if(!self.move_groundentity)
322                         return;
323                 else if(self.move_suspendedinair && wasfreed(self.move_groundentity))
324                 {
325                         self.move_groundentity = world;
326                         return;
327                 }
328         }
329
330         self.move_suspendedinair = FALSE;
331
332         _Movetype_CheckVelocity();
333
334         if(self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS)
335         {
336                 self.move_didgravity = 1;
337                 if(GRAVITY_UNAFFECTED_BY_TICRATE)
338                 {
339                         if(self.gravity)
340                                 self.move_velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
341                         else
342                                 self.move_velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
343                 }
344                 else
345                 {
346                         if(self.gravity)
347                                 self.move_velocity_z -= dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
348                         else
349                                 self.move_velocity_z -= dt * getstatf(STAT_MOVEVARS_GRAVITY);
350                 }
351         }
352
353         self.move_angles = self.move_angles + self.move_avelocity * dt;
354
355         float movetime, bump;
356         movetime = dt;
357         for(bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; ++bump)
358         {
359                 vector move;
360                 move = self.move_velocity * movetime;
361                 _Movetype_PushEntity(move, TRUE);
362                 if(wasfreed(self))
363                         return;
364
365                 if(trace_startsolid)
366                 {
367                         _Movetype_UnstickEntity();
368                         _Movetype_PushEntity(move, FALSE);
369                         if(wasfreed(self))
370                                 return;
371                 }
372
373                 if(trace_fraction == 1)
374                         break;
375
376                 movetime *= 1 - min(1, trace_fraction);
377
378                 if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
379                 {
380                         self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 2.0);
381                         self.move_flags &= ~FL_ONGROUND;
382                 }
383                 else if(self.move_movetype == MOVETYPE_BOUNCE)
384                 {
385                         float d, bouncefac, bouncestop;
386
387                         bouncefac = self.move_bounce_factor;     if(!bouncefac)  bouncefac = 0.5;
388                         bouncestop = self.move_bounce_stopspeed; if(!bouncestop) bouncestop = 60 / 800;
389                         if(self.gravity)
390                                 bouncestop *= self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
391                         else
392                                 bouncestop *= getstatf(STAT_MOVEVARS_GRAVITY);
393
394                         self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1 + bouncefac);
395
396                         d = trace_plane_normal * self.move_velocity;
397                         if(trace_plane_normal_z > 0.7 && d < bouncestop && d > -bouncestop)
398                         {
399                                 self.move_flags |= FL_ONGROUND;
400                                 self.move_groundentity = trace_ent;
401                                 self.move_velocity = '0 0 0';
402                                 self.move_avelocity = '0 0 0';
403                         }
404                         else
405                                 self.move_flags &= ~FL_ONGROUND;
406                 }
407                 else
408                 {
409                         self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1.0);
410                         if(trace_plane_normal_z > 0.7)
411                         {
412                                 self.move_flags |= FL_ONGROUND;
413                                 self.move_groundentity = trace_ent;
414                                 if(trace_ent.solid == SOLID_BSP)
415                                         self.move_suspendedinair = TRUE;
416                                 self.move_velocity = '0 0 0';
417                                 self.move_avelocity = '0 0 0';
418                         }
419                         else
420                                 self.move_flags &= ~FL_ONGROUND;
421                 }
422
423                 // DP revision 8905 (just, WHY...)
424                 if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
425                         break;
426
427                 // DP revision 8918 (WHY...)
428                 if(self.move_flags & FL_ONGROUND)
429                         break;
430         }
431
432         if(GRAVITY_UNAFFECTED_BY_TICRATE)
433         if(self.move_didgravity > 0)
434         if(!(self.move_flags & FL_ONGROUND))
435         {
436                 if(self.gravity)
437                         self.move_velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
438                 else
439                         self.move_velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
440         }
441
442         _Movetype_CheckWaterTransition(self);
443 }
444
445 void _Movetype_Physics_Frame(float movedt)
446 {
447         self.move_didgravity = -1;
448         switch(self.move_movetype)
449         {
450                 case MOVETYPE_PUSH:
451                 case MOVETYPE_FAKEPUSH:
452                         error("SV_Physics_Pusher not implemented");
453                         break;
454                 case MOVETYPE_NONE:
455                         break;
456                 case MOVETYPE_FOLLOW:
457                         error("SV_Physics_Follow not implemented");
458                         break;
459                 case MOVETYPE_NOCLIP:
460                         _Movetype_CheckWater(self);
461                         self.move_origin = self.move_origin + ticrate * self.move_velocity;
462                         self.move_angles = self.move_angles + ticrate * self.move_avelocity;
463                         _Movetype_LinkEdict(FALSE);
464                         break;
465                 case MOVETYPE_STEP:
466                         error("SV_Physics_Step not implemented");
467                         break;
468                 case MOVETYPE_WALK:
469                         error("SV_Physics_Walk not implemented");
470                         break;
471                 case MOVETYPE_TOSS:
472                 case MOVETYPE_BOUNCE:
473                 case MOVETYPE_BOUNCEMISSILE:
474                 case MOVETYPE_FLYMISSILE:
475                 case MOVETYPE_FLY:
476                         _Movetype_Physics_Toss(movedt);
477                         break;
478         }
479 }
480
481 void Movetype_Physics_NoMatchServer() // optimized
482 {
483         float movedt;
484
485         movedt = time - self.move_time;
486         self.move_time = time;
487
488         _Movetype_Physics_Frame(movedt);
489         if(wasfreed(self))
490                 return;
491
492         self.avelocity = self.move_avelocity;
493         self.velocity = self.move_velocity;
494         self.angles = self.move_angles;
495         setorigin(self, self.move_origin);
496 }
497
498 void Movetype_Physics_MatchServer(float sloppy)
499 {
500         Movetype_Physics_MatchTicrate(ticrate, sloppy);
501 }
502
503 void Movetype_Physics_MatchTicrate(float tr, float sloppy) // SV_Physics_Entity
504 {
505         float n, i, dt, movedt;
506
507         if(tr <= 0)
508         {
509                 Movetype_Physics_NoMatchServer();
510                 return;
511         }
512
513         dt = time - self.move_time;
514
515         movedt = tr;
516         n = max(0, floor(dt / tr));
517         dt -= n * tr;
518         self.move_time += n * tr;
519
520         if(!self.move_didgravity)
521                 self.move_didgravity = ((self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS) && !(self.move_flags & FL_ONGROUND));
522
523         for(i = 0; i < n; ++i)
524         {
525                 _Movetype_Physics_Frame(movedt);
526                 if(wasfreed(self))
527                         return;
528         }
529
530         self.avelocity = self.move_avelocity;
531
532         if(dt > 0 && self.move_movetype != MOVETYPE_NONE && !(self.move_flags & FL_ONGROUND))
533         {
534                 // now continue the move from move_time to time
535                 self.velocity = self.move_velocity;
536
537                 if(self.move_didgravity > 0)
538                 {
539                         if(GRAVITY_UNAFFECTED_BY_TICRATE)
540                         {
541                                 if(self.gravity)
542                                         self.velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
543                                 else
544                                         self.velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
545                         }
546                         else
547                         {
548                                 if(self.gravity)
549                                         self.velocity_z -= dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
550                                 else
551                                         self.velocity_z -= dt * getstatf(STAT_MOVEVARS_GRAVITY);
552                         }
553                 }
554
555                 self.angles = self.move_angles + dt * self.avelocity;
556
557                 if(sloppy || self.movetype == MOVETYPE_NOCLIP)
558                 {
559                         setorigin(self, self.move_origin + dt * self.velocity);
560                 }
561                 else
562                 {
563                         _Movetype_PushEntityTrace(dt * self.velocity);
564                         if(!trace_startsolid)
565                                 setorigin(self, trace_endpos);
566                 }
567
568                 if(self.move_didgravity > 0)
569                 {
570                         if(GRAVITY_UNAFFECTED_BY_TICRATE)
571                         {
572                                 if(self.gravity)
573                                         self.velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
574                                 else
575                                         self.velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
576                         }
577                 }
578         }
579         else
580         {
581                 self.velocity = self.move_velocity;
582                 self.angles = self.move_angles;
583                 setorigin(self, self.move_origin);
584         }
585 }