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