]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/ecs/systems/physics.qc
Support noclip + fly
[xonotic/xonotic-data.pk3dir.git] / qcsrc / ecs / systems / physics.qc
1 #include "physics.qh"
2
3 const int PHYSICS_TRACE_PLANE_MAX = 5;
4
5 // NOTE: currently unsuitable for players
6 void sys_phys_update(entity this, float dt)
7 {
8         // x: { 60: 0.5, 45: ~0.7, 30: ~0.9 }, from: 'vec2(0, 1) * vec2(cos(90 - x), sin(90 - x))'
9         // read as 'within x degrees'
10         float maxgrounddot = 0.5;
11         float maxstepdot = 0.7;
12         vector upvec = '0 0 1';
13         float groundsnap = 1;
14         bool jump = this.com_in_jump;
15         bool jumpstep = true;
16
17         vector mn = this.mins;
18         vector mx = this.maxs;
19
20         vector acc = this.com_phys_acc;
21         vector vel = this.com_phys_vel;
22         vector pos = this.com_phys_pos_prev = this.com_phys_pos;
23         bool onground = this.com_phys_grounded;
24
25         bool nogravityonground = this.com_phys_nogravityonground;
26         float stepheight = this.com_phys_stepheight;
27         float stepdownheight = -stepheight;
28         float jumpvel = this.com_phys_jumpvel;
29         float bounce = this.com_phys_bounce;
30         float friction = this.com_phys_friction;
31         float gravity = this.com_phys_gravity;
32         bool noclip = this.com_phys_noclip;
33         if (noclip) {
34             jump = false;
35             nogravityonground = false;
36         }
37
38         vector g = upvec * -gravity;
39
40         // apply accelaration in two steps: https://www.niksula.hut.fi/~hkankaan/Homepages/gravity.html
41         // alternatives: rk4, verlet, euler
42         vel += (acc + g) * dt / 2;
43         {
44                 if (onground || noclip)
45                 {
46                         if (nogravityonground)
47                         {
48                 g = '0 0 0';
49                             if (vel * upvec < 0) vel = vec_reflect(vel, upvec, 0);  // kill downward velocity
50                         }
51                         if (jump)
52                         {
53                                 vel += upvec * jumpvel;
54                         }
55                         else  // the first landing frame is free
56                         {
57                                 // friction
58                                 vector slide = noclip ? vel : vec_reflect(vel, upvec, 0);
59                                 vector push = vel - slide;
60                                 // TODO: slick
61                                 slide *= 1 - friction * dt;
62                                 vel = slide + push;
63                         }
64                 }
65                 vector step = vel * dt;
66                 bool pass = false;
67                 bool foundground = false;                  // assume until proven otherwise
68                 if (nogravityonground) foundground = true; // override
69                 bool steplimit = 1;
70                 if (noclip) pass = true; else
71                 for (int i = 0; i < PHYSICS_TRACE_PLANE_MAX; ++i)
72                 {
73                         vector p0 = pos;
74                         vector p1 = p0 + step;
75                         tracebox(p0, mn, mx, p1, MOVE_NORMAL, this);
76                         float frac = trace_fraction;
77                         vector norm = trace_plane_normal;
78                         if (frac == 1)
79                         {
80                                 // all clear
81                                 if (steplimit > 0 && onground && vel * upvec <= 0)
82                                 {
83                                         // can we step down?
84                                         tracebox(p1, mn, mx, p1 + upvec * stepdownheight, MOVE_NORMAL, this);
85                                         if (trace_fraction == 1)
86                                         {
87                                                 // no stairs here
88                                         }
89                                         else if (trace_plane_normal * upvec >= maxstepdot)
90                                         {
91                                                 // step down
92                                                 step += upvec * (stepdownheight * trace_fraction);
93                                         }
94                                 }
95                                 pass = true;
96                                 break;
97                         }
98                         // hit something
99                         if (norm * upvec >= maxgrounddot) foundground = true;
100                         if (steplimit > 0 && (jumpstep || onground))    // try: vel * upvec >= 0
101                         {
102                                 // can we step up?
103                                 vector slide = vec_reflect(step, upvec, 0); // remove fall component
104                                 vector p1 = p0 + slide;                     // step is here
105                                 tracebox(p1 + upvec * stepheight, mn, mx, p1, MOVE_NORMAL, this);
106                                 if (trace_fraction < 1 && trace_plane_normal * upvec >= maxstepdot)
107                                 {
108                                         // there is a step in front of us, get above it
109                                         // TODO: not if it's slippery (slick)
110                                         vector stepup = upvec * (1 - trace_fraction) * stepheight;
111                                         tracebox(p0, mn, mx, p0 + stepup, MOVE_NORMAL, this);
112                                         if (trace_fraction == 1)
113                                         {
114                                                 // go over
115                                                 tracebox(p0 + stepup, mn, mx, p1 + stepup, MOVE_NORMAL, this);
116                                                 if (trace_fraction == 1)
117                                                 {
118                                                         // all clear
119                                                         steplimit -= 1;
120                                                         pos += stepup;
121                                                         if (vel * upvec < 0) vel = vec_reflect(vel, upvec, 0);  // kill downward velocity
122                                                         step = p1 - p0;
123                                                         pass = true;
124                                                         break;
125                                                 }
126                                         }
127                                 }
128                         }
129                         // no stairs here
130                         pos += frac * step;
131                         vel = vec_reflect(vel, norm, bounce);
132                         step = (1 - frac) * vel * dt;
133                         continue;
134                 }
135                 if (nogravityonground)
136                 {
137                         vector p1 = pos + step;
138                         tracebox(p1, mn, mx, p1 - groundsnap * upvec, MOVE_NORMAL, this);
139                         foundground = trace_plane_normal * upvec >= maxgrounddot;
140                 }
141                 if (pass)
142                 {
143                         pos += step;
144                         if (!foundground)
145                         {
146                                 if (onground) emit(phys_stepfall, this);
147                         }
148                         else
149                         {
150                                 if (!onground) emit(phys_stepland, this);
151                         }
152                         onground = foundground;
153                 }
154         }
155         vel += (acc + g) * dt / 2;
156
157         this.com_phys_acc = acc;
158         this.com_phys_vel = vel;
159         this.com_phys_pos = pos;
160         this.com_phys_grounded = onground;
161 }