Merge branch 'master' into terencehill/menu_hudskin_selector
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / movetypes / movetypes.qc
1 #include "include.qh"
2 #include "../physics.qh"
3
4 #if defined(CSQC)
5         #include "../../client/defs.qh"
6         #include "../stats.qh"
7         #include "../util.qh"
8         #include "movetypes.qh"
9         #include "../../lib/csqcmodel/common.qh"
10         #include "../../server/t_items.qh"
11 #elif defined(MENUQC)
12 #elif defined(SVQC)
13         #include "../../server/autocvars.qh"
14 #endif
15
16 void _Movetype_WallFriction(entity this, vector stepnormal)  // SV_WallFriction
17 {
18         /*float d, i;
19         vector into, side;
20         makevectors(this.v_angle);
21         d = (stepnormal * v_forward) + 0.5;
22
23         if(d < 0)
24         {
25             i = (stepnormal * this.move_velocity);
26             into = i * stepnormal;
27             side = this.move_velocity - into;
28             this.move_velocity_x = side.x * (1 * d);
29             this.move_velocity_y = side.y * (1 * d);
30         }*/
31 }
32
33 vector planes[MAX_CLIP_PLANES];
34 int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnormal, float stepheight) // SV_FlyMove
35 {
36         int blocked = 0, bumpcount;
37         int i, j, numplanes = 0;
38         float time_left = dt, grav = 0;
39         vector push;
40         vector primal_velocity, original_velocity, restore_velocity;
41
42         for(i = 0; i < MAX_CLIP_PLANES; ++i)
43                 planes[i] = '0 0 0';
44
45         if(applygravity)
46         {
47                 this.move_didgravity = 1;
48                 grav = dt * (PHYS_ENTGRAVITY(this) ? PHYS_ENTGRAVITY(this) : 1) * PHYS_GRAVITY;
49
50                 if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(this.move_flags & FL_ONGROUND))
51                 {
52                         if(GRAVITY_UNAFFECTED_BY_TICRATE)
53                                 this.move_velocity_z -= grav * 0.5;
54                         else
55                                 this.move_velocity_z -= grav;
56                 }
57         }
58
59         original_velocity = primal_velocity = restore_velocity = this.move_velocity;
60
61         for(bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
62         {
63                 if(this.move_velocity == '0 0 0')
64                         break;
65
66                 push = this.move_velocity * time_left;
67                 vector prev_origin = this.move_origin;
68                 _Movetype_PushEntity(this, push, true);
69                 if(trace_startsolid && this.move_origin != prev_origin)
70                 {
71                         // we got teleported by a touch function
72                         // let's abort the move
73                         blocked |= 8;
74                         break;
75                 }
76
77                 // this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity
78                 // abort move if we're stuck in the world (and didn't make it out)
79                 if(trace_startsolid && trace_allsolid)
80                 {
81                         this.move_velocity = restore_velocity;
82                         return 3;
83                 }
84
85                 if(trace_fraction == 1)
86                         break;
87
88                 float my_trace_fraction = trace_fraction;
89                 vector my_trace_plane_normal = trace_plane_normal;
90
91                 if(trace_plane_normal_z)
92                 {
93                         if(trace_plane_normal_z > 0.7)
94                         {
95                                 // floor
96                                 blocked |= 1;
97
98                                 if(!trace_ent)
99                                 {
100                                         //dprint("_Movetype_FlyMove: !trace_ent\n");
101                                         trace_ent = world;
102                                 }
103
104                                 this.move_flags |= FL_ONGROUND;
105                                 this.move_groundentity = trace_ent;
106                         }
107                 }
108                 else if(stepheight)
109                 {
110                         // step - handle it immediately
111                         vector org = this.move_origin;
112                         vector steppush = '0 0 1' * stepheight;
113
114                         _Movetype_PushEntity(this, steppush, true);
115                         if(trace_startsolid && this.move_origin != org)
116                         {
117                                 blocked |= 8;
118                                 break;
119                         }
120                         _Movetype_PushEntity(this, push, true);
121                         if(trace_startsolid && this.move_origin != org)
122                         {
123                                 blocked |= 8;
124                                 break;
125                         }
126                         float trace2_fraction = trace_fraction;
127                         steppush = '0 0 1' * (org_z - this.move_origin_z);
128                         _Movetype_PushEntity(this, steppush, true);
129                         if(trace_startsolid && this.move_origin != org)
130                         {
131                                 blocked |= 8;
132                                 break;
133                         }
134
135                         // accept the new position if it made some progress...
136                         if(fabs(this.move_origin_x - org_x) >= 0.03125 || fabs(this.move_origin_y - org_y) >= 0.03125)
137                         {
138                                 trace_endpos = this.move_origin;
139                                 time_left *= 1 - trace2_fraction;
140                                 numplanes = 0;
141                                 continue;
142                         }
143                         else
144                                 this.move_origin = org;
145                 }
146                 else
147                 {
148                         // step - return it to caller
149                         blocked |= 2;
150                         // save the trace for player extrafriction
151                         if(stepnormal)
152                                 stepnormal = trace_plane_normal;
153                 }
154
155                 if(my_trace_fraction >= 0.001)
156                 {
157                         // actually covered some distance
158                         original_velocity = this.move_velocity;
159                         numplanes = 0;
160                 }
161
162                 time_left *= 1 - my_trace_fraction;
163
164                 // clipped to another plane
165                 if(numplanes >= MAX_CLIP_PLANES)
166                 {
167                         // this shouldn't really happen
168                         this.move_velocity = '0 0 0';
169                         blocked = 3;
170                         break;
171                 }
172
173                 planes[numplanes] = my_trace_plane_normal;
174                 numplanes++;
175
176                 // modify original_velocity so it parallels all of the clip planes
177                 vector new_velocity = '0 0 0';
178                 for (i = 0;i < numplanes;i++)
179                 {
180                         new_velocity = _Movetype_ClipVelocity(original_velocity, planes[i], 1);
181                         for (j = 0;j < numplanes;j++)
182                         {
183                                 if(j != i)
184                                 {
185                                         // not ok
186                                         if((new_velocity * planes[j]) < 0)
187                                                 break;
188                                 }
189                         }
190                         if(j == numplanes)
191                                 break;
192                 }
193
194                 if(i != numplanes)
195                 {
196                         // go along this plane
197                         this.move_velocity = new_velocity;
198                 }
199                 else
200                 {
201                         // go along the crease
202                         if(numplanes != 2)
203                         {
204                                 this.move_velocity = '0 0 0';
205                                 blocked = 7;
206                                 break;
207                         }
208                         vector dir = cross(planes[0], planes[1]);
209                         // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
210                         float ilength = sqrt((dir * dir));
211                         if(ilength)
212                                 ilength = 1.0 / ilength;
213                         dir.x *= ilength;
214                         dir.y *= ilength;
215                         dir.z *= ilength;
216                         float d = (dir * this.move_velocity);
217                         this.move_velocity = dir * d;
218                 }
219
220                 // if current velocity is against the original velocity,
221                 // stop dead to avoid tiny occilations in sloping corners
222                 if((this.move_velocity * primal_velocity) <= 0)
223                 {
224                         this.move_velocity = '0 0 0';
225                         break;
226                 }
227         }
228
229         // LordHavoc: this came from QW and allows you to get out of water more easily
230         if(GAMEPLAYFIX_EASIERWATERJUMP && (this.move_flags & FL_WATERJUMP) && !(blocked & 8))
231                 this.move_velocity = primal_velocity;
232
233         if(applygravity)
234         {
235                 if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(this.move_flags & FL_ONGROUND))
236                 {
237                         if(GRAVITY_UNAFFECTED_BY_TICRATE)
238                                 this.move_velocity_z -= grav * 0.5f;
239                 }
240         }
241
242         return blocked;
243 }
244
245 void _Movetype_CheckVelocity(entity this)  // SV_CheckVelocity
246 {
247         // if(vlen(this.move_velocity) < 0.0001)
248         // this.move_velocity = '0 0 0';
249 }
250
251 bool _Movetype_CheckWater(entity ent)  // SV_CheckWater
252 {
253         vector point = ent.move_origin;
254         point.z += (ent.mins.z + 1);
255
256         int nativecontents = pointcontents(point);
257         if(ent.move_watertype && ent.move_watertype != nativecontents)
258         {
259                 // dprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", ent.move_watertype, nativecontents);
260                 if(ent.contentstransition)
261                         ent.contentstransition(ent.move_watertype, nativecontents);
262         }
263
264         ent.move_waterlevel = 0;
265         ent.move_watertype = CONTENT_EMPTY;
266
267         int supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents);
268         if(supercontents & DPCONTENTS_LIQUIDSMASK)
269         {
270                 ent.move_watertype = nativecontents;
271                 ent.move_waterlevel = 1;
272                 point.y = (ent.origin.y + ((ent.mins.z + ent.maxs.y) * 0.5));
273                 if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
274                 {
275                         ent.move_waterlevel = 2;
276                         point.y = ent.origin.y + ent.view_ofs.y;
277                         if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
278                                 ent.move_waterlevel = 3;
279                 }
280         }
281
282         return ent.move_waterlevel > 1;
283 }
284
285 void _Movetype_CheckWaterTransition(entity ent)  // SV_CheckWaterTransition
286 {
287         int contents = pointcontents(ent.move_origin);
288
289         if(!ent.move_watertype)
290         {
291                 // just spawned here
292                 if(!autocvar_cl_gameplayfix_fixedcheckwatertransition)
293                 {
294                         ent.move_watertype = contents;
295                         ent.move_waterlevel = 1;
296                         return;
297                 }
298         }
299         else if(ent.move_watertype != contents)
300         {
301                 // dprintf("_Movetype_CheckWaterTransition(): Origin: %s, Direct: '%d', Original: '%d', New: '%d'\n", vtos(ent.move_origin), pointcontents(ent.move_origin), ent.move_watertype, contents);
302                 if(ent.contentstransition)
303                         ent.contentstransition(ent.move_watertype, contents);
304         }
305
306         if(contents <= CONTENT_WATER)
307         {
308                 ent.move_watertype = contents;
309                 ent.move_waterlevel = 1;
310         }
311         else
312         {
313                 ent.move_watertype = CONTENT_EMPTY;
314                 ent.move_waterlevel = (autocvar_cl_gameplayfix_fixedcheckwatertransition ? 0 : contents);
315         }
316 }
317
318 void _Movetype_Impact(entity this, entity oth)  // SV_Impact
319 {
320         entity oldother = other;
321
322         if(this.move_touch)
323         {
324                 other = oth;
325
326                 WITH(entity, self, this, this.move_touch());
327
328                 other = oldother;
329         }
330
331         if(oth.move_touch)
332         {
333                 other = this;
334
335                 WITH(entity, self, oth, oth.move_touch());
336
337                 other = oldother;
338         }
339 }
340
341 void _Movetype_LinkEdict_TouchAreaGrid(entity this)  // SV_LinkEdict_TouchAreaGrid
342 {
343         entity oldother = other;
344
345         for (entity e = findradius(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin)); e; e = e.chain)
346         {
347                 if(e.move_touch && boxesoverlap(e.absmin, e.absmax, this.absmin, this.absmax))
348                 {
349                         other = this;
350
351                         trace_allsolid = false;
352                         trace_startsolid = false;
353                         trace_fraction = 1;
354                         trace_inwater = false;
355                         trace_inopen = true;
356                         trace_endpos = e.origin;
357                         trace_plane_normal = '0 0 1';
358                         trace_plane_dist = 0;
359                         trace_ent = this;
360
361                         WITH(entity, self, e, e.move_touch());
362                 }
363         }
364
365         other = oldother;
366 }
367
368 void _Movetype_LinkEdict(entity this, bool touch_triggers)  // SV_LinkEdict
369 {
370         vector mi, ma;
371         if(this.solid == SOLID_BSP)
372         {
373                 // TODO set the absolute bbox
374                 mi = this.mins;
375                 ma = this.maxs;
376         }
377         else
378         {
379                 mi = this.mins;
380                 ma = this.maxs;
381         }
382         mi += this.move_origin;
383         ma += this.move_origin;
384
385         if(this.move_flags & FL_ITEM)
386         {
387                 mi.x -= 15;
388                 mi.y -= 15;
389                 mi.z -= 1;
390                 ma.x += 15;
391                 ma.y += 15;
392                 ma.z += 1;
393         }
394         else
395         {
396                 mi.x -= 1;
397                 mi.y -= 1;
398                 mi.z -= 1;
399                 ma.x += 1;
400                 ma.y += 1;
401                 ma.z += 1;
402         }
403
404         this.absmin = mi;
405         this.absmax = ma;
406
407         if(touch_triggers)
408                 _Movetype_LinkEdict_TouchAreaGrid(this);
409 }
410
411 bool _Movetype_TestEntityPosition(entity this, vector ofs)  // SV_TestEntityPosition
412 {
413 //      vector org = this.move_origin + ofs;
414
415         int cont = this.dphitcontentsmask;
416         this.dphitcontentsmask = DPCONTENTS_SOLID;
417         tracebox(this.move_origin, this.mins, this.maxs, this.move_origin, MOVE_NOMONSTERS, this);
418         this.dphitcontentsmask = cont;
419
420         if(trace_startsolid)
421                 return true;
422
423         if(vlen(trace_endpos - this.move_origin) > 0.0001)
424                 this.move_origin = trace_endpos;
425         return false;
426 }
427
428 bool _Movetype_UnstickEntity(entity this)  // SV_UnstickEntity
429 {
430         if(!_Movetype_TestEntityPosition(this, '0 0 0')) return true;
431         if(!_Movetype_TestEntityPosition(this, '-1 0 0')) goto success;
432         if(!_Movetype_TestEntityPosition(this, '1 0 0')) goto success;
433         if(!_Movetype_TestEntityPosition(this, '0 -1 0')) goto success;
434         if(!_Movetype_TestEntityPosition(this, '0 1 0')) goto success;
435         if(!_Movetype_TestEntityPosition(this, '-1 -1 0')) goto success;
436         if(!_Movetype_TestEntityPosition(this, '1 -1 0')) goto success;
437         if(!_Movetype_TestEntityPosition(this, '-1 1 0')) goto success;
438         if(!_Movetype_TestEntityPosition(this, '1 1 0')) goto success;
439         for (int i = 1; i <= 17; ++i)
440         {
441                 if(!_Movetype_TestEntityPosition(this, '0 0 -1' * i)) goto success;
442                 if(!_Movetype_TestEntityPosition(this, '0 0 1' * i)) goto success;
443         }
444         LOG_TRACEF("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n",
445                 num_for_edict(this), this.classname, vtos(this.move_origin));
446         return false;
447         : success;
448         LOG_TRACEF("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n",
449                 num_for_edict(this), this.classname, vtos(this.move_origin));
450         _Movetype_LinkEdict(this, true);
451         return true;
452 }
453
454 vector _Movetype_ClipVelocity(vector vel, vector norm, float f)  // SV_ClipVelocity
455 {
456         vel -= ((vel * norm) * norm) * f;
457
458         if(vel.x > -0.1 && vel.x < 0.1) vel.x = 0;
459         if(vel.y > -0.1 && vel.y < 0.1) vel.y = 0;
460         if(vel.z > -0.1 && vel.z < 0.1) vel.z = 0;
461
462         return vel;
463 }
464
465 void _Movetype_PushEntityTrace(entity this, vector push)
466 {
467         vector end = this.move_origin + push;
468         int type;
469         if(this.move_nomonsters)
470                 type = max(0, this.move_nomonsters);
471         else if(this.move_movetype == MOVETYPE_FLYMISSILE)
472                 type = MOVE_MISSILE;
473         else if(this.solid == SOLID_TRIGGER || this.solid == SOLID_NOT)
474                 type = MOVE_NOMONSTERS;
475         else
476                 type = MOVE_NORMAL;
477
478         tracebox(this.move_origin, this.mins, this.maxs, end, type, this);
479 }
480
481 float _Movetype_PushEntity(entity this, vector push, bool failonstartsolid)  // SV_PushEntity
482 {
483         _Movetype_PushEntityTrace(this, push);
484
485         if(trace_startsolid && failonstartsolid)
486                 return trace_fraction;
487
488         this.move_origin = trace_endpos;
489
490         if(trace_fraction < 1)
491                 if(this.solid >= SOLID_TRIGGER && (!(this.move_flags & FL_ONGROUND) || (this.move_groundentity != trace_ent)))
492                         _Movetype_Impact(this, trace_ent);
493
494         return trace_fraction;
495 }
496
497
498 .float ltime;
499 .void() blocked;
500 // matrix version of makevectors, sets v_forward, v_right and v_up
501 void makevectors_matrix(vector myangles)  // AngleVectorsFLU
502 {
503         v_forward = v_right = v_up = '0 0 0';
504
505         float y = myangles.y * (M_PI * 2 / 360);
506         float sy = sin(y);
507         float cy = cos(y);
508         float p = myangles.x * (M_PI * 2 / 360);
509         float sp = sin(p);
510         float cp = cos(p);
511         if(v_forward)
512         {
513                 v_forward.x = cp * cy;
514                 v_forward.y = cp * sy;
515                 v_forward.z = -sp;
516         }
517         if(v_right || v_up)
518         {
519                 if(myangles.z)
520                 {
521                         float r = myangles.z * (M_PI * 2 / 360);
522                         float sr = sin(r);
523                         float cr = cos(r);
524                         if(v_right)
525                         {
526                                 v_right.x = sr * sp * cy + cr * -sy;
527                                 v_right.y = sr * sp * sy + cr * cy;
528                                 v_right.z = sr * cp;
529                         }
530                         if(v_up)
531                         {
532                                 v_up.x = cr * sp * cy + -sr * -sy;
533                                 v_up.y = cr * sp * sy + -sr * cy;
534                                 v_up.z = cr * cp;
535                         }
536                 }
537                 else
538                 {
539                         if(v_right)
540                         {
541                                 v_right.x = -sy;
542                                 v_right.y = cy;
543                                 v_right.z = 0;
544                         }
545                         if(v_up)
546                         {
547                                 v_up.x = sp * cy;
548                                 v_up.y = sp * sy;
549                                 v_up.z = cp;
550                         }
551                 }
552         }
553 }
554
555 void _Movetype_Physics_Frame(entity this, float movedt)
556 {
557         this.move_didgravity = -1;
558         switch (this.move_movetype)
559         {
560                 case MOVETYPE_PUSH:
561                 case MOVETYPE_FAKEPUSH:
562                         _Movetype_Physics_Pusher(this, movedt);
563                         break;
564                 case MOVETYPE_NONE:
565                         break;
566                 case MOVETYPE_FOLLOW:
567                         _Movetype_Physics_Follow(this);
568                         break;
569                 case MOVETYPE_NOCLIP:
570                         _Movetype_CheckWater(this);
571                         this.move_origin = this.move_origin + TICRATE * this.move_velocity;
572                         this.move_angles = this.move_angles + TICRATE * this.move_avelocity;
573                         _Movetype_LinkEdict(this, false);
574                         break;
575                 case MOVETYPE_STEP:
576                         _Movetype_Physics_Step(this, movedt);
577                         break;
578                 case MOVETYPE_WALK:
579                         _Movetype_Physics_Walk(this, movedt);
580                         break;
581                 case MOVETYPE_TOSS:
582                 case MOVETYPE_BOUNCE:
583                 case MOVETYPE_BOUNCEMISSILE:
584                 case MOVETYPE_FLYMISSILE:
585                 case MOVETYPE_FLY:
586                         _Movetype_Physics_Toss(this, movedt);
587                         break;
588         }
589 }
590
591 void Movetype_Physics_NoMatchServer(entity this)  // optimized
592 {
593         float movedt = time - this.move_time;
594         this.move_time = time;
595
596         _Movetype_Physics_Frame(this, movedt);
597         if(wasfreed(this))
598                 return;
599
600         this.avelocity = this.move_avelocity;
601         this.velocity = this.move_velocity;
602         this.angles = this.move_angles;
603         setorigin(this, this.move_origin);
604 }
605
606 void Movetype_Physics_MatchServer(entity this, bool sloppy)
607 {
608         Movetype_Physics_MatchTicrate(this, TICRATE, sloppy);
609 }
610
611 void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy)  // SV_Physics_Entity
612 {
613         if(tr <= 0)
614         {
615                 Movetype_Physics_NoMatchServer(this);
616                 return;
617         }
618
619         float dt = time - this.move_time;
620
621         int n = max(0, floor(dt / tr));
622         dt -= n * tr;
623         this.move_time += n * tr;
624
625         if(!this.move_didgravity)
626                 this.move_didgravity = ((this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS) && !(this.move_flags & FL_ONGROUND));
627
628         for (int i = 0; i < n; ++i)
629         {
630                 _Movetype_Physics_Frame(this, tr);
631                 if(wasfreed(this))
632                         return;
633         }
634
635         this.avelocity = this.move_avelocity;
636
637         if(dt > 0 && this.move_movetype != MOVETYPE_NONE && !(this.move_flags & FL_ONGROUND))
638         {
639                 // now continue the move from move_time to time
640                 this.velocity = this.move_velocity;
641
642                 if(this.move_didgravity > 0)
643                 {
644                         this.velocity_z -= (GRAVITY_UNAFFECTED_BY_TICRATE ? 0.5 : 1)
645                             * dt
646                             * (this.gravity ? this.gravity : 1)
647                             * PHYS_GRAVITY;
648                 }
649
650                 this.angles = this.move_angles + dt * this.avelocity;
651
652                 if(sloppy || this.move_movetype == MOVETYPE_NOCLIP)
653                 {
654                         setorigin(this, this.move_origin + dt * this.velocity);
655                 }
656                 else
657                 {
658                         _Movetype_PushEntityTrace(this, dt * this.velocity);
659                         if(!trace_startsolid)
660                                 setorigin(this, trace_endpos);
661                 }
662
663                 if(this.move_didgravity > 0 && GRAVITY_UNAFFECTED_BY_TICRATE)
664                         this.velocity_z -= 0.5 * dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY;
665         }
666         else
667         {
668                 this.velocity = this.move_velocity;
669                 this.angles = this.move_angles;
670                 setorigin(this, this.move_origin);
671         }
672 }