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