Merge branch 'master' into terencehill/music_player
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / movetypes.qc
1 const float 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         float 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         float cont;
193         org = self.move_origin + ofs;
194
195         cont = self.dphitcontentsmask;
196         self.dphitcontentsmask = DPCONTENTS_SOLID;
197         tracebox(self.move_origin, self.mins, self.maxs, self.move_origin, MOVE_NOMONSTERS, self);
198         self.dphitcontentsmask = cont;
199
200         if(trace_startsolid)
201                 return TRUE;
202
203         if(vlen(trace_endpos - self.move_origin) > 0.0001)
204                 self.move_origin = trace_endpos;
205         return FALSE;
206 }
207
208 float _Movetype_UnstickEntity() // SV_UnstickEntity
209 {
210         if(!_Movetype_TestEntityPosition('0 0 0'))
211                 return TRUE;
212         if(!_Movetype_TestEntityPosition('-1 0 0')) goto success;
213         if(!_Movetype_TestEntityPosition('1 0 0')) goto success;
214         if(!_Movetype_TestEntityPosition('0 -1 0')) goto success;
215         if(!_Movetype_TestEntityPosition('0 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         if(!_Movetype_TestEntityPosition('1 1 0')) goto success;
220         float i;
221         for(i = 1; i <= 17; ++i)
222         {
223                 if(!_Movetype_TestEntityPosition('0 0 -1' * i)) goto success;
224                 if(!_Movetype_TestEntityPosition('0 0 1' * i)) goto success;
225         }
226         dprintf("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n", num_for_edict(self), self.classname, vtos(self.move_origin));
227         return FALSE;
228 :success
229         dprintf("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n", num_for_edict(self), self.classname, vtos(self.move_origin));
230         _Movetype_LinkEdict(TRUE);
231         return TRUE;
232 }
233
234 vector _Movetype_ClipVelocity(vector vel, vector norm, float f) // SV_ClipVelocity
235 {
236         vel = vel - ((vel * norm) * norm) * f;
237
238         if(vel_x > -0.1 && vel_x < 0.1) vel_x = 0;
239         if(vel_y > -0.1 && vel_y < 0.1) vel_y = 0;
240         if(vel_z > -0.1 && vel_z < 0.1) vel_z = 0;
241
242         return vel;
243 }
244
245 void _Movetype_PushEntityTrace(vector push)
246 {
247         vector end;
248         float type;
249
250         end = self.move_origin + push;
251
252         if(self.move_nomonsters)
253                 type = max(0, self.move_nomonsters);
254         else if(self.move_movetype == MOVETYPE_FLYMISSILE)
255                 type = MOVE_MISSILE;
256         else if(self.solid == SOLID_TRIGGER || self.solid == SOLID_NOT)
257                 type = MOVE_NOMONSTERS;
258         else
259                 type = MOVE_NORMAL;
260
261         tracebox(self.move_origin, self.mins, self.maxs, end, type, self);
262 }
263
264 float _Movetype_PushEntity(vector push, float failonstartsolid) // SV_PushEntity
265 {
266         _Movetype_PushEntityTrace(push);
267
268         if(trace_startsolid && failonstartsolid)
269                 return trace_fraction;
270
271         self.move_origin = trace_endpos;
272
273         if(trace_fraction < 1)
274                 if(self.solid >= SOLID_TRIGGER && (!(self.move_flags & FL_ONGROUND) || (self.move_groundentity != trace_ent)))
275                         _Movetype_Impact(trace_ent);
276
277         return trace_fraction;
278 }
279
280 #define MAX_CLIP_PLANES 5
281 void _Movetype_Physics_Toss(float dt) // SV_Physics_Toss
282 {
283         if(self.move_flags & FL_ONGROUND)
284         {
285                 if(self.move_velocity_z >= 1/32)
286                         self.move_flags &= ~FL_ONGROUND;
287                 else if(!self.move_groundentity)
288                         return;
289                 else if(self.move_suspendedinair && wasfreed(self.move_groundentity))
290                 {
291                         self.move_groundentity = world;
292                         return;
293                 }
294         }
295
296         self.move_suspendedinair = FALSE;
297
298         _Movetype_CheckVelocity();
299
300         if(self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS)
301         {
302                 self.move_didgravity = 1;
303                 if(GRAVITY_UNAFFECTED_BY_TICRATE)
304                 {
305                         if(self.gravity)
306                                 self.move_velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
307                         else
308                                 self.move_velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
309                 }
310                 else
311                 {
312                         if(self.gravity)
313                                 self.move_velocity_z -= dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
314                         else
315                                 self.move_velocity_z -= dt * getstatf(STAT_MOVEVARS_GRAVITY);
316                 }
317         }
318
319         self.move_angles = self.move_angles + self.move_avelocity * dt;
320
321         float movetime, bump;
322         movetime = dt;
323         for(bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; ++bump)
324         {
325                 vector move;
326                 move = self.move_velocity * movetime;
327                 _Movetype_PushEntity(move, TRUE);
328                 if(wasfreed(self))
329                         return;
330
331                 if(trace_startsolid)
332                 {
333                         _Movetype_UnstickEntity();
334                         _Movetype_PushEntity(move, FALSE);
335                         if(wasfreed(self))
336                                 return;
337                 }
338
339                 if(trace_fraction == 1)
340                         break;
341
342                 movetime *= 1 - min(1, trace_fraction);
343
344                 if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
345                 {
346                         self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 2.0);
347                         self.move_flags &= ~FL_ONGROUND;
348                 }
349                 else if(self.move_movetype == MOVETYPE_BOUNCE)
350                 {
351                         float d, bouncefac, bouncestop;
352
353                         bouncefac = self.move_bounce_factor;     if(!bouncefac)  bouncefac = 0.5;
354                         bouncestop = self.move_bounce_stopspeed; if(!bouncestop) bouncestop = 60 / 800;
355                         if(self.gravity)
356                                 bouncestop *= self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
357                         else
358                                 bouncestop *= getstatf(STAT_MOVEVARS_GRAVITY);
359
360                         self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1 + bouncefac);
361
362                         d = trace_plane_normal * self.move_velocity;
363                         if(trace_plane_normal_z > 0.7 && d < bouncestop && d > -bouncestop)
364                         {
365                                 self.move_flags |= FL_ONGROUND;
366                                 self.move_groundentity = trace_ent;
367                                 self.move_velocity = '0 0 0';
368                                 self.move_avelocity = '0 0 0';
369                         }
370                         else
371                                 self.move_flags &= ~FL_ONGROUND;
372                 }
373                 else
374                 {
375                         self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1.0);
376                         if(trace_plane_normal_z > 0.7)
377                         {
378                                 self.move_flags |= FL_ONGROUND;
379                                 self.move_groundentity = trace_ent;
380                                 if(trace_ent.solid == SOLID_BSP)
381                                         self.move_suspendedinair = TRUE;
382                                 self.move_velocity = '0 0 0';
383                                 self.move_avelocity = '0 0 0';
384                         }
385                         else
386                                 self.move_flags &= ~FL_ONGROUND;
387                 }
388
389                 // DP revision 8905 (just, WHY...)
390                 if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
391                         break;
392
393                 // DP revision 8918 (WHY...)
394                 if(self.move_flags & FL_ONGROUND)
395                         break;
396         }
397
398         if(GRAVITY_UNAFFECTED_BY_TICRATE)
399         if(self.move_didgravity > 0)
400         if(!(self.move_flags & FL_ONGROUND))
401         {
402                 if(self.gravity)
403                         self.move_velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
404                 else
405                         self.move_velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
406         }
407
408         _Movetype_CheckWaterTransition(self);
409 }
410
411 void _Movetype_Physics_Frame(float movedt)
412 {
413         self.move_didgravity = -1;
414         switch(self.move_movetype)
415         {
416                 case MOVETYPE_PUSH:
417                 case MOVETYPE_FAKEPUSH:
418                         error("SV_Physics_Pusher not implemented");
419                         break;
420                 case MOVETYPE_NONE:
421                         break;
422                 case MOVETYPE_FOLLOW:
423                         error("SV_Physics_Follow not implemented");
424                         break;
425                 case MOVETYPE_NOCLIP:
426                         _Movetype_CheckWater(self);
427                         self.move_origin = self.move_origin + ticrate * self.move_velocity;
428                         self.move_angles = self.move_angles + ticrate * self.move_avelocity;
429                         _Movetype_LinkEdict(FALSE);
430                         break;
431                 case MOVETYPE_STEP:
432                         error("SV_Physics_Step not implemented");
433                         break;
434                 case MOVETYPE_WALK:
435                         error("SV_Physics_Walk not implemented");
436                         break;
437                 case MOVETYPE_TOSS:
438                 case MOVETYPE_BOUNCE:
439                 case MOVETYPE_BOUNCEMISSILE:
440                 case MOVETYPE_FLYMISSILE:
441                 case MOVETYPE_FLY:
442                         _Movetype_Physics_Toss(movedt);
443                         break;
444         }
445 }
446
447 void Movetype_Physics_NoMatchServer() // optimized
448 {
449         float movedt;
450
451         movedt = time - self.move_time;
452         self.move_time = time;
453
454         _Movetype_Physics_Frame(movedt);
455         if(wasfreed(self))
456                 return;
457
458         self.avelocity = self.move_avelocity;
459         self.velocity = self.move_velocity;
460         self.angles = self.move_angles;
461         setorigin(self, self.move_origin);
462 }
463
464 void Movetype_Physics_MatchServer(float sloppy)
465 {
466         Movetype_Physics_MatchTicrate(ticrate, sloppy);
467 }
468
469 void Movetype_Physics_MatchTicrate(float tr, float sloppy) // SV_Physics_Entity
470 {
471         float n, i, dt, movedt;
472
473         if(tr <= 0)
474         {
475                 Movetype_Physics_NoMatchServer();
476                 return;
477         }
478
479         dt = time - self.move_time;
480
481         movedt = tr;
482         n = max(0, floor(dt / tr));
483         dt -= n * tr;
484         self.move_time += n * tr;
485
486         if(!self.move_didgravity)
487                 self.move_didgravity = ((self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS) && !(self.move_flags & FL_ONGROUND));
488
489         for(i = 0; i < n; ++i)
490         {
491                 _Movetype_Physics_Frame(movedt);
492                 if(wasfreed(self))
493                         return;
494         }
495
496         self.avelocity = self.move_avelocity;
497
498         if(dt > 0 && self.move_movetype != MOVETYPE_NONE && !(self.move_flags & FL_ONGROUND))
499         {
500                 // now continue the move from move_time to time
501                 self.velocity = self.move_velocity;
502
503                 if(self.move_didgravity > 0)
504                 {
505                         if(GRAVITY_UNAFFECTED_BY_TICRATE)
506                         {
507                                 if(self.gravity)
508                                         self.velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
509                                 else
510                                         self.velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
511                         }
512                         else
513                         {
514                                 if(self.gravity)
515                                         self.velocity_z -= dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
516                                 else
517                                         self.velocity_z -= dt * getstatf(STAT_MOVEVARS_GRAVITY);
518                         }
519                 }
520
521                 self.angles = self.move_angles + dt * self.avelocity;
522
523                 if(sloppy || self.movetype == MOVETYPE_NOCLIP)
524                 {
525                         setorigin(self, self.move_origin + dt * self.velocity);
526                 }
527                 else
528                 {
529                         _Movetype_PushEntityTrace(dt * self.velocity);
530                         if(!trace_startsolid)
531                                 setorigin(self, trace_endpos);
532                 }
533
534                 if(self.move_didgravity > 0)
535                 {
536                         if(GRAVITY_UNAFFECTED_BY_TICRATE)
537                         {
538                                 if(self.gravity)
539                                         self.velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
540                                 else
541                                         self.velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
542                         }
543                 }
544         }
545         else
546         {
547                 self.velocity = self.move_velocity;
548                 self.angles = self.move_angles;
549                 setorigin(self, self.move_origin);
550         }
551 }