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