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) 12void bugrigs_SetVars(); 13 14REGISTER_MUTATOR(bugrigs, cvar("g_bugrigs")) 15{ 16 MUTATOR_ONADD 17 { 18 bugrigs_SetVars(); 19 } 20 return false; 21} 22#elif defined(CSQC) 23REGISTER_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 45void 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 66void RaceCarPhysics(entity this, float dt) 67{ 68 // using this move type for "big rigs" 69 // the engine does not push the entity! 70 71 vector rigvel; 72 73 vector angles_save = this.angles; 74 float accel = bound(-1, this.movement.x / PHYS_MAXSPEED(this), 1); 75 float steer = bound(-1, this.movement.y / PHYS_MAXSPEED(this), 1); 76 77 if (PHYS_BUGRIGS_REVERSE_SPEEDING(this)) 78 { 79 if (accel < 0) 80 { 81 // back accel is DIGITAL 82 // to prevent speedhack 83 if (accel < -0.5) 84 accel = -1; 85 else 86 accel = 0; 87 } 88 } 89 90 this.angles_x = 0; 91 this.angles_z = 0; 92 makevectors(this.angles); // new forward direction! 93 94 if (IS_ONGROUND(this) || PHYS_BUGRIGS_AIR_STEERING(this)) 95 { 96 float myspeed = this.velocity * v_forward; 97 float upspeed = this.velocity * v_up; 98 99 // responsiveness factor for steering and acceleration 100 float f = 1 / (1 + ((max(-myspeed, myspeed) / PHYS_BUGRIGS_SPEED_REF(this)) ** PHYS_BUGRIGS_SPEED_POW(this))); 101 //MAXIMA: f(v) := 1 / (1 + (v / PHYS_BUGRIGS_SPEED_REF(this)) ^ PHYS_BUGRIGS_SPEED_POW(this)); 102 103 float steerfactor; 104 if (myspeed < 0 && PHYS_BUGRIGS_REVERSE_SPINNING(this)) 105 steerfactor = -myspeed * PHYS_BUGRIGS_STEER(this); 106 else 107 steerfactor = -myspeed * f * PHYS_BUGRIGS_STEER(this); 108 109 float accelfactor; 110 if (myspeed < 0 && PHYS_BUGRIGS_REVERSE_SPEEDING(this)) 111 accelfactor = PHYS_BUGRIGS_ACCEL(this); 112 else 113 accelfactor = f * PHYS_BUGRIGS_ACCEL(this); 114 //MAXIMA: accel(v) := f(v) * PHYS_BUGRIGS_ACCEL(this); 115 116 if (accel < 0) 117 { 118 if (myspeed > 0) 119 { 120 myspeed = max(0, myspeed - dt * (PHYS_BUGRIGS_FRICTION_FLOOR(this) - PHYS_BUGRIGS_FRICTION_BRAKE(this) * accel)); 121 } 122 else 123 { 124 if (!PHYS_BUGRIGS_REVERSE_SPEEDING(this)) 125 myspeed = min(0, myspeed + dt * PHYS_BUGRIGS_FRICTION_FLOOR(this)); 126 } 127 } 128 else 129 { 130 if (myspeed >= 0) 131 { 132 myspeed = max(0, myspeed - dt * PHYS_BUGRIGS_FRICTION_FLOOR(this)); 133 } 134 else 135 { 136 if (PHYS_BUGRIGS_REVERSE_STOPPING(this)) 137 myspeed = 0; 138 else 139 myspeed = min(0, myspeed + dt * (PHYS_BUGRIGS_FRICTION_FLOOR(this) + PHYS_BUGRIGS_FRICTION_BRAKE(this) * accel)); 140 } 141 } 142 // terminal velocity = velocity at which 50 == accelfactor, that is, 1549 units/sec 143 //MAXIMA: friction(v) := PHYS_BUGRIGS_FRICTION_FLOOR(this); 144 145 this.angles_y += steer * dt * steerfactor; // apply steering 146 makevectors(this.angles); // new forward direction! 147 148 myspeed += accel * accelfactor * dt; 149 150 rigvel = myspeed * v_forward + '0 0 1' * upspeed; 151 } 152 else 153 { 154 float myspeed = vlen(this.velocity); 155 156 // responsiveness factor for steering and acceleration 157 float f = 1 / (1 + (max(0, myspeed / PHYS_BUGRIGS_SPEED_REF(this)) ** PHYS_BUGRIGS_SPEED_POW(this))); 158 float steerfactor = -myspeed * f; 159 this.angles_y += steer * dt * steerfactor; // apply steering 160 161 rigvel = this.velocity; 162 makevectors(this.angles); // new forward direction! 163 } 164 165 rigvel *= max(0, 1 - vlen(rigvel) * PHYS_BUGRIGS_FRICTION_AIR(this) * dt); 166 //MAXIMA: airfriction(v) := v * v * PHYS_BUGRIGS_FRICTION_AIR(this); 167 //MAXIMA: total_acceleration(v) := accel(v) - friction(v) - airfriction(v); 168 //MAXIMA: solve(total_acceleration(v) = 0, v); 169 170 if (PHYS_BUGRIGS_PLANAR_MOVEMENT(this)) 171 { 172 vector rigvel_xy, neworigin, up; 173 float mt; 174 175 rigvel_z -= dt * PHYS_GRAVITY(this); // 4x gravity plays better 176 rigvel_xy = vec2(rigvel); 177 178 if (PHYS_BUGRIGS_CAR_JUMPING(this)) 179 mt = MOVE_NORMAL; 180 else 181 mt = MOVE_NOMONSTERS; 182 183 tracebox(this.origin, this.mins, this.maxs, this.origin + '0 0 1024', mt, this); 184 up = trace_endpos - this.origin; 185 186 // BUG RIGS: align the move to the surface instead of doing collision testing 187 // can we move? 188 tracebox(trace_endpos, this.mins, this.maxs, trace_endpos + rigvel_xy * dt, mt, this); 189 190 // align to surface 191 tracebox(trace_endpos, this.mins, this.maxs, trace_endpos - up + '0 0 1' * rigvel_z * dt, mt, this); 192 193 if (trace_fraction < 0.5) 194 { 195 trace_fraction = 1; 196 neworigin = this.origin; 197 } 198 else 199 neworigin = trace_endpos; 200 201 if (trace_fraction < 1) 202 { 203 // now set angles_x so that the car points parallel to the surface 204 this.angles = vectoangles( 205 '1 0 0' * v_forward_x * trace_plane_normal_z 206 + 207 '0 1 0' * v_forward_y * trace_plane_normal_z 208 + 209 '0 0 1' * -(v_forward_x * trace_plane_normal_x + v_forward_y * trace_plane_normal_y) 210 ); 211 SET_ONGROUND(this); 212 } 213 else 214 { 215 // now set angles_x so that the car points forward, but is tilted in velocity direction 216 UNSET_ONGROUND(this); 217 } 218 219 this.velocity = (neworigin - this.origin) * (1.0 / dt); 220 set_movetype(this, MOVETYPE_NOCLIP); 221 } 222 else 223 { 224 rigvel_z -= dt * PHYS_GRAVITY(this); // 4x gravity plays better 225 this.velocity = rigvel; 226 set_movetype(this, MOVETYPE_FLY); 227 } 228 229 trace_fraction = 1; 230 tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 4', MOVE_NORMAL, this); 231 if (trace_fraction != 1) 232 { 233 this.angles = vectoangles2( 234 '1 0 0' * v_forward_x * trace_plane_normal_z 235 + 236 '0 1 0' * v_forward_y * trace_plane_normal_z 237 + 238 '0 0 1' * -(v_forward_x * trace_plane_normal_x + v_forward_y * trace_plane_normal_y), 239 trace_plane_normal 240 ); 241 } 242 else 243 { 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 vf1 = v_forward * f; 261 vu1 = v_up * f; 262 makevectors(angles_save); 263 vf1 = vf1 + v_forward * (1 - f); 264 vu1 = vu1 + v_up * (1 - f); 265 smoothangles = vectoangles2(vf1, vu1); 266 this.angles_x = -smoothangles_x; 267 this.angles_z = smoothangles_z; 268} 269 270#ifdef SVQC 271.vector bugrigs_prevangles; 272#endif 273MUTATOR_HOOKFUNCTION(bugrigs, PM_Physics) 274{ 275 entity player = M_ARGV(0, entity); 276 float dt = M_ARGV(2, float); 277 278 if(!PHYS_BUGRIGS(player) || !IS_PLAYER(player)) { return; } 279 280#ifdef SVQC 281 player.angles = player.bugrigs_prevangles; 282#endif 283 284 RaceCarPhysics(player, dt); 285 return true; 286} 287 288MUTATOR_HOOKFUNCTION(bugrigs, PlayerPhysics) 289{ 290 if(!PHYS_BUGRIGS(M_ARGV(0, entity))) { return; } 291#ifdef SVQC 292 entity player = M_ARGV(0, entity); 293 player.bugrigs_prevangles = player.angles; 294#endif 295} 296 297#ifdef SVQC 298 299MUTATOR_HOOKFUNCTION(bugrigs, ClientConnect) 300{ 301 entity player = M_ARGV(0, entity); 302 303 stuffcmd(player, "cl_cmd settemp chase_active 1\n"); 304} 305 306MUTATOR_HOOKFUNCTION(bugrigs, BuildMutatorsString) 307{ 308 M_ARGV(0, string) = strcat(M_ARGV(0, string), ":bugrigs"); 309} 310 311MUTATOR_HOOKFUNCTION(bugrigs, BuildMutatorsPrettyString) 312{ 313 M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Bug rigs"); 314} 315 316#endif 317 318#endif 319