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