]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/mutators/mutator/bugrigs/bugrigs.qc
take3: format 903 files
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / mutators / mutator / bugrigs / bugrigs.qc
1 #include "bugrigs.qh"
2
3 #ifdef GAMEQC
4
5 #ifdef SVQC
6 #include <server/antilag.qh>
7 #endif
8 #include <common/physics/player.qh>
9
10
11 #if defined(SVQC)
12 void bugrigs_SetVars();
13
14 REGISTER_MUTATOR(bugrigs, cvar("g_bugrigs"))
15 {
16         MUTATOR_ONADD
17         {
18                 bugrigs_SetVars();
19         }
20         return false;
21 }
22 #elif defined(CSQC)
23 REGISTER_MUTATOR(bugrigs, true);
24 #endif
25
26
27 #define PHYS_BUGRIGS(s) STAT(BUGRIGS, s)
28 #define PHYS_BUGRIGS_ACCEL(s) STAT(BUGRIGS_ACCEL, s)
29 #define PHYS_BUGRIGS_AIR_STEERING(s) STAT(BUGRIGS_AIR_STEERING, s)
30 #define PHYS_BUGRIGS_ANGLE_SMOOTHING(s) STAT(BUGRIGS_ANGLE_SMOOTHING, s)
31 #define PHYS_BUGRIGS_CAR_JUMPING(s) STAT(BUGRIGS_CAR_JUMPING, s)
32 #define PHYS_BUGRIGS_FRICTION_AIR(s) STAT(BUGRIGS_FRICTION_AIR, s)
33 #define PHYS_BUGRIGS_FRICTION_BRAKE(s) STAT(BUGRIGS_FRICTION_BRAKE, s)
34 #define PHYS_BUGRIGS_FRICTION_FLOOR(s) STAT(BUGRIGS_FRICTION_FLOOR, s)
35 #define PHYS_BUGRIGS_PLANAR_MOVEMENT(s) STAT(BUGRIGS_PLANAR_MOVEMENT, s)
36 #define PHYS_BUGRIGS_REVERSE_SPEEDING(s) STAT(BUGRIGS_REVERSE_SPEEDING, s)
37 #define PHYS_BUGRIGS_REVERSE_SPINNING(s) STAT(BUGRIGS_REVERSE_SPINNING, s)
38 #define PHYS_BUGRIGS_REVERSE_STOPPING(s) STAT(BUGRIGS_REVERSE_STOPPING, s)
39 #define PHYS_BUGRIGS_SPEED_POW(s) STAT(BUGRIGS_SPEED_POW, s)
40 #define PHYS_BUGRIGS_SPEED_REF(s) STAT(BUGRIGS_SPEED_REF, s)
41 #define PHYS_BUGRIGS_STEER(s) STAT(BUGRIGS_STEER, s)
42
43 #if defined(SVQC)
44
45 void bugrigs_SetVars()
46 {
47         g_bugrigs = cvar("g_bugrigs");
48         g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
49         g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
50         g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
51         g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
52         g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
53         g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
54         g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
55         g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
56         g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
57         g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
58         g_bugrigs_accel = cvar("g_bugrigs_accel");
59         g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
60         g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
61         g_bugrigs_steer = cvar("g_bugrigs_steer");
62 }
63
64 #endif
65
66 float racecar_angle(float forward, float down)
67 {
68         if (forward < 0) {
69                 forward = -forward;
70                 down = -down;
71         }
72
73         float ret = vectoyaw('0 1 0' * down + '1 0 0' * forward);
74
75         float angle_mult = forward / (800 + forward);
76
77         if (ret > 180) {
78                 return ret * angle_mult + 360 * (1 - angle_mult);
79         } else {
80                 return ret * angle_mult;
81         }
82 }
83
84 void RaceCarPhysics(entity this, float dt)
85 {
86         // using this move type for "big rigs"
87         // the engine does not push the entity!
88
89         vector rigvel;
90
91         vector angles_save = this.angles;
92         float accel = bound(-1, PHYS_CS(this).movement.x / PHYS_MAXSPEED(this), 1);
93         float steer = bound(-1, PHYS_CS(this).movement.y / PHYS_MAXSPEED(this), 1);
94
95         if (PHYS_BUGRIGS_REVERSE_SPEEDING(this)) {
96                 if (accel < 0) {
97                         // back accel is DIGITAL
98                         // to prevent speedhack
99                         if (accel < -0.5) {
100                                 accel = -1;
101                         } else {
102                                 accel = 0;
103                         }
104                 }
105         }
106
107         this.angles_x = 0;
108         this.angles_z = 0;
109         makevectors(this.angles); // new forward direction!
110
111         if (IS_ONGROUND(this) || PHYS_BUGRIGS_AIR_STEERING(this)) {
112                 float myspeed = this.velocity * v_forward;
113                 float upspeed = this.velocity * v_up;
114
115                 // responsiveness factor for steering and acceleration
116                 float f = 1 / (1 + POW((max(-myspeed, myspeed) / PHYS_BUGRIGS_SPEED_REF(this)), PHYS_BUGRIGS_SPEED_POW(this)));
117                 // MAXIMA: f(v) := 1 / (1 + (v / PHYS_BUGRIGS_SPEED_REF(this)) ^ PHYS_BUGRIGS_SPEED_POW(this));
118
119                 float steerfactor;
120                 if (myspeed < 0 && PHYS_BUGRIGS_REVERSE_SPINNING(this)) {
121                         steerfactor = -myspeed * PHYS_BUGRIGS_STEER(this);
122                 } else {
123                         steerfactor = -myspeed * f * PHYS_BUGRIGS_STEER(this);
124                 }
125
126                 float accelfactor;
127                 if (myspeed < 0 && PHYS_BUGRIGS_REVERSE_SPEEDING(this)) {
128                         accelfactor = PHYS_BUGRIGS_ACCEL(this);
129                 } else {
130                         accelfactor = f * PHYS_BUGRIGS_ACCEL(this);
131                 }
132                 // MAXIMA: accel(v) := f(v) * PHYS_BUGRIGS_ACCEL(this);
133
134                 if (accel < 0) {
135                         if (myspeed > 0) {
136                                 myspeed = max(0, myspeed - dt * (PHYS_BUGRIGS_FRICTION_FLOOR(this) - PHYS_BUGRIGS_FRICTION_BRAKE(this) * accel));
137                         } else {
138                                 if (!PHYS_BUGRIGS_REVERSE_SPEEDING(this)) {
139                                         myspeed = min(0, myspeed + dt * PHYS_BUGRIGS_FRICTION_FLOOR(this));
140                                 }
141                         }
142                 } else {
143                         if (myspeed >= 0) {
144                                 myspeed = max(0, myspeed - dt * PHYS_BUGRIGS_FRICTION_FLOOR(this));
145                         } else {
146                                 if (PHYS_BUGRIGS_REVERSE_STOPPING(this)) {
147                                         myspeed = 0;
148                                 } else {
149                                         myspeed = min(0, myspeed + dt * (PHYS_BUGRIGS_FRICTION_FLOOR(this) + PHYS_BUGRIGS_FRICTION_BRAKE(this) * accel));
150                                 }
151                         }
152                 }
153                 // terminal velocity = velocity at which 50 == accelfactor, that is, 1549 units/sec
154                 // MAXIMA: friction(v) := PHYS_BUGRIGS_FRICTION_FLOOR(this);
155
156                 this.angles_y += steer * dt * steerfactor; // apply steering
157                 makevectors(this.angles);                  // new forward direction!
158
159                 myspeed += accel * accelfactor * dt;
160
161                 rigvel = myspeed * v_forward + '0 0 1' * upspeed;
162         } else {
163                 float myspeed = vlen(this.velocity);
164
165                 // responsiveness factor for steering and acceleration
166                 float f = 1 / (1 + POW(max(0, myspeed / PHYS_BUGRIGS_SPEED_REF(this)), PHYS_BUGRIGS_SPEED_POW(this)));
167                 float steerfactor = -myspeed * f;
168                 this.angles_y += steer * dt * steerfactor; // apply steering
169
170                 rigvel = this.velocity;
171                 makevectors(this.angles);                  // new forward direction!
172         }
173
174         rigvel *= max(0, 1 - vlen(rigvel) * PHYS_BUGRIGS_FRICTION_AIR(this) * dt);
175         // MAXIMA: airfriction(v) := v * v * PHYS_BUGRIGS_FRICTION_AIR(this);
176         // MAXIMA: total_acceleration(v) := accel(v) - friction(v) - airfriction(v);
177         // MAXIMA: solve(total_acceleration(v) = 0, v);
178
179         if (PHYS_BUGRIGS_PLANAR_MOVEMENT(this)) {
180                 vector rigvel_xy, neworigin, up;
181                 float mt;
182
183                 rigvel_z -= dt * PHYS_GRAVITY(this); // 4x gravity plays better
184                 rigvel_xy = vec2(rigvel);
185
186                 if (PHYS_BUGRIGS_CAR_JUMPING(this)) {
187                         mt = MOVE_NORMAL;
188                 } else {
189                         mt = MOVE_NOMONSTERS;
190                 }
191
192                 tracebox(this.origin, this.mins, this.maxs, this.origin + '0 0 1024', mt, this);
193                 up = trace_endpos - this.origin;
194
195                 // BUG RIGS: align the move to the surface instead of doing collision testing
196                 // can we move?
197                 tracebox(trace_endpos, this.mins, this.maxs, trace_endpos + rigvel_xy * dt, mt, this);
198
199                 // align to surface
200                 tracebox(trace_endpos, this.mins, this.maxs, trace_endpos - up + '0 0 1' * rigvel_z * dt, mt, this);
201
202                 if (trace_fraction < 0.5) {
203                         trace_fraction = 1;
204                         neworigin = this.origin;
205                 } else {
206                         neworigin = trace_endpos;
207                 }
208
209                 if (trace_fraction < 1) {
210                         // now set angles_x so that the car points parallel to the surface
211                         this.angles = vectoangles(
212                                 '1 0 0' * v_forward.x * trace_plane_normal.z
213                                 +
214                                 '0 1 0' * v_forward.y * trace_plane_normal.z
215                                 +
216                                 '0 0 1' * -(v_forward.x * trace_plane_normal.x + v_forward.y * trace_plane_normal.y)
217                                 );
218                         SET_ONGROUND(this);
219                 } else {
220                         // now set angles_x so that the car points forward, but is tilted in velocity direction
221                         UNSET_ONGROUND(this);
222                 }
223
224                 this.velocity = (neworigin - this.origin) * (1.0 / dt);
225                 set_movetype(this, MOVETYPE_NOCLIP);
226         } else {
227                 rigvel_z -= dt * PHYS_GRAVITY(this); // 4x gravity plays better
228                 this.velocity = rigvel;
229                 set_movetype(this, MOVETYPE_FLY);
230         }
231
232         trace_fraction = 1;
233         tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 4', MOVE_NORMAL, this);
234         if (trace_fraction != 1) {
235                 this.angles = vectoangles2(
236                         '1 0 0' * v_forward.x * trace_plane_normal.z
237                         +
238                         '0 1 0' * v_forward.y * trace_plane_normal.z
239                         +
240                         '0 0 1' * -(v_forward.x * trace_plane_normal.x + v_forward.y * trace_plane_normal.y),
241                         trace_plane_normal
242                         );
243         } else {
244                 vector vel_local;
245
246                 vel_local.x = v_forward * this.velocity;
247                 vel_local.y = v_right * this.velocity;
248                 vel_local.z = v_up * this.velocity;
249
250                 this.angles_x = racecar_angle(vel_local.x, vel_local.z);
251                 this.angles_z = racecar_angle(-vel_local.y, vel_local.z);
252         }
253
254         // smooth the angles
255         vector vf1, vu1, smoothangles;
256         makevectors(this.angles);
257         float f = bound(0, dt * PHYS_BUGRIGS_ANGLE_SMOOTHING(this), 1);
258         if (f == 0) {
259                 f = 1;
260         }
261         vf1 = v_forward * f;
262         vu1 = v_up * f;
263         makevectors(angles_save);
264         vf1 = vf1 + v_forward * (1 - f);
265         vu1 = vu1 + v_up * (1 - f);
266         smoothangles = vectoangles2(vf1, vu1);
267         this.angles_x = -smoothangles.x;
268         this.angles_z =  smoothangles.z;
269 }
270
271 #ifdef SVQC
272 .vector bugrigs_prevangles;
273 #endif
274 MUTATOR_HOOKFUNCTION(bugrigs, PM_Physics)
275 {
276         entity player = M_ARGV(0, entity);
277         float dt = M_ARGV(2, float);
278
279         if (!PHYS_BUGRIGS(player) || !IS_PLAYER(player)) { return; }
280
281 #ifdef SVQC
282         player.angles = player.bugrigs_prevangles;
283 #endif
284
285         RaceCarPhysics(player, dt);
286         return true;
287 }
288
289 MUTATOR_HOOKFUNCTION(bugrigs, PlayerPhysics)
290 {
291         if (!PHYS_BUGRIGS(M_ARGV(0, entity))) { return; }
292 #ifdef SVQC
293         entity player = M_ARGV(0, entity);
294         player.bugrigs_prevangles = player.angles;
295 #endif
296 }
297
298 #ifdef SVQC
299
300 MUTATOR_HOOKFUNCTION(bugrigs, ClientConnect)
301 {
302         entity player = M_ARGV(0, entity);
303
304         stuffcmd(player, "cl_cmd settemp chase_active 1\n");
305 }
306
307 MUTATOR_HOOKFUNCTION(bugrigs, BuildMutatorsString)
308 {
309         M_ARGV(0, string) = strcat(M_ARGV(0, string), ":bugrigs");
310 }
311
312 MUTATOR_HOOKFUNCTION(bugrigs, BuildMutatorsPrettyString)
313 {
314         M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Bug rigs");
315 }
316
317 #endif
318
319 #endif