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