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