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