Merge branch 'master' into terencehill/menu_hudskin_selector
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / mutators / mutator / bugrigs / bugrigs.qc
1 #ifdef IMPLEMENTATION
2 #ifdef SVQC
3         #include "../../../../server/antilag.qh"
4 #endif
5 #include "../../../physics.qh"
6
7
8 #if defined(SVQC)
9 void bugrigs_SetVars();
10 void bugrigs_AddStats();
11
12 REGISTER_MUTATOR(bugrigs, cvar("g_bugrigs"))
13 {
14         MUTATOR_ONADD
15         {
16                 bugrigs_SetVars();
17                 bugrigs_AddStats();
18         }
19         return false;
20 }
21 #elif defined(CSQC)
22 REGISTER_MUTATOR(bugrigs, true);
23 #endif
24
25
26 #ifdef CSQC
27
28 #define PHYS_BUGRIGS                                            getstati(STAT_BUGRIGS)
29 #define PHYS_BUGRIGS_ANGLE_SMOOTHING            getstati(STAT_BUGRIGS_ANGLE_SMOOTHING)
30 #define PHYS_BUGRIGS_PLANAR_MOVEMENT            getstati(STAT_BUGRIGS_PLANAR_MOVEMENT)
31 #define PHYS_BUGRIGS_REVERSE_SPEEDING           getstati(STAT_BUGRIGS_REVERSE_SPEEDING)
32 #define PHYS_BUGRIGS_FRICTION_FLOOR             getstatf(STAT_BUGRIGS_FRICTION_FLOOR)
33 #define PHYS_BUGRIGS_AIR_STEERING                       getstati(STAT_BUGRIGS_AIR_STEERING)
34 #define PHYS_BUGRIGS_FRICTION_BRAKE             getstatf(STAT_BUGRIGS_FRICTION_BRAKE)
35 #define PHYS_BUGRIGS_ACCEL                                      getstatf(STAT_BUGRIGS_ACCEL)
36 #define PHYS_BUGRIGS_SPEED_REF                          getstatf(STAT_BUGRIGS_SPEED_REF)
37 #define PHYS_BUGRIGS_SPEED_POW                          getstatf(STAT_BUGRIGS_SPEED_POW)
38 #define PHYS_BUGRIGS_STEER                                      getstatf(STAT_BUGRIGS_STEER)
39 #define PHYS_BUGRIGS_FRICTION_AIR                       getstatf(STAT_BUGRIGS_FRICTION_AIR)
40 #define PHYS_BUGRIGS_CAR_JUMPING                        getstatf(STAT_BUGRIGS_CAR_JUMPING)
41 #define PHYS_BUGRIGS_REVERSE_SPINNING           getstatf(STAT_BUGRIGS_REVERSE_SPINNING)
42 #define PHYS_BUGRIGS_REVERSE_STOPPING           getstatf(STAT_BUGRIGS_REVERSE_STOPPING)
43
44 #elif defined(SVQC)
45
46 bool g_bugrigs;
47 bool g_bugrigs_planar_movement;
48 bool g_bugrigs_planar_movement_car_jumping;
49 float g_bugrigs_reverse_spinning;
50 float g_bugrigs_reverse_speeding;
51 float g_bugrigs_reverse_stopping;
52 float g_bugrigs_air_steering;
53 float g_bugrigs_angle_smoothing;
54 float g_bugrigs_friction_floor;
55 float g_bugrigs_friction_brake;
56 float g_bugrigs_friction_air;
57 float g_bugrigs_accel;
58 float g_bugrigs_speed_ref;
59 float g_bugrigs_speed_pow;
60 float g_bugrigs_steer;
61
62 #define PHYS_BUGRIGS                                            g_bugrigs
63 #define PHYS_BUGRIGS_ANGLE_SMOOTHING            g_bugrigs_angle_smoothing
64 #define PHYS_BUGRIGS_PLANAR_MOVEMENT            g_bugrigs_planar_movement
65 #define PHYS_BUGRIGS_REVERSE_SPEEDING           g_bugrigs_reverse_speeding
66 #define PHYS_BUGRIGS_FRICTION_FLOOR                     g_bugrigs_friction_floor
67 #define PHYS_BUGRIGS_AIR_STEERING                       g_bugrigs_air_steering
68 #define PHYS_BUGRIGS_FRICTION_BRAKE                     g_bugrigs_friction_brake
69 #define PHYS_BUGRIGS_ACCEL                                      g_bugrigs_accel
70 #define PHYS_BUGRIGS_SPEED_REF                          g_bugrigs_speed_ref
71 #define PHYS_BUGRIGS_SPEED_POW                          g_bugrigs_speed_pow
72 #define PHYS_BUGRIGS_STEER                                      g_bugrigs_steer
73 #define PHYS_BUGRIGS_FRICTION_AIR                       g_bugrigs_friction_air
74 #define PHYS_BUGRIGS_CAR_JUMPING                        g_bugrigs_planar_movement_car_jumping
75 #define PHYS_BUGRIGS_REVERSE_SPINNING           g_bugrigs_reverse_spinning
76 #define PHYS_BUGRIGS_REVERSE_STOPPING           g_bugrigs_reverse_stopping
77
78 .float stat_bugrigs;
79 .float stat_bugrigs_angle_smoothing;
80 .float stat_bugrigs_planar_movement;
81 .float stat_bugrigs_reverse_speeding;
82 .float stat_bugrigs_friction_floor;
83 .float stat_bugrigs_air_steering;
84 .float stat_bugrigs_friction_brake;
85 .float stat_bugrigs_accel;
86 .float stat_bugrigs_speed_ref;
87 .float stat_bugrigs_speed_pow;
88 .float stat_bugrigs_steer;
89 .float stat_bugrigs_friction_air;
90 .float stat_bugrigs_car_jumping;
91 .float stat_bugrigs_reverse_spinning;
92 .float stat_bugrigs_reverse_stopping;
93
94 void bugrigs_SetVars()
95 {
96         g_bugrigs = cvar("g_bugrigs");
97         g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
98         g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
99         g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
100         g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
101         g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
102         g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
103         g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
104         g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
105         g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
106         g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
107         g_bugrigs_accel = cvar("g_bugrigs_accel");
108         g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
109         g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
110         g_bugrigs_steer = cvar("g_bugrigs_steer");
111 }
112
113 void bugrigs_UpdateStats(entity this)
114 {
115         this.stat_bugrigs = PHYS_BUGRIGS;
116         this.stat_bugrigs_angle_smoothing = PHYS_BUGRIGS_ANGLE_SMOOTHING;
117         this.stat_bugrigs_planar_movement = PHYS_BUGRIGS_PLANAR_MOVEMENT;
118         this.stat_bugrigs_reverse_speeding = PHYS_BUGRIGS_REVERSE_SPEEDING;
119         this.stat_bugrigs_friction_floor = PHYS_BUGRIGS_FRICTION_FLOOR;
120         this.stat_bugrigs_air_steering = PHYS_BUGRIGS_AIR_STEERING;
121         this.stat_bugrigs_friction_brake = PHYS_BUGRIGS_FRICTION_BRAKE;
122         this.stat_bugrigs_accel = PHYS_BUGRIGS_ACCEL;
123         this.stat_bugrigs_speed_ref = PHYS_BUGRIGS_SPEED_REF;
124         this.stat_bugrigs_speed_pow = PHYS_BUGRIGS_SPEED_POW;
125         this.stat_bugrigs_steer = PHYS_BUGRIGS_STEER;
126         this.stat_bugrigs_friction_air = PHYS_BUGRIGS_FRICTION_AIR;
127         this.stat_bugrigs_car_jumping = PHYS_BUGRIGS_CAR_JUMPING;
128         this.stat_bugrigs_reverse_spinning = PHYS_BUGRIGS_REVERSE_SPINNING;
129         this.stat_bugrigs_reverse_stopping = PHYS_BUGRIGS_REVERSE_STOPPING;
130 }
131
132 void bugrigs_AddStats()
133 {
134         addstat(STAT_BUGRIGS, AS_INT, stat_bugrigs);
135         addstat(STAT_BUGRIGS_ANGLE_SMOOTHING, AS_INT, stat_bugrigs_angle_smoothing);
136         addstat(STAT_BUGRIGS_PLANAR_MOVEMENT, AS_INT, stat_bugrigs_planar_movement);
137         addstat(STAT_BUGRIGS_REVERSE_SPEEDING, AS_INT, stat_bugrigs_reverse_speeding);
138         addstat(STAT_BUGRIGS_FRICTION_FLOOR, AS_FLOAT, stat_bugrigs_friction_floor);
139         addstat(STAT_BUGRIGS_AIR_STEERING, AS_INT, stat_bugrigs_air_steering);
140         addstat(STAT_BUGRIGS_FRICTION_BRAKE, AS_FLOAT, stat_bugrigs_friction_brake);
141         addstat(STAT_BUGRIGS_ACCEL, AS_FLOAT, stat_bugrigs_accel);
142         addstat(STAT_BUGRIGS_SPEED_REF, AS_FLOAT, stat_bugrigs_speed_ref);
143         addstat(STAT_BUGRIGS_SPEED_POW, AS_FLOAT, stat_bugrigs_speed_pow);
144         addstat(STAT_BUGRIGS_STEER, AS_FLOAT, stat_bugrigs_steer);
145         addstat(STAT_BUGRIGS_FRICTION_AIR, AS_FLOAT, stat_bugrigs_friction_air);
146         addstat(STAT_BUGRIGS_CAR_JUMPING, AS_FLOAT, stat_bugrigs_car_jumping);
147         addstat(STAT_BUGRIGS_REVERSE_SPINNING, AS_FLOAT, stat_bugrigs_reverse_spinning);
148         addstat(STAT_BUGRIGS_REVERSE_STOPPING, AS_FLOAT, stat_bugrigs_reverse_stopping);
149 }
150
151 #endif
152
153 void RaceCarPhysics(entity this)
154 {
155         // using this move type for "big rigs"
156         // the engine does not push the entity!
157
158         vector rigvel;
159
160         vector angles_save = this.angles;
161         float accel = bound(-1, this.movement.x / PHYS_MAXSPEED(this), 1);
162         float steer = bound(-1, this.movement.y / PHYS_MAXSPEED(this), 1);
163
164         if (PHYS_BUGRIGS_REVERSE_SPEEDING)
165         {
166                 if (accel < 0)
167                 {
168                         // back accel is DIGITAL
169                         // to prevent speedhack
170                         if (accel < -0.5)
171                                 accel = -1;
172                         else
173                                 accel = 0;
174                 }
175         }
176
177         this.angles_x = 0;
178         this.angles_z = 0;
179         makevectors(this.angles); // new forward direction!
180
181         if (IS_ONGROUND(this) || PHYS_BUGRIGS_AIR_STEERING)
182         {
183                 float myspeed = this.velocity * v_forward;
184                 float upspeed = this.velocity * v_up;
185
186                 // responsiveness factor for steering and acceleration
187                 float f = 1 / (1 + pow(max(-myspeed, myspeed) / PHYS_BUGRIGS_SPEED_REF, PHYS_BUGRIGS_SPEED_POW));
188                 //MAXIMA: f(v) := 1 / (1 + (v / PHYS_BUGRIGS_SPEED_REF) ^ PHYS_BUGRIGS_SPEED_POW);
189
190                 float steerfactor;
191                 if (myspeed < 0 && PHYS_BUGRIGS_REVERSE_SPINNING)
192                         steerfactor = -myspeed * PHYS_BUGRIGS_STEER;
193                 else
194                         steerfactor = -myspeed * f * PHYS_BUGRIGS_STEER;
195
196                 float accelfactor;
197                 if (myspeed < 0 && PHYS_BUGRIGS_REVERSE_SPEEDING)
198                         accelfactor = PHYS_BUGRIGS_ACCEL;
199                 else
200                         accelfactor = f * PHYS_BUGRIGS_ACCEL;
201                 //MAXIMA: accel(v) := f(v) * PHYS_BUGRIGS_ACCEL;
202
203                 if (accel < 0)
204                 {
205                         if (myspeed > 0)
206                         {
207                                 myspeed = max(0, myspeed - PHYS_INPUT_TIMELENGTH * (PHYS_BUGRIGS_FRICTION_FLOOR - PHYS_BUGRIGS_FRICTION_BRAKE * accel));
208                         }
209                         else
210                         {
211                                 if (!PHYS_BUGRIGS_REVERSE_SPEEDING)
212                                         myspeed = min(0, myspeed + PHYS_INPUT_TIMELENGTH * PHYS_BUGRIGS_FRICTION_FLOOR);
213                         }
214                 }
215                 else
216                 {
217                         if (myspeed >= 0)
218                         {
219                                 myspeed = max(0, myspeed - PHYS_INPUT_TIMELENGTH * PHYS_BUGRIGS_FRICTION_FLOOR);
220                         }
221                         else
222                         {
223                                 if (PHYS_BUGRIGS_REVERSE_STOPPING)
224                                         myspeed = 0;
225                                 else
226                                         myspeed = min(0, myspeed + PHYS_INPUT_TIMELENGTH * (PHYS_BUGRIGS_FRICTION_FLOOR + PHYS_BUGRIGS_FRICTION_BRAKE * accel));
227                         }
228                 }
229                 // terminal velocity = velocity at which 50 == accelfactor, that is, 1549 units/sec
230                 //MAXIMA: friction(v) := PHYS_BUGRIGS_FRICTION_FLOOR;
231
232                 this.angles_y += steer * PHYS_INPUT_TIMELENGTH * steerfactor; // apply steering
233                 makevectors(this.angles); // new forward direction!
234
235                 myspeed += accel * accelfactor * PHYS_INPUT_TIMELENGTH;
236
237                 rigvel = myspeed * v_forward + '0 0 1' * upspeed;
238         }
239         else
240         {
241                 float myspeed = vlen(this.velocity);
242
243                 // responsiveness factor for steering and acceleration
244                 float f = 1 / (1 + pow(max(0, myspeed / PHYS_BUGRIGS_SPEED_REF), PHYS_BUGRIGS_SPEED_POW));
245                 float steerfactor = -myspeed * f;
246                 this.angles_y += steer * PHYS_INPUT_TIMELENGTH * steerfactor; // apply steering
247
248                 rigvel = this.velocity;
249                 makevectors(this.angles); // new forward direction!
250         }
251
252         rigvel *= max(0, 1 - vlen(rigvel) * PHYS_BUGRIGS_FRICTION_AIR * PHYS_INPUT_TIMELENGTH);
253         //MAXIMA: airfriction(v) := v * v * PHYS_BUGRIGS_FRICTION_AIR;
254         //MAXIMA: total_acceleration(v) := accel(v) - friction(v) - airfriction(v);
255         //MAXIMA: solve(total_acceleration(v) = 0, v);
256
257         if (PHYS_BUGRIGS_PLANAR_MOVEMENT)
258         {
259                 vector rigvel_xy, neworigin, up;
260                 float mt;
261
262                 rigvel_z -= PHYS_INPUT_TIMELENGTH * PHYS_GRAVITY; // 4x gravity plays better
263                 rigvel_xy = vec2(rigvel);
264
265                 if (PHYS_BUGRIGS_CAR_JUMPING)
266                         mt = MOVE_NORMAL;
267                 else
268                         mt = MOVE_NOMONSTERS;
269
270                 tracebox(this.origin, this.mins, this.maxs, this.origin + '0 0 1024', mt, this);
271                 up = trace_endpos - this.origin;
272
273                 // BUG RIGS: align the move to the surface instead of doing collision testing
274                 // can we move?
275                 tracebox(trace_endpos, this.mins, this.maxs, trace_endpos + rigvel_xy * PHYS_INPUT_TIMELENGTH, mt, this);
276
277                 // align to surface
278                 tracebox(trace_endpos, this.mins, this.maxs, trace_endpos - up + '0 0 1' * rigvel_z * PHYS_INPUT_TIMELENGTH, mt, this);
279
280                 if (trace_fraction < 0.5)
281                 {
282                         trace_fraction = 1;
283                         neworigin = this.origin;
284                 }
285                 else
286                         neworigin = trace_endpos;
287
288                 if (trace_fraction < 1)
289                 {
290                         // now set angles_x so that the car points parallel to the surface
291                         this.angles = vectoangles(
292                                         '1 0 0' * v_forward_x * trace_plane_normal_z
293                                         +
294                                         '0 1 0' * v_forward_y * trace_plane_normal_z
295                                         +
296                                         '0 0 1' * -(v_forward_x * trace_plane_normal_x + v_forward_y * trace_plane_normal_y)
297                                         );
298                         SET_ONGROUND(this);
299                 }
300                 else
301                 {
302                         // now set angles_x so that the car points forward, but is tilted in velocity direction
303                         UNSET_ONGROUND(this);
304                 }
305
306                 this.velocity = (neworigin - this.origin) * (1.0 / PHYS_INPUT_TIMELENGTH);
307                 this.movetype = MOVETYPE_NOCLIP;
308         }
309         else
310         {
311                 rigvel_z -= PHYS_INPUT_TIMELENGTH * PHYS_GRAVITY; // 4x gravity plays better
312                 this.velocity = rigvel;
313                 this.movetype = MOVETYPE_FLY;
314         }
315
316         trace_fraction = 1;
317         tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 4', MOVE_NORMAL, this);
318         if (trace_fraction != 1)
319         {
320                 this.angles = vectoangles2(
321                                 '1 0 0' * v_forward_x * trace_plane_normal_z
322                                 +
323                                 '0 1 0' * v_forward_y * trace_plane_normal_z
324                                 +
325                                 '0 0 1' * -(v_forward_x * trace_plane_normal_x + v_forward_y * trace_plane_normal_y),
326                                 trace_plane_normal
327                                 );
328         }
329         else
330         {
331                 vector vel_local;
332
333                 vel_local_x = v_forward * this.velocity;
334                 vel_local_y = v_right * this.velocity;
335                 vel_local_z = v_up * this.velocity;
336
337                 this.angles_x = racecar_angle(vel_local_x, vel_local_z);
338                 this.angles_z = racecar_angle(-vel_local_y, vel_local_z);
339         }
340
341         // smooth the angles
342         vector vf1, vu1, smoothangles;
343         makevectors(this.angles);
344         float f = bound(0, PHYS_INPUT_TIMELENGTH * PHYS_BUGRIGS_ANGLE_SMOOTHING, 1);
345         if (f == 0)
346                 f = 1;
347         vf1 = v_forward * f;
348         vu1 = v_up * f;
349         makevectors(angles_save);
350         vf1 = vf1 + v_forward * (1 - f);
351         vu1 = vu1 + v_up * (1 - f);
352         smoothangles = vectoangles2(vf1, vu1);
353         this.angles_x = -smoothangles_x;
354         this.angles_z =  smoothangles_z;
355
356         PM_ClientMovement_Move(this);
357 }
358
359 #ifdef SVQC
360 .vector bugrigs_prevangles;
361 #endif
362 MUTATOR_HOOKFUNCTION(bugrigs, PM_Physics)
363 {
364         if(!PHYS_BUGRIGS || !IS_PLAYER(self)) { return false; }
365
366 #ifdef SVQC
367         self.angles = self.bugrigs_prevangles;
368 #endif
369
370         RaceCarPhysics(self);
371         return true;
372 }
373
374 MUTATOR_HOOKFUNCTION(bugrigs, PlayerPhysics)
375 {
376         if(!PHYS_BUGRIGS) { return false; }
377 #ifdef SVQC
378         self.bugrigs_prevangles = self.angles;
379
380         bugrigs_UpdateStats(self);
381 #endif
382         return false;
383 }
384
385 #ifdef SVQC
386
387 MUTATOR_HOOKFUNCTION(bugrigs, ClientConnect)
388 {
389         stuffcmd(self, "cl_cmd settemp chase_active 1\n");
390         return false;
391 }
392
393 MUTATOR_HOOKFUNCTION(bugrigs, BuildMutatorsString)
394 {
395         ret_string = strcat(ret_string, ":bugrigs");
396         return false;
397 }
398
399 MUTATOR_HOOKFUNCTION(bugrigs, BuildMutatorsPrettyString)
400 {
401         ret_string = strcat(ret_string, ", Bug rigs");
402         return false;
403 }
404
405 #endif
406 #endif