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