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