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