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