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