]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/movetypes.qc
a1e9a65f7a6008717266c715e72173bbcae4aedb
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / movetypes.qc
1 const int MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE = 4;
2 #define GRAVITY_UNAFFECTED_BY_TICRATE (getstati(STAT_MOVEFLAGS) & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
3
4 .entity move_groundentity; // FIXME add move_groundnetworkentity?
5 .float move_suspendedinair;
6 .float move_didgravity;
7
8 void _Movetype_CheckVelocity() // SV_CheckVelocity
9 {
10 }
11
12 float _Movetype_CheckWater(entity ent) // SV_CheckWater
13 {
14         int supercontents;
15         float nativecontents;
16         vector point;
17
18         point = ent.move_origin;
19         point_z += (ent.mins.z + 1);
20
21         nativecontents = pointcontents(point);
22
23         if(ent.move_watertype)
24         if(ent.move_watertype != nativecontents)
25         {
26                 //print(sprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", ent.move_watertype, nativecontents));
27                 if(ent.contentstransition)
28                         ent.contentstransition(ent.move_watertype, nativecontents);
29         }
30
31         ent.move_waterlevel = 0;
32         ent.move_watertype = CONTENT_EMPTY;
33
34         supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents);
35         if(supercontents & DPCONTENTS_LIQUIDSMASK)
36         {
37                 ent.move_watertype = nativecontents;
38                 ent.move_waterlevel = 1;
39                 point_y = (ent.origin.y + ((ent.mins.z + ent.maxs.y) * 0.5));
40                 if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
41                 {
42                         ent.move_waterlevel = 2;
43                         point_y = ent.origin.y + ent.view_ofs.y;
44                         if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
45                                 ent.move_waterlevel = 3;
46                 }
47         }
48
49         return (ent.move_waterlevel > 1);
50 }
51
52 void _Movetype_CheckWaterTransition(entity ent) // SV_CheckWaterTransition
53 {
54         float contents = pointcontents(ent.move_origin);
55         
56         if(!ent.move_watertype)
57         {
58                 // just spawned here
59                 if(!autocvar_cl_gameplayfix_fixedcheckwatertransition)
60                 {
61                         ent.move_watertype = contents;
62                         ent.move_waterlevel = 1;
63                         return;
64                 }
65         }
66         else if(ent.move_watertype != contents)
67         {
68                 //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));
69                 if(ent.contentstransition)
70                         ent.contentstransition(ent.move_watertype, contents);
71         }
72
73         if(contents <= CONTENT_WATER)
74         {
75                 ent.move_watertype = contents;
76                 ent.move_waterlevel = 1;
77         }
78         else
79         {
80                 ent.move_watertype = CONTENT_EMPTY;
81                 ent.move_waterlevel = (autocvar_cl_gameplayfix_fixedcheckwatertransition ? 0 : contents);
82         }
83 }
84
85 void _Movetype_Impact(entity oth) // SV_Impact
86 {
87         entity oldother, oldself;
88
89         oldself = self;
90         oldother = other;
91
92         if(self.move_touch)
93         {
94                 other = oth;
95
96                 self.move_touch();
97
98                 other = oldother;
99         }
100
101         if(oth.move_touch)
102         {
103                 other = self;
104                 self = oth;
105
106                 self.move_touch();
107
108                 self = oldself;
109                 other = oldother;
110         }
111 }
112
113 void _Movetype_LinkEdict_TouchAreaGrid() // SV_LinkEdict_TouchAreaGrid
114 {
115         entity e, oldself, oldother;
116
117         oldself = self;
118         oldother = other;
119
120         for(e = findradius(0.5 * (self.absmin + self.absmax), 0.5 * vlen(self.absmax - self.absmin)); e; e = e.chain)
121         {
122                 if(e.move_touch)
123                 if(boxesoverlap(e.absmin, e.absmax, oldself.absmin, oldself.absmax))
124                 {
125                         self = e;
126                         other = oldself;
127
128                         trace_allsolid = false;
129                         trace_startsolid = false;
130                         trace_fraction = 1;
131                         trace_inwater = false;
132                         trace_inopen = true;
133                         trace_endpos = e.origin;
134                         trace_plane_normal = '0 0 1';
135                         trace_plane_dist = 0;
136                         trace_ent = oldself;
137
138                         e.move_touch();
139                 }
140         }
141
142         other = oldother;
143         self = oldself;
144 }
145
146 void _Movetype_LinkEdict(float touch_triggers) // SV_LinkEdict
147 {
148         vector mi, ma;
149         if(self.solid == SOLID_BSP)
150         {
151                 // TODO set the absolute bbox
152                 mi = self.mins;
153                 ma = self.maxs;
154         }
155         else
156         {
157                 mi = self.mins;
158                 ma = self.maxs;
159         }
160         mi = mi + self.origin;
161         ma = ma + self.origin;
162
163         if(self.move_flags & FL_ITEM)
164         {
165                 mi.x -= 15;
166                 mi.y -= 15;
167                 mi.z -= 1;
168                 ma.x += 15;
169                 ma.y += 15;
170                 ma.z += 1;
171         }
172         else
173         {
174                 mi.x -= 1;
175                 mi.y -= 1;
176                 mi.z -= 1;
177                 ma.x += 1;
178                 ma.y += 1;
179                 ma.z += 1;
180         }
181
182         self.absmin = mi;
183         self.absmax = ma;
184
185         if(touch_triggers)
186                 _Movetype_LinkEdict_TouchAreaGrid();
187 }
188
189 float _Movetype_TestEntityPosition(vector ofs) // SV_TestEntityPosition
190 {
191         vector org;
192         org = self.move_origin + ofs;
193
194         int 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 const float 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(bool sloppy)
464 {
465         Movetype_Physics_MatchTicrate(ticrate, sloppy);
466 }
467
468 void Movetype_Physics_MatchTicrate(float tr, bool 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 }