1 /*
2 ===========================================================================
3 Copyright (C) 2000 - 2013, Raven Software, Inc.
4 Copyright (C) 2001 - 2013, Activision, Inc.
5 Copyright (C) 2013 - 2015, OpenJK contributors
6 
7 This file is part of the OpenJK source code.
8 
9 OpenJK is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License version 2 as
11 published by the Free Software Foundation.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, see <http://www.gnu.org/licenses/>.
20 ===========================================================================
21 */
22 
23 #include "bg_public.h"
24 #include "bg_vehicles.h"
25 
26 #ifdef _GAME
27 	#include "g_local.h"
28 #elif _CGAME
29 	#include "cgame/cg_local.h"
30 #endif
31 
32 extern float DotToSpot( vec3_t spot, vec3_t from, vec3_t fromAngles );
33 #ifdef _GAME //SP or gameside MP
34 	extern vmCvar_t	cg_thirdPersonAlpha;
35 	extern vec3_t playerMins;
36 	extern vec3_t playerMaxs;
37 	extern void ChangeWeapon( gentity_t *ent, int newWeapon );
38 	extern int PM_AnimLength( int index, animNumber_t anim );
39 	extern void G_VehicleTrace( trace_t *results, const vec3_t start, const vec3_t tMins, const vec3_t tMaxs, const vec3_t end, int passEntityNum, int contentmask );
40 #endif
41 
42 extern qboolean BG_UnrestrainedPitchRoll( playerState_t *ps, Vehicle_t *pVeh );
43 extern void BG_SetAnim(playerState_t *ps, animation_t *animations, int setAnimParts,int anim,int setAnimFlags);
44 extern int BG_GetTime(void);
45 
46 //this stuff has got to be predicted, so..
BG_FighterUpdate(Vehicle_t * pVeh,const usercmd_t * pUcmd,vec3_t trMins,vec3_t trMaxs,float gravity,void (* traceFunc)(trace_t * results,const vec3_t start,const vec3_t lmins,const vec3_t lmaxs,const vec3_t end,int passEntityNum,int contentMask))47 qboolean BG_FighterUpdate(Vehicle_t *pVeh, const usercmd_t *pUcmd, vec3_t trMins, vec3_t trMaxs, float gravity, void (*traceFunc)( trace_t *results, const vec3_t start, const vec3_t lmins, const vec3_t lmaxs, const vec3_t end, int passEntityNum, int contentMask ))
48 {
49 	vec3_t		bottom;
50 	playerState_t *parentPS;
51 	//qboolean	isDead = qfalse;
52 #ifdef _GAME //don't do this on client
53 	int i;
54 
55 	// Make sure the riders are not visible or collidable.
56 	pVeh->m_pVehicleInfo->Ghost( pVeh, pVeh->m_pPilot );
57 	for ( i = 0; i < pVeh->m_pVehicleInfo->maxPassengers; i++ )
58 	{
59 		pVeh->m_pVehicleInfo->Ghost( pVeh, pVeh->m_ppPassengers[i] );
60 	}
61 #endif
62 
63 
64 	parentPS = pVeh->m_pParentEntity->playerState;
65 
66 	if (!parentPS)
67 	{
68 		Com_Error(ERR_DROP, "NULL PS in BG_FighterUpdate (%s)", pVeh->m_pVehicleInfo->name);
69 		return qfalse;
70 	}
71 
72 	// If we have a pilot, take out gravity (it's a flying craft...).
73 	if ( pVeh->m_pPilot )
74 	{
75 		parentPS->gravity = 0;
76 	}
77 	else
78 	{
79 		if (pVeh->m_pVehicleInfo->gravity)
80 		{
81 			parentPS->gravity = pVeh->m_pVehicleInfo->gravity;
82 		}
83 		else
84 		{ //it doesn't have gravity specified apparently
85 			parentPS->gravity = gravity;
86 		}
87 	}
88 
89 	/*
90 	isDead = (qboolean)((parentPS->eFlags&EF_DEAD)!=0);
91 
92 	if ( isDead ||
93 		(pVeh->m_pVehicleInfo->surfDestruction &&
94 			pVeh->m_iRemovedSurfaces ) )
95 	{//can't land if dead or spiralling out of control
96 		pVeh->m_LandTrace.fraction = 1.0f;
97 		pVeh->m_LandTrace.contents = pVeh->m_LandTrace.surfaceFlags = 0;
98 		VectorClear( pVeh->m_LandTrace.plane.normal );
99 		pVeh->m_LandTrace.allsolid = qfalse;
100 		pVeh->m_LandTrace.startsolid = qfalse;
101 	}
102 	else
103 	{
104 	*/
105 	//argh, no, I need to have a way to see when they impact the ground while damaged. -rww
106 
107 		// Check to see if the fighter has taken off yet (if it's a certain height above ground).
108 		VectorCopy( parentPS->origin, bottom );
109 		bottom[2] -= pVeh->m_pVehicleInfo->landingHeight;
110 
111 		traceFunc( &pVeh->m_LandTrace, parentPS->origin, trMins, trMaxs, bottom, pVeh->m_pParentEntity->s.number, (MASK_NPCSOLID&~CONTENTS_BODY) );
112 	//}
113 
114 	return qtrue;
115 }
116 
117 #ifdef _GAME //ONLY in SP or on server, not cgame
118 
119 // Like a think or move command, this updates various vehicle properties.
Update(Vehicle_t * pVeh,const usercmd_t * pUcmd)120 static qboolean Update( Vehicle_t *pVeh, const usercmd_t *pUcmd )
121 {
122 	assert(pVeh->m_pParentEntity);
123 	if (!BG_FighterUpdate(pVeh, pUcmd, ((gentity_t *)pVeh->m_pParentEntity)->r.mins,
124 		((gentity_t *)pVeh->m_pParentEntity)->r.maxs,
125 		g_gravity.value,
126 		G_VehicleTrace))
127 	{
128 		return qfalse;
129 	}
130 
131 	if ( !g_vehicleInfo[VEHICLE_BASE].Update( pVeh, pUcmd ) )
132 	{
133 		return qfalse;
134 	}
135 
136 	return qtrue;
137 }
138 
139 // Board this Vehicle (get on). The first entity to board an empty vehicle becomes the Pilot.
Board(Vehicle_t * pVeh,bgEntity_t * pEnt)140 static qboolean Board( Vehicle_t *pVeh, bgEntity_t *pEnt )
141 {
142 	if ( !g_vehicleInfo[VEHICLE_BASE].Board( pVeh, pEnt ) )
143 		return qfalse;
144 
145 	// Set the board wait time (they won't be able to do anything, including getting off, for this amount of time).
146 	pVeh->m_iBoarding = level.time + 1500;
147 
148 	return qtrue;
149 }
150 
151 // Eject an entity from the vehicle.
Eject(Vehicle_t * pVeh,bgEntity_t * pEnt,qboolean forceEject)152 static qboolean Eject( Vehicle_t *pVeh, bgEntity_t *pEnt, qboolean forceEject )
153 {
154 	if ( g_vehicleInfo[VEHICLE_BASE].Eject( pVeh, pEnt, forceEject ) )
155 	{
156 		return qtrue;
157 	}
158 
159 	return qfalse;
160 }
161 
162 #endif //end game-side only
163 
164 //method of decrementing the given angle based on the given taking variable frame times into account
PredictedAngularDecrement(float scale,float timeMod,float originalAngle)165 static float PredictedAngularDecrement(float scale, float timeMod, float originalAngle)
166 {
167 	float fixedBaseDec = originalAngle*0.05f;
168 	float r = 0.0f;
169 
170 	if (fixedBaseDec < 0.0f)
171 	{
172 		fixedBaseDec = -fixedBaseDec;
173 	}
174 
175 	fixedBaseDec *= (1.0f+(1.0f-scale));
176 
177 	if (fixedBaseDec < 0.1f)
178 	{ //don't increment in incredibly small fractions, it would eat up unnecessary bandwidth.
179 		fixedBaseDec = 0.1f;
180 	}
181 
182 	fixedBaseDec *= (timeMod*0.1f);
183 	if (originalAngle > 0.0f)
184 	{ //subtract
185 		r = (originalAngle-fixedBaseDec);
186 		if (r < 0.0f)
187 		{
188 			r = 0.0f;
189 		}
190 	}
191 	else if (originalAngle < 0.0f)
192 	{ //add
193 		r = (originalAngle+fixedBaseDec);
194 		if (r > 0.0f)
195 		{
196 			r = 0.0f;
197 		}
198 	}
199 
200 	return r;
201 }
202 
203 #ifdef _GAME//only do this check on game side, because if it's cgame, it's being predicted, and it's only predicted if the local client is the driver
FighterIsInSpace(gentity_t * gParent)204 qboolean FighterIsInSpace( gentity_t *gParent )
205 {
206 	if ( gParent
207 		&& gParent->client
208 		&& gParent->client->inSpaceIndex
209 		&& gParent->client->inSpaceIndex < ENTITYNUM_WORLD )
210 	{
211 		return qtrue;
212 	}
213 	return qfalse;
214 }
215 #endif
216 
FighterOverValidLandingSurface(Vehicle_t * pVeh)217 qboolean FighterOverValidLandingSurface( Vehicle_t *pVeh )
218 {
219 	if ( pVeh->m_LandTrace.fraction < 1.0f //ground present
220 		&& pVeh->m_LandTrace.plane.normal[2] >= MIN_LANDING_SLOPE )//flat enough
221 		//FIXME: also check for a certain surface flag ... "landing zones"?
222 	{
223 		return qtrue;
224 	}
225 	return qfalse;
226 }
227 
FighterIsLanded(Vehicle_t * pVeh,playerState_t * parentPS)228 qboolean FighterIsLanded( Vehicle_t *pVeh, playerState_t *parentPS )
229 {
230 	if ( FighterOverValidLandingSurface( pVeh )
231 		&& !parentPS->speed )//stopped
232 	{
233 		return qtrue;
234 	}
235 	return qfalse;
236 }
237 
FighterIsLanding(Vehicle_t * pVeh,playerState_t * parentPS)238 qboolean FighterIsLanding( Vehicle_t *pVeh, playerState_t *parentPS )
239 {
240 
241 	if ( FighterOverValidLandingSurface( pVeh )
242 #ifdef _GAME//only do this check on game side, because if it's cgame, it's being predicted, and it's only predicted if the local client is the driver
243 		&& pVeh->m_pVehicleInfo->Inhabited( pVeh )//has to have a driver in order to be capable of landing
244 #endif
245 		&& (pVeh->m_ucmd.forwardmove < 0||pVeh->m_ucmd.upmove<0) //decelerating or holding crouch button
246 		&& parentPS->speed <= MIN_LANDING_SPEED )//going slow enough to start landing - was using pVeh->m_pVehicleInfo->speedIdle, but that's still too fast
247 	{
248 		return qtrue;
249 	}
250 	return qfalse;
251 }
252 
FighterIsLaunching(Vehicle_t * pVeh,playerState_t * parentPS)253 qboolean FighterIsLaunching( Vehicle_t *pVeh, playerState_t *parentPS )
254 {
255 
256 	if ( FighterOverValidLandingSurface( pVeh )
257 #ifdef _GAME//only do this check on game side, because if it's cgame, it's being predicted, and it's only predicted if the local client is the driver
258 		&& pVeh->m_pVehicleInfo->Inhabited( pVeh )//has to have a driver in order to be capable of landing
259 #endif
260 		&& pVeh->m_ucmd.upmove > 0 //trying to take off
261 		&& parentPS->speed <= 200.0f )//going slow enough to start landing - was using pVeh->m_pVehicleInfo->speedIdle, but that's still too fast
262 	{
263 		return qtrue;
264 	}
265 	return qfalse;
266 }
267 
FighterSuspended(Vehicle_t * pVeh,playerState_t * parentPS)268 qboolean FighterSuspended( Vehicle_t *pVeh, playerState_t *parentPS )
269 {
270 #ifdef _GAME//only do this check on game side, because if it's cgame, it's being predicted, and it's only predicted if the local client is the driver
271 	if (!pVeh->m_pPilot//empty
272 		&& !parentPS->speed//not moving
273 		&& pVeh->m_ucmd.forwardmove <= 0//not trying to go forward for whatever reason
274 		&& pVeh->m_pParentEntity != NULL
275 		&& (((gentity_t *)pVeh->m_pParentEntity)->spawnflags&2) )//SUSPENDED spawnflag is on
276 	{
277 		return qtrue;
278 	}
279 	return qfalse;
280 #elif _CGAME
281 	return qfalse;
282 #endif
283 }
284 
285 //MP RULE - ALL PROCESSMOVECOMMANDS FUNCTIONS MUST BE BG-COMPATIBLE!!!
286 //If you really need to violate this rule for SP, then use ifdefs.
287 //By BG-compatible, I mean no use of game-specific data - ONLY use
288 //stuff available in the MP bgEntity (in SP, the bgEntity is #defined
289 //as a gentity, but the MP-compatible access restrictions are based
290 //on the bgEntity structure in the MP codebase) -rww
291 // ProcessMoveCommands the Vehicle.
292 #define FIGHTER_MIN_TAKEOFF_FRACTION 0.7f
ProcessMoveCommands(Vehicle_t * pVeh)293 static void ProcessMoveCommands( Vehicle_t *pVeh )
294 {
295 	/************************************************************************************/
296 	/*	BEGIN	Here is where we move the vehicle (forward or back or whatever). BEGIN	*/
297 	/************************************************************************************/
298 
299 	//Client sets ucmds and such for speed alterations
300 	float speedInc, speedIdleDec, speedIdle, speedIdleAccel, speedMin, speedMax;
301 	bgEntity_t *parent = pVeh->m_pParentEntity;
302 	qboolean isLandingOrLaunching = qfalse;
303 	//this function should only be called from pmove.. if it gets called elsehwere,
304 	//obviously this will explode.
305 	int curTime = pm->cmd.serverTime;
306 
307 	playerState_t *parentPS = parent->playerState;
308 
309 	if ( parentPS->hyperSpaceTime
310 		&& curTime - parentPS->hyperSpaceTime < HYPERSPACE_TIME )
311 	{//Going to Hyperspace
312 		//totally override movement
313 		float timeFrac = ((float)(curTime-parentPS->hyperSpaceTime))/HYPERSPACE_TIME;
314 		if ( timeFrac < HYPERSPACE_TELEPORT_FRAC )
315 		{//for first half, instantly jump to top speed!
316 			if ( !(parentPS->eFlags2&EF2_HYPERSPACE) )
317 			{//waiting to face the right direction, do nothing
318 				parentPS->speed = 0.0f;
319 			}
320 			else
321 			{
322 				if ( parentPS->speed < HYPERSPACE_SPEED )
323 				{//just started hyperspace
324 //MIKE: This is going to play the sound twice for the predicting client, I suggest using
325 //a predicted event or only doing it game-side. -rich
326 #ifdef _GAME
327 					//G_EntitySound( ((gentity_t *)(pVeh->m_pParentEntity)), CHAN_LOCAL, pVeh->m_pVehicleInfo->soundHyper );
328 #elif _CGAME
329 					trap->S_StartSound( NULL, pm->ps->clientNum, CHAN_LOCAL, pVeh->m_pVehicleInfo->soundHyper );
330 #endif
331 				}
332 
333 				parentPS->speed = HYPERSPACE_SPEED;
334 			}
335 		}
336 		else
337 		{//slow from top speed to 200...
338 			parentPS->speed = 200.0f + ((1.0f-timeFrac)*(1.0f/HYPERSPACE_TELEPORT_FRAC)*(HYPERSPACE_SPEED-200.0f));
339 			//don't mess with acceleration, just pop to the high velocity
340 			if ( VectorLength( parentPS->velocity ) < parentPS->speed )
341 			{
342 				VectorScale( parentPS->moveDir, parentPS->speed, parentPS->velocity );
343 			}
344 		}
345 		return;
346 	}
347 
348 	if ( pVeh->m_iDropTime >= curTime )
349 	{//no speed, just drop
350 		parentPS->speed = 0.0f;
351 		parentPS->gravity = 800;
352 		return;
353 	}
354 
355 	isLandingOrLaunching = (FighterIsLanding( pVeh, parentPS )||FighterIsLaunching( pVeh, parentPS ));
356 
357 	// If we are hitting the ground, just allow the fighter to go up and down.
358 	if ( isLandingOrLaunching//going slow enough to start landing
359 		&& (pVeh->m_ucmd.forwardmove<=0||pVeh->m_LandTrace.fraction<=FIGHTER_MIN_TAKEOFF_FRACTION) )//not trying to accelerate away already (or: you are trying to, but not high enough off the ground yet)
360 	{//FIXME: if start to move forward and fly over something low while still going relatively slow, you may try to land even though you don't mean to...
361 		//float fInvFrac = 1.0f - pVeh->m_LandTrace.fraction;
362 
363 		if ( pVeh->m_ucmd.upmove > 0 )
364 		{
365 			if ( parentPS->velocity[2] <= 0
366 				&& pVeh->m_pVehicleInfo->soundTakeOff )
367 			{//taking off for the first time
368 				#ifdef _GAME//MP GAME-side
369 					G_EntitySound( ((gentity_t *)(pVeh->m_pParentEntity)), CHAN_AUTO, pVeh->m_pVehicleInfo->soundTakeOff );
370 				#endif
371 			}
372 			parentPS->velocity[2] += pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier;// * ( /*fInvFrac **/ 1.5f );
373 		}
374 		else if ( pVeh->m_ucmd.upmove < 0 )
375 		{
376 			parentPS->velocity[2] -= pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier;// * ( /*fInvFrac **/ 1.8f );
377 		}
378 		else if ( pVeh->m_ucmd.forwardmove < 0 )
379 		{
380 			if ( pVeh->m_LandTrace.fraction != 0.0f )
381 			{
382 				parentPS->velocity[2] -= pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier;
383 			}
384 
385 			if ( pVeh->m_LandTrace.fraction <= FIGHTER_MIN_TAKEOFF_FRACTION )
386 			{
387 				//pVeh->m_pParentEntity->client->ps.velocity[0] *= pVeh->m_LandTrace.fraction;
388 				//pVeh->m_pParentEntity->client->ps.velocity[1] *= pVeh->m_LandTrace.fraction;
389 
390 				//remember to always base this stuff on the time modifier! otherwise, you create
391 				//framerate-dependancy issues and break prediction in MP -rww
392 				//parentPS->velocity[2] *= pVeh->m_LandTrace.fraction;
393 				//it's not an angle, but hey
394 				parentPS->velocity[2] = PredictedAngularDecrement(pVeh->m_LandTrace.fraction, pVeh->m_fTimeModifier*5.0f, parentPS->velocity[2]);
395 
396 				parentPS->speed = 0;
397 			}
398 		}
399 
400 		// Make sure they don't pitch as they near the ground.
401 		//pVeh->m_vOrientation[PITCH] *= 0.7f;
402 		pVeh->m_vOrientation[PITCH] = PredictedAngularDecrement(0.7f, pVeh->m_fTimeModifier*10.0f, pVeh->m_vOrientation[PITCH]);
403 
404 		return;
405 	}
406 
407 	if ( (pVeh->m_ucmd.upmove > 0) && pVeh->m_pVehicleInfo->turboSpeed )
408 	{
409 		if ((curTime - pVeh->m_iTurboTime)>pVeh->m_pVehicleInfo->turboRecharge)
410 		{
411 			pVeh->m_iTurboTime = (curTime + pVeh->m_pVehicleInfo->turboDuration);
412 
413 #ifdef _GAME//MP GAME-side
414 			//NOTE: turbo sound can't be part of effect if effect is played on every muzzle!
415 			if ( pVeh->m_pVehicleInfo->soundTurbo )
416 			{
417 				G_EntitySound( ((gentity_t *)(pVeh->m_pParentEntity)), CHAN_AUTO, pVeh->m_pVehicleInfo->soundTurbo );
418 			}
419 #endif
420 		}
421 	}
422 	speedInc = pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier;
423 	if ( curTime < pVeh->m_iTurboTime )
424 	{//going turbo speed
425 		speedMax = pVeh->m_pVehicleInfo->turboSpeed;
426 		//double our acceleration
427 		//speedInc *= 2.0f;
428 		//no no no! this would el breako el predictiono! we want the following... -rww
429         speedInc = (pVeh->m_pVehicleInfo->acceleration*2.0f) * pVeh->m_fTimeModifier;
430 
431 		//force us to move forward
432 		pVeh->m_ucmd.forwardmove = 127;
433 		//add flag to let cgame know to draw the iTurboFX effect
434 		parentPS->eFlags |= EF_JETPACK_ACTIVE;
435 	}
436 	/*
437 	//FIXME: if turbotime is up and we're waiting for it to recharge, should our max speed drop while we recharge?
438 	else if ( (curTime - pVeh->m_iTurboTime)<3000 )
439 	{//still waiting for the recharge
440 		speedMax = pVeh->m_pVehicleInfo->speedMax*0.75;
441 	}
442 	*/
443 	else
444 	{//normal max speed
445 		speedMax = pVeh->m_pVehicleInfo->speedMax;
446 		if ( (parentPS->eFlags&EF_JETPACK_ACTIVE) )
447 		{//stop cgame from playing the turbo exhaust effect
448 			parentPS->eFlags &= ~EF_JETPACK_ACTIVE;
449 		}
450 	}
451 	speedIdleDec = pVeh->m_pVehicleInfo->decelIdle * pVeh->m_fTimeModifier;
452 	speedIdle = pVeh->m_pVehicleInfo->speedIdle;
453 	speedIdleAccel = pVeh->m_pVehicleInfo->accelIdle * pVeh->m_fTimeModifier;
454 	speedMin = pVeh->m_pVehicleInfo->speedMin;
455 
456 	if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_BACK_HEAVY)) )
457 	{//engine has taken heavy damage
458 		speedMax *= 0.8f;//at 80% speed
459 	}
460 	else if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_BACK_LIGHT)) )
461 	{//engine has taken light damage
462 		speedMax *= 0.6f;//at 60% speed
463 	}
464 
465 	if (pVeh->m_iRemovedSurfaces
466 		|| parentPS->electrifyTime>=curTime)
467 	{ //go out of control
468 		parentPS->speed += speedInc;
469 		//Why set forwardmove?  PMove code doesn't use it... does it?
470 		pVeh->m_ucmd.forwardmove = 127;
471 	}
472 #ifdef _GAME //well, the thing is always going to be inhabited if it's being predicted!
473 	else if ( FighterSuspended( pVeh, parentPS ) )
474 	{
475 		parentPS->speed = 0;
476 		pVeh->m_ucmd.forwardmove = 0;
477 	}
478 	else if ( !pVeh->m_pVehicleInfo->Inhabited( pVeh )
479 		&& parentPS->speed > 0 )
480 	{//pilot jumped out while we were moving forward (not landing or landed) so just keep the throttle locked
481 		//Why set forwardmove?  PMove code doesn't use it... does it?
482 		pVeh->m_ucmd.forwardmove = 127;
483 	}
484 #endif
485 	else if ( ( parentPS->speed || parentPS->groundEntityNum == ENTITYNUM_NONE  ||
486 		 pVeh->m_ucmd.forwardmove || pVeh->m_ucmd.upmove > 0 ) && pVeh->m_LandTrace.fraction >= 0.05f )
487 	{
488 		if ( pVeh->m_ucmd.forwardmove > 0 && speedInc )
489 		{
490 			parentPS->speed += speedInc;
491 			pVeh->m_ucmd.forwardmove = 127;
492 		}
493 		else if ( pVeh->m_ucmd.forwardmove < 0
494 			|| pVeh->m_ucmd.upmove < 0 )
495 		{//decelerating or braking
496 			if ( pVeh->m_ucmd.upmove < 0 )
497 			{//braking (trying to land?), slow down faster
498 				if ( pVeh->m_ucmd.forwardmove )
499 				{//decelerator + brakes
500 					speedInc += pVeh->m_pVehicleInfo->braking;
501 					speedIdleDec += pVeh->m_pVehicleInfo->braking;
502 				}
503 				else
504 				{//just brakes
505 					speedInc = speedIdleDec = pVeh->m_pVehicleInfo->braking;
506 				}
507 			}
508 			if ( parentPS->speed > speedIdle )
509 			{
510 				parentPS->speed -= speedInc;
511 			}
512 			else if ( parentPS->speed > speedMin )
513 			{
514 				if ( FighterOverValidLandingSurface( pVeh ) )
515 				{//there's ground below us and we're trying to slow down, slow down faster
516 					parentPS->speed -= speedInc;
517 				}
518 				else
519 				{
520 					parentPS->speed -= speedIdleDec;
521 					if ( parentPS->speed < MIN_LANDING_SPEED )
522 					{//unless you can land, don't drop below the landing speed!!!  This way you can't come to a dead stop in mid-air
523 						parentPS->speed = MIN_LANDING_SPEED;
524 					}
525 				}
526 			}
527 			if ( pVeh->m_pVehicleInfo->type == VH_FIGHTER )
528 			{
529 				pVeh->m_ucmd.forwardmove = 127;
530 			}
531 			else if ( speedMin >= 0 )
532 			{
533 				pVeh->m_ucmd.forwardmove = 0;
534 			}
535 		}
536 		//else not accel, decel or braking
537 		else if ( pVeh->m_pVehicleInfo->throttleSticks )
538 		{//we're using a throttle that sticks at current speed
539 			if ( parentPS->speed <= MIN_LANDING_SPEED )
540 			{//going less than landing speed
541 				if ( FighterOverValidLandingSurface( pVeh ) )
542 				{//close to ground and not going very fast
543 					//slow to a stop if within landing height and not accel/decel/braking
544 					if ( parentPS->speed > 0 )
545 					{//slow down
546 						parentPS->speed -= speedIdleDec;
547 					}
548 					else if ( parentPS->speed < 0 )
549 					{//going backwards, slow down
550 						parentPS->speed += speedIdleDec;
551 					}
552 				}
553 				else
554 				{//not over a valid landing surf, but going too slow
555 					//speed up to idle speed if not over a valid landing surf and not accel/decel/braking
556 					if ( parentPS->speed < speedIdle )
557 					{
558 						parentPS->speed += speedIdleAccel;
559 						if ( parentPS->speed > speedIdle )
560 						{
561 							parentPS->speed = speedIdle;
562 						}
563 					}
564 				}
565 			}
566 		}
567 		else
568 		{//then speed up or slow down to idle speed
569 			//accelerate to cruising speed only, otherwise, just coast
570 			// If they've launched, apply some constant motion.
571 			if ( (pVeh->m_LandTrace.fraction >= 1.0f //no ground
572 					|| pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE )//or can't land on ground below us
573 				&& speedIdle > 0 )
574 			{//not above ground and have an idle speed
575 				//float fSpeed = pVeh->m_pParentEntity->client->ps.speed;
576 				if ( parentPS->speed < speedIdle )
577 				{
578 					parentPS->speed += speedIdleAccel;
579 					if ( parentPS->speed > speedIdle )
580 					{
581 						parentPS->speed = speedIdle;
582 					}
583 				}
584 				else if ( parentPS->speed > 0 )
585 				{//slow down
586 					parentPS->speed -= speedIdleDec;
587 
588 					if ( parentPS->speed < speedIdle )
589 					{
590 						parentPS->speed = speedIdle;
591 					}
592 				}
593 			}
594 			else//either close to ground or no idle speed
595 			{//slow to a stop if no idle speed or within landing height and not accel/decel/braking
596 				if ( parentPS->speed > 0 )
597 				{//slow down
598 					parentPS->speed -= speedIdleDec;
599 				}
600 				else if ( parentPS->speed < 0 )
601 				{//going backwards, slow down
602 					parentPS->speed += speedIdleDec;
603 				}
604 			}
605 		}
606 	}
607 	else
608 	{
609 		if ( pVeh->m_ucmd.forwardmove < 0 )
610 		{
611 			pVeh->m_ucmd.forwardmove = 0;
612 		}
613 		if ( pVeh->m_ucmd.upmove < 0 )
614 		{
615 			pVeh->m_ucmd.upmove = 0;
616 		}
617 	}
618 
619 #if 1//This is working now, but there are some transitional jitters... Rich?
620 //STRAFING==============================================================================
621 	if ( pVeh->m_pVehicleInfo->strafePerc
622 #ifdef _GAME//only do this check on game side, because if it's cgame, it's being predicted, and it's only predicted if the local client is the driver
623 		&& pVeh->m_pVehicleInfo->Inhabited( pVeh )//has to have a driver in order to be capable of landing
624 #endif
625 		&& !pVeh->m_iRemovedSurfaces
626 		&& parentPS->electrifyTime<curTime
627 		&& (pVeh->m_LandTrace.fraction >= 1.0f//no grounf
628 			||pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE//can't land here
629 			||parentPS->speed>MIN_LANDING_SPEED)//going too fast to land
630 		&& pVeh->m_ucmd.rightmove )
631 	{//strafe
632 		vec3_t vAngles, vRight;
633 		float strafeSpeed = (pVeh->m_pVehicleInfo->strafePerc*speedMax)*5.0f;
634 		VectorCopy( pVeh->m_vOrientation, vAngles );
635 		vAngles[PITCH] = vAngles[ROLL] = 0;
636 		AngleVectors( vAngles, NULL, vRight, NULL );
637 
638 		if ( pVeh->m_ucmd.rightmove > 0 )
639 		{//strafe right
640 			//FIXME: this will probably make it possible to cheat and
641 			//		go faster than max speed if you keep turning and strafing...
642 			if ( parentPS->hackingTime > -MAX_STRAFE_TIME )
643 			{//can strafe right for 2 seconds
644 				float curStrafeSpeed = DotProduct( parentPS->velocity, vRight );
645 				if ( curStrafeSpeed > 0.0f )
646 				{//if > 0, already strafing right
647 					strafeSpeed -= curStrafeSpeed;//so it doesn't add up
648 				}
649 				if ( strafeSpeed > 0 )
650 				{
651 					VectorMA( parentPS->velocity, strafeSpeed*pVeh->m_fTimeModifier, vRight, parentPS->velocity );
652 				}
653 				parentPS->hackingTime -= 50*pVeh->m_fTimeModifier;
654 			}
655 		}
656 		else
657 		{//strafe left
658 			if ( parentPS->hackingTime < MAX_STRAFE_TIME )
659 			{//can strafe left for 2 seconds
660 				float curStrafeSpeed = DotProduct( parentPS->velocity, vRight );
661 				if ( curStrafeSpeed < 0.0f )
662 				{//if < 0, already strafing left
663 					strafeSpeed += curStrafeSpeed;//so it doesn't add up
664 				}
665 				if ( strafeSpeed > 0 )
666 				{
667 					VectorMA( parentPS->velocity, -strafeSpeed*pVeh->m_fTimeModifier, vRight, parentPS->velocity );
668 				}
669 				parentPS->hackingTime += 50*pVeh->m_fTimeModifier;
670 			}
671 		}
672 		//strafing takes away from forward speed?  If so, strafePerc above should use speedMax
673 		//parentPS->speed *= (1.0f-pVeh->m_pVehicleInfo->strafePerc);
674 	}
675 	else//if ( parentPS->hackingTimef )
676 	{
677 		if ( parentPS->hackingTime > 0 )
678 		{
679 			parentPS->hackingTime -= 50*pVeh->m_fTimeModifier;
680 			if ( parentPS->hackingTime < 0 )
681 			{
682 				parentPS->hackingTime = 0.0f;
683 			}
684 		}
685 		else if ( parentPS->hackingTime < 0 )
686 		{
687 			parentPS->hackingTime += 50*pVeh->m_fTimeModifier;
688 			if ( parentPS->hackingTime > 0 )
689 			{
690 				parentPS->hackingTime = 0.0f;
691 			}
692 		}
693 	}
694 //STRAFING==============================================================================
695 #endif
696 
697 	if ( parentPS->speed > speedMax )
698 	{
699 		parentPS->speed = speedMax;
700 	}
701 	else if ( parentPS->speed < speedMin )
702 	{
703 		parentPS->speed = speedMin;
704 	}
705 
706 #ifdef _GAME//FIXME: get working in game and cgame
707 	if ((pVeh->m_vOrientation[PITCH]*0.1f) > 10.0f)
708 	{ //pitched downward, increase speed more and more based on our tilt
709 		if ( FighterIsInSpace( (gentity_t *)parent ) )
710 		{//in space, do nothing with speed base on pitch...
711 		}
712 		else
713 		{
714 			//really should only do this when on a planet
715 			float mult = pVeh->m_vOrientation[PITCH]*0.1f;
716 			if (mult < 1.0f)
717 			{
718 				mult = 1.0f;
719 			}
720 			parentPS->speed = PredictedAngularDecrement(mult, pVeh->m_fTimeModifier*10.0f, parentPS->speed);
721 		}
722 	}
723 
724 	if (pVeh->m_iRemovedSurfaces
725 		|| parentPS->electrifyTime>=curTime)
726 	{ //going down
727 		if ( FighterIsInSpace( (gentity_t *)parent ) )
728 		{//we're in a valid trigger_space brush
729 			//simulate randomness
730 			if ( !(parent->s.number&3) )
731 			{//even multiple of 3, don't do anything
732 				parentPS->gravity = 0;
733 			}
734 			else if ( !(parent->s.number&2) )
735 			{//even multiple of 2, go up
736 				parentPS->gravity = -500.0f;
737 				parentPS->velocity[2] = 80.0f;
738 			}
739 			else
740 			{//odd number, go down
741 				parentPS->gravity = 500.0f;
742 				parentPS->velocity[2] = -80.0f;
743 			}
744 		}
745 		else
746 		{//over a planet
747 			parentPS->gravity = 500.0f;
748 			parentPS->velocity[2] = -80.0f;
749 		}
750 	}
751 	else if ( FighterSuspended( pVeh, parentPS ) )
752 	{
753 		parentPS->gravity = 0;
754 	}
755 	else if ( (!parentPS->speed||parentPS->speed < speedIdle) && pVeh->m_ucmd.upmove <= 0 )
756 	{//slowing down or stopped and not trying to take off
757 		if ( FighterIsInSpace( (gentity_t *)parent ) )
758 		{//we're in space, stopping doesn't make us drift downward
759 			if ( FighterOverValidLandingSurface( pVeh ) )
760 			{//well, there's something below us to land on, so go ahead and lower us down to it
761 				parentPS->gravity = (speedIdle - parentPS->speed)/4;
762 			}
763 		}
764 		else
765 		{//over a planet
766 			parentPS->gravity = (speedIdle - parentPS->speed)/4;
767 		}
768 	}
769 	else
770 	{
771 		parentPS->gravity = 0;
772 	}
773 #else//FIXME: get above checks working in game and cgame
774 	parentPS->gravity = 0;
775 #endif
776 
777 	/********************************************************************************/
778 	/*	END Here is where we move the vehicle (forward or back or whatever). END	*/
779 	/********************************************************************************/
780 }
781 
782 extern void BG_VehicleTurnRateForSpeed( Vehicle_t *pVeh, float speed, float *mPitchOverride, float *mYawOverride );
FighterWingMalfunctionCheck(Vehicle_t * pVeh,playerState_t * parentPS)783 static void FighterWingMalfunctionCheck( Vehicle_t *pVeh, playerState_t *parentPS )
784 {
785 	float mPitchOverride = 1.0f;
786 	float mYawOverride = 1.0f;
787 	BG_VehicleTurnRateForSpeed( pVeh, parentPS->speed, &mPitchOverride, &mYawOverride );
788 	//check right wing damage
789 	if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_RIGHT_HEAVY)) )
790 	{//right wing has taken heavy damage
791 		pVeh->m_vOrientation[ROLL] += (sin( pVeh->m_ucmd.serverTime*0.001 )+1.0f)*pVeh->m_fTimeModifier*mYawOverride*50.0f;
792 	}
793 	else if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_RIGHT_LIGHT)) )
794 	{//right wing has taken light damage
795 		pVeh->m_vOrientation[ROLL] += (sin( pVeh->m_ucmd.serverTime*0.001 )+1.0f)*pVeh->m_fTimeModifier*mYawOverride*12.5f;
796 	}
797 
798 	//check left wing damage
799 	if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_LEFT_HEAVY)) )
800 	{//left wing has taken heavy damage
801 		pVeh->m_vOrientation[ROLL] -= (sin( pVeh->m_ucmd.serverTime*0.001 )+1.0f)*pVeh->m_fTimeModifier*mYawOverride*50.0f;
802 	}
803 	else if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_LEFT_LIGHT)) )
804 	{//left wing has taken light damage
805 		pVeh->m_vOrientation[ROLL] -= (sin( pVeh->m_ucmd.serverTime*0.001 )+1.0f)*pVeh->m_fTimeModifier*mYawOverride*12.5f;
806 	}
807 
808 }
809 
FighterNoseMalfunctionCheck(Vehicle_t * pVeh,playerState_t * parentPS)810 static void FighterNoseMalfunctionCheck( Vehicle_t *pVeh, playerState_t *parentPS )
811 {
812 	float mPitchOverride = 1.0f;
813 	float mYawOverride = 1.0f;
814 	BG_VehicleTurnRateForSpeed( pVeh, parentPS->speed, &mPitchOverride, &mYawOverride );
815 	//check nose damage
816 	if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_FRONT_HEAVY)) )
817 	{//nose has taken heavy damage
818 		//pitch up and down over time
819 		pVeh->m_vOrientation[PITCH] += sin( pVeh->m_ucmd.serverTime*0.001 )*pVeh->m_fTimeModifier*mPitchOverride*50.0f;
820 	}
821 	else if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_FRONT_LIGHT)) )
822 	{//nose has taken heavy damage
823 		//pitch up and down over time
824 		pVeh->m_vOrientation[PITCH] += sin( pVeh->m_ucmd.serverTime*0.001 )*pVeh->m_fTimeModifier*mPitchOverride*20.0f;
825 	}
826 }
827 
FighterDamageRoutine(Vehicle_t * pVeh,bgEntity_t * parent,playerState_t * parentPS,playerState_t * riderPS,qboolean isDead)828 static void FighterDamageRoutine( Vehicle_t *pVeh, bgEntity_t *parent, playerState_t *parentPS, playerState_t *riderPS, qboolean isDead )
829 {
830  	if ( !pVeh->m_iRemovedSurfaces )
831 	{//still in one piece
832 		if ( pVeh->m_pParentEntity && isDead )
833 		{//death spiral
834 			pVeh->m_ucmd.upmove = 0;
835 			//FIXME: don't bias toward pitching down when not in space
836 			/*
837 			if ( FighterIsInSpace( pVeh->m_pParentEntity ) )
838 			{
839 			}
840 			else
841 			*/
842 			if ( !(pVeh->m_pParentEntity->s.number%3) )
843 			{//NOT everyone should do this
844 				pVeh->m_vOrientation[PITCH] += pVeh->m_fTimeModifier;
845 				if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
846 				{
847 					if ( pVeh->m_vOrientation[PITCH] > 60.0f )
848 					{
849 						pVeh->m_vOrientation[PITCH] = 60.0f;
850 					}
851 				}
852 			}
853 			else if ( !(pVeh->m_pParentEntity->s.number%2) )
854 			{
855 				pVeh->m_vOrientation[PITCH] -= pVeh->m_fTimeModifier;
856 				if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
857 				{
858 					if ( pVeh->m_vOrientation[PITCH] > -60.0f )
859 					{
860 						pVeh->m_vOrientation[PITCH] = -60.0f;
861 					}
862 				}
863 			}
864 			if ( (pVeh->m_pParentEntity->s.number%2) )
865 			{
866 				pVeh->m_vOrientation[YAW] += pVeh->m_fTimeModifier;
867 				pVeh->m_vOrientation[ROLL] += pVeh->m_fTimeModifier*4.0f;
868 			}
869 			else
870 			{
871 				pVeh->m_vOrientation[YAW] -= pVeh->m_fTimeModifier;
872 				pVeh->m_vOrientation[ROLL] -= pVeh->m_fTimeModifier*4.0f;
873 			}
874 		}
875 		return;
876 	}
877 
878 	//if we get into here we have at least one broken piece
879 	pVeh->m_ucmd.upmove = 0;
880 
881 	//if you're off the ground and not suspended, pitch down
882 	//FIXME: not in space!
883 	if ( pVeh->m_LandTrace.fraction >= 0.1f )
884 	{
885 		if ( !FighterSuspended( pVeh, parentPS ) )
886 		{
887 			//pVeh->m_ucmd.forwardmove = 0;
888 			//FIXME: don't bias towards pitching down when in space...
889 			if ( !(pVeh->m_pParentEntity->s.number%2) )
890 			{//NOT everyone should do this
891 				pVeh->m_vOrientation[PITCH] += pVeh->m_fTimeModifier;
892 				if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
893 				{
894 					if ( pVeh->m_vOrientation[PITCH] > 60.0f )
895 					{
896 						pVeh->m_vOrientation[PITCH] = 60.0f;
897 					}
898 				}
899 			}
900 			else if ( !(pVeh->m_pParentEntity->s.number%3) )
901 			{
902 				pVeh->m_vOrientation[PITCH] -= pVeh->m_fTimeModifier;
903 				if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
904 				{
905 					if ( pVeh->m_vOrientation[PITCH] > -60.0f )
906 					{
907 						pVeh->m_vOrientation[PITCH] = -60.0f;
908 					}
909 				}
910 			}
911 			//else: just keep going forward
912 		}
913 	}
914 #ifdef _GAME
915 	if ( pVeh->m_LandTrace.fraction < 1.0f )
916 	{ //if you land at all when pieces of your ship are missing, then die
917 		gentity_t *vparent = (gentity_t *)pVeh->m_pParentEntity;
918 		gentity_t *killer = vparent;
919 		if (vparent->client->ps.otherKiller < ENTITYNUM_WORLD &&
920 			vparent->client->ps.otherKillerTime > level.time)
921 		{
922 			gentity_t *potentialKiller = &g_entities[vparent->client->ps.otherKiller];
923 
924 			if (potentialKiller->inuse && potentialKiller->client)
925 			{ //he's valid I guess
926 				killer = potentialKiller;
927 			}
928 		}
929 		G_Damage(vparent, killer, killer, vec3_origin, vparent->client->ps.origin, 99999, DAMAGE_NO_ARMOR, MOD_SUICIDE);
930 	}
931 #endif
932 
933 	if ( ((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_C) ||
934 		(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_D)) &&
935 		((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_E) ||
936 		(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_F)) )
937 	{ //wings on both side broken
938 		float factor = 2.0f;
939 		if ((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_E) &&
940 			(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_F) &&
941 			(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_C) &&
942 			(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_D))
943 		{ //all wings broken
944 			factor *= 2.0f;
945 		}
946 
947 		if ( !(pVeh->m_pParentEntity->s.number%4)||!(pVeh->m_pParentEntity->s.number%5) )
948 		{//won't yaw, so increase roll factor
949 			factor *= 4.0f;
950 		}
951 
952 		pVeh->m_vOrientation[ROLL] += (pVeh->m_fTimeModifier*factor); //do some spiralling
953 	}
954 	else if ((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_C) ||
955 		(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_D))
956 	{ //left wing broken
957 		float factor = 2.0f;
958 		if ((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_C) &&
959 			(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_D))
960 		{ //if both are broken..
961 			factor *= 2.0f;
962 		}
963 
964 		if ( !(pVeh->m_pParentEntity->s.number%4)||!(pVeh->m_pParentEntity->s.number%5) )
965 		{//won't yaw, so increase roll factor
966 			factor *= 4.0f;
967 		}
968 
969 		pVeh->m_vOrientation[ROLL] += factor*pVeh->m_fTimeModifier;
970 	}
971 	else if ((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_E) ||
972 		(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_F))
973 	{ //right wing broken
974 		float factor = 2.0f;
975 		if ((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_E) &&
976 			(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_F))
977 		{ //if both are broken..
978 			factor *= 2.0f;
979 		}
980 
981 		if ( !(pVeh->m_pParentEntity->s.number%4)||!(pVeh->m_pParentEntity->s.number%5) )
982 		{//won't yaw, so increase roll factor
983 			factor *= 4.0f;
984 		}
985 
986 		pVeh->m_vOrientation[ROLL] -= factor*pVeh->m_fTimeModifier;
987 	}
988 }
989 
990 #ifdef VEH_CONTROL_SCHEME_4
991 
992 #define FIGHTER_TURNING_MULTIPLIER 0.8f//was 1.6f //magic number hackery
993 #define FIGHTER_TURNING_DEADZONE 0.25f//no turning if offset is this much
FighterRollAdjust(Vehicle_t * pVeh,playerState_t * riderPS,playerState_t * parentPS)994 void FighterRollAdjust(Vehicle_t *pVeh, playerState_t *riderPS, playerState_t *parentPS)
995 {
996 /*
997 	float angDif = AngleSubtract(pVeh->m_vOrientation[YAW], riderPS->viewangles[YAW]);
998 */
999 	float angDif = AngleSubtract(pVeh->m_vPrevRiderViewAngles[YAW],riderPS->viewangles[YAW]);///2.0f;//AngleSubtract(pVeh->m_vPrevRiderViewAngles[YAW], riderPS->viewangles[YAW]);
1000 	/*
1001 	if ( fabs( angDif ) < FIGHTER_TURNING_DEADZONE )
1002 	{
1003 		angDif = 0.0f;
1004 	}
1005 	else if ( angDif >= FIGHTER_TURNING_DEADZONE )
1006 	{
1007 		angDif -= FIGHTER_TURNING_DEADZONE;
1008 	}
1009 	else if ( angDif <= -FIGHTER_TURNING_DEADZONE )
1010 	{
1011 		angDif += FIGHTER_TURNING_DEADZONE;
1012 	}
1013 	*/
1014 
1015 	angDif *= 0.5f;
1016 	if ( angDif > 0.0f )
1017 	{
1018 		angDif *= angDif;
1019 	}
1020 	else if ( angDif < 0.0f )
1021 	{
1022 		angDif *= -angDif;
1023 	}
1024 
1025 	if (parentPS && parentPS->speed)
1026 	{
1027 		float maxDif = pVeh->m_pVehicleInfo->turningSpeed*FIGHTER_TURNING_MULTIPLIER;
1028 
1029 		if ( pVeh->m_pVehicleInfo->speedDependantTurning )
1030 		{
1031 			float speedFrac = 1.0f;
1032 			if ( pVeh->m_LandTrace.fraction >= 1.0f
1033 				|| pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE  )
1034 			{
1035 				float s = parentPS->speed;
1036 				if (s < 0.0f)
1037 				{
1038 					s = -s;
1039 				}
1040 				speedFrac = (s/(pVeh->m_pVehicleInfo->speedMax*0.75f));
1041 				if ( speedFrac < 0.25f )
1042 				{
1043 					speedFrac = 0.25f;
1044 				}
1045 				else if ( speedFrac > 1.0f )
1046 				{
1047 					speedFrac = 1.0f;
1048 				}
1049 			}
1050 			angDif *= speedFrac;
1051 		}
1052 		if (angDif > maxDif)
1053 		{
1054 			angDif = maxDif;
1055 		}
1056 		else if (angDif < -maxDif)
1057 		{
1058 			angDif = -maxDif;
1059 		}
1060 		pVeh->m_vOrientation[ROLL] = AngleNormalize180(pVeh->m_vOrientation[ROLL] + angDif*(pVeh->m_fTimeModifier*0.2f));
1061 	}
1062 }
1063 
FighterYawAdjust(Vehicle_t * pVeh,playerState_t * riderPS,playerState_t * parentPS)1064 void FighterYawAdjust(Vehicle_t *pVeh, playerState_t *riderPS, playerState_t *parentPS)
1065 {
1066 	float angDif = AngleSubtract(pVeh->m_vPrevRiderViewAngles[YAW],riderPS->viewangles[YAW]);///2.0f;//AngleSubtract(pVeh->m_vPrevRiderViewAngles[YAW], riderPS->viewangles[YAW]);
1067 	if ( fabs( angDif ) < FIGHTER_TURNING_DEADZONE )
1068 	{
1069 		angDif = 0.0f;
1070 	}
1071 	else if ( angDif >= FIGHTER_TURNING_DEADZONE )
1072 	{
1073 		angDif -= FIGHTER_TURNING_DEADZONE;
1074 	}
1075 	else if ( angDif <= -FIGHTER_TURNING_DEADZONE )
1076 	{
1077 		angDif += FIGHTER_TURNING_DEADZONE;
1078 	}
1079 
1080 	angDif *= 0.5f;
1081 	if ( angDif > 0.0f )
1082 	{
1083 		angDif *= angDif;
1084 	}
1085 	else if ( angDif < 0.0f )
1086 	{
1087 		angDif *= -angDif;
1088 	}
1089 
1090 	if (parentPS && parentPS->speed)
1091 	{
1092 		float maxDif = pVeh->m_pVehicleInfo->turningSpeed*FIGHTER_TURNING_MULTIPLIER;
1093 
1094 		if ( pVeh->m_pVehicleInfo->speedDependantTurning )
1095 		{
1096 			float speedFrac = 1.0f;
1097 			if ( pVeh->m_LandTrace.fraction >= 1.0f
1098 				|| pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE  )
1099 			{
1100 				float s = parentPS->speed;
1101 				if (s < 0.0f)
1102 				{
1103 					s = -s;
1104 				}
1105 				speedFrac = (s/(pVeh->m_pVehicleInfo->speedMax*0.75f));
1106 				if ( speedFrac < 0.25f )
1107 				{
1108 					speedFrac = 0.25f;
1109 				}
1110 				else if ( speedFrac > 1.0f )
1111 				{
1112 					speedFrac = 1.0f;
1113 				}
1114 			}
1115 			angDif *= speedFrac;
1116 		}
1117 		if (angDif > maxDif)
1118 		{
1119 			angDif = maxDif;
1120 		}
1121 		else if (angDif < -maxDif)
1122 		{
1123 			angDif = -maxDif;
1124 		}
1125 		pVeh->m_vOrientation[YAW] = AngleNormalize180(pVeh->m_vOrientation[YAW] - (angDif*(pVeh->m_fTimeModifier*0.2f)) );
1126 	}
1127 }
1128 
FighterPitchAdjust(Vehicle_t * pVeh,playerState_t * riderPS,playerState_t * parentPS)1129 void FighterPitchAdjust(Vehicle_t *pVeh, playerState_t *riderPS, playerState_t *parentPS)
1130 {
1131 	float angDif = AngleSubtract(0,riderPS->viewangles[PITCH]);//AngleSubtract(pVeh->m_vPrevRiderViewAngles[PITCH], riderPS->viewangles[PITCH]);
1132 	if ( fabs( angDif ) < FIGHTER_TURNING_DEADZONE )
1133 	{
1134 		angDif = 0.0f;
1135 	}
1136 	else if ( angDif >= FIGHTER_TURNING_DEADZONE )
1137 	{
1138 		angDif -= FIGHTER_TURNING_DEADZONE;
1139 	}
1140 	else if ( angDif <= -FIGHTER_TURNING_DEADZONE )
1141 	{
1142 		angDif += FIGHTER_TURNING_DEADZONE;
1143 	}
1144 
1145 	angDif *= 0.5f;
1146 	if ( angDif > 0.0f )
1147 	{
1148 		angDif *= angDif;
1149 	}
1150 	else if ( angDif < 0.0f )
1151 	{
1152 		angDif *= -angDif;
1153 	}
1154 
1155 	if (parentPS && parentPS->speed)
1156 	{
1157 		float maxDif = pVeh->m_pVehicleInfo->turningSpeed*FIGHTER_TURNING_MULTIPLIER;
1158 
1159 		if ( pVeh->m_pVehicleInfo->speedDependantTurning )
1160 		{
1161 			float speedFrac = 1.0f;
1162 			if ( pVeh->m_LandTrace.fraction >= 1.0f
1163 				|| pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE  )
1164 			{
1165 				float s = parentPS->speed;
1166 				if (s < 0.0f)
1167 				{
1168 					s = -s;
1169 				}
1170 				speedFrac = (s/(pVeh->m_pVehicleInfo->speedMax*0.75f));
1171 				if ( speedFrac < 0.25f )
1172 				{
1173 					speedFrac = 0.25f;
1174 				}
1175 				else if ( speedFrac > 1.0f )
1176 				{
1177 					speedFrac = 1.0f;
1178 				}
1179 			}
1180 			angDif *= speedFrac;
1181 		}
1182 		if (angDif > maxDif)
1183 		{
1184 			angDif = maxDif;
1185 		}
1186 		else if (angDif < -maxDif)
1187 		{
1188 			angDif = -maxDif;
1189 		}
1190 		pVeh->m_vOrientation[PITCH] = AngleNormalize180(pVeh->m_vOrientation[PITCH] - (angDif*(pVeh->m_fTimeModifier*0.2f)) );
1191 	}
1192 }
1193 
1194 #else// VEH_CONTROL_SCHEME_4
1195 
FighterYawAdjust(Vehicle_t * pVeh,playerState_t * riderPS,playerState_t * parentPS)1196 void FighterYawAdjust(Vehicle_t *pVeh, playerState_t *riderPS, playerState_t *parentPS)
1197 {
1198 	float angDif = AngleSubtract(pVeh->m_vOrientation[YAW], riderPS->viewangles[YAW]);
1199 
1200 	if (parentPS && parentPS->speed)
1201 	{
1202 		float s = parentPS->speed;
1203 		float maxDif = pVeh->m_pVehicleInfo->turningSpeed*0.8f; //magic number hackery
1204 
1205 		if (s < 0.0f)
1206 		{
1207 			s = -s;
1208 		}
1209 		angDif *= s/pVeh->m_pVehicleInfo->speedMax;
1210 		if (angDif > maxDif)
1211 		{
1212 			angDif = maxDif;
1213 		}
1214 		else if (angDif < -maxDif)
1215 		{
1216 			angDif = -maxDif;
1217 		}
1218 		pVeh->m_vOrientation[YAW] = AngleNormalize180(pVeh->m_vOrientation[YAW] - angDif*(pVeh->m_fTimeModifier*0.2f));
1219 	}
1220 }
1221 
FighterPitchAdjust(Vehicle_t * pVeh,playerState_t * riderPS,playerState_t * parentPS)1222 void FighterPitchAdjust(Vehicle_t *pVeh, playerState_t *riderPS, playerState_t *parentPS)
1223 {
1224 	float angDif = AngleSubtract(pVeh->m_vOrientation[PITCH], riderPS->viewangles[PITCH]);
1225 
1226 	if (parentPS && parentPS->speed)
1227 	{
1228 		float s = parentPS->speed;
1229 		float maxDif = pVeh->m_pVehicleInfo->turningSpeed*0.8f; //magic number hackery
1230 
1231 		if (s < 0.0f)
1232 		{
1233 			s = -s;
1234 		}
1235 		angDif *= s/pVeh->m_pVehicleInfo->speedMax;
1236 		if (angDif > maxDif)
1237 		{
1238 			angDif = maxDif;
1239 		}
1240 		else if (angDif < -maxDif)
1241 		{
1242 			angDif = -maxDif;
1243 		}
1244 		pVeh->m_vOrientation[PITCH] = AngleNormalize360(pVeh->m_vOrientation[PITCH] - angDif*(pVeh->m_fTimeModifier*0.2f));
1245 	}
1246 }
1247 #endif// VEH_CONTROL_SCHEME_4
1248 
FighterPitchClamp(Vehicle_t * pVeh,playerState_t * riderPS,playerState_t * parentPS,int curTime)1249 void FighterPitchClamp(Vehicle_t *pVeh, playerState_t *riderPS, playerState_t *parentPS, int curTime )
1250 {
1251 	if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
1252 	{//cap pitch reasonably
1253 		if ( pVeh->m_pVehicleInfo->pitchLimit != -1
1254 			&& !pVeh->m_iRemovedSurfaces
1255 			&& parentPS->electrifyTime < curTime )
1256 		{
1257 			if (pVeh->m_vOrientation[PITCH] > pVeh->m_pVehicleInfo->pitchLimit )
1258 			{
1259 				pVeh->m_vOrientation[PITCH] = pVeh->m_pVehicleInfo->pitchLimit;
1260 			}
1261 			else if (pVeh->m_vOrientation[PITCH] < -pVeh->m_pVehicleInfo->pitchLimit)
1262 			{
1263 				pVeh->m_vOrientation[PITCH] = -pVeh->m_pVehicleInfo->pitchLimit;
1264 			}
1265 		}
1266 	}
1267 }
1268 
1269 //MP RULE - ALL PROCESSORIENTCOMMANDS FUNCTIONS MUST BE BG-COMPATIBLE!!!
1270 //If you really need to violate this rule for SP, then use ifdefs.
1271 //By BG-compatible, I mean no use of game-specific data - ONLY use
1272 //stuff available in the MP bgEntity (in SP, the bgEntity is #defined
1273 //as a gentity, but the MP-compatible access restrictions are based
1274 //on the bgEntity structure in the MP codebase) -rww
1275 // ProcessOrientCommands the Vehicle.
ProcessOrientCommands(Vehicle_t * pVeh)1276 static void ProcessOrientCommands( Vehicle_t *pVeh )
1277 {
1278 	/********************************************************************************/
1279 	/*	BEGIN	Here is where make sure the vehicle is properly oriented.	BEGIN	*/
1280 	/********************************************************************************/
1281 
1282 	bgEntity_t *parent = pVeh->m_pParentEntity;
1283 	playerState_t *parentPS, *riderPS;
1284 	float angleTimeMod;
1285 #ifdef _GAME
1286 	const float groundFraction = 0.1f;
1287 #endif
1288 	float	curRoll = 0.0f;
1289 	qboolean isDead = qfalse;
1290 	qboolean isLandingOrLanded = qfalse;
1291 #ifdef _GAME
1292 	int curTime = level.time;
1293 #elif _CGAME
1294 	//FIXME: pass in ucmd?  Not sure if this is reliable...
1295 	int curTime = pm->cmd.serverTime;
1296 #endif
1297 
1298 	bgEntity_t *rider = NULL;
1299 	if (parent->s.owner != ENTITYNUM_NONE)
1300 	{
1301 		rider = PM_BGEntForNum(parent->s.owner); //&g_entities[parent->r.ownerNum];
1302 	}
1303 
1304 	if ( !rider )
1305 	{
1306 		rider = parent;
1307 	}
1308 
1309 	parentPS = parent->playerState;
1310 	riderPS = rider->playerState;
1311 	isDead = (qboolean)((parentPS->eFlags&EF_DEAD)!=0);
1312 
1313 #ifdef VEH_CONTROL_SCHEME_4
1314 	if ( parentPS->hyperSpaceTime
1315 		&& (curTime - parentPS->hyperSpaceTime) < HYPERSPACE_TIME )
1316 	{//Going to Hyperspace
1317 		//VectorCopy( riderPS->viewangles, pVeh->m_vOrientation );
1318 		//VectorCopy( riderPS->viewangles, parentPS->viewangles );
1319 		VectorCopy( parentPS->viewangles, pVeh->m_vOrientation );
1320 		VectorClear( pVeh->m_vPrevRiderViewAngles );
1321 		pVeh->m_vPrevRiderViewAngles[YAW] = AngleNormalize180(riderPS->viewangles[YAW]);
1322 		FighterPitchClamp( pVeh, riderPS, parentPS, curTime );
1323 		return;
1324 	}
1325 	else if ( parentPS->vehTurnaroundIndex
1326 		&& parentPS->vehTurnaroundTime > curTime )
1327 	{//in turn-around brush
1328 		//VectorCopy( riderPS->viewangles, pVeh->m_vOrientation );
1329 		//VectorCopy( riderPS->viewangles, parentPS->viewangles );
1330 		VectorCopy( parentPS->viewangles, pVeh->m_vOrientation );
1331 		VectorClear( pVeh->m_vPrevRiderViewAngles );
1332 		pVeh->m_vPrevRiderViewAngles[YAW] = AngleNormalize180(riderPS->viewangles[YAW]);
1333 		FighterPitchClamp( pVeh, riderPS, parentPS, curTime );
1334 		return;
1335 	}
1336 
1337 #else// VEH_CONTROL_SCHEME_4
1338 
1339 	if ( parentPS->hyperSpaceTime
1340 		&& (curTime - parentPS->hyperSpaceTime) < HYPERSPACE_TIME )
1341 	{//Going to Hyperspace
1342 		VectorCopy( riderPS->viewangles, pVeh->m_vOrientation );
1343 		VectorCopy( riderPS->viewangles, parentPS->viewangles );
1344 		return;
1345 	}
1346 #endif// VEH_CONTROL_SCHEME_4
1347 
1348 	if ( pVeh->m_iDropTime >= curTime )
1349 	{//you can only YAW during this
1350 		parentPS->viewangles[YAW] = pVeh->m_vOrientation[YAW] = riderPS->viewangles[YAW];
1351 #ifdef VEH_CONTROL_SCHEME_4
1352 		VectorClear( pVeh->m_vPrevRiderViewAngles );
1353 		pVeh->m_vPrevRiderViewAngles[YAW] = AngleNormalize180(riderPS->viewangles[YAW]);
1354 #endif// VEH_CONTROL_SCHEME_4
1355 		return;
1356 	}
1357 
1358 	angleTimeMod = pVeh->m_fTimeModifier;
1359 
1360 	if ( isDead || parentPS->electrifyTime>=curTime ||
1361 		(pVeh->m_pVehicleInfo->surfDestruction &&
1362 		pVeh->m_iRemovedSurfaces &&
1363 		(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_C) &&
1364 		(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_D) &&
1365 		(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_E) &&
1366 		(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_F)) )
1367 	{ //do some special stuff for when all the wings are torn off
1368 		FighterDamageRoutine(pVeh, parent, parentPS, riderPS, isDead);
1369 		pVeh->m_vOrientation[ROLL] = AngleNormalize180( pVeh->m_vOrientation[ROLL] );
1370 		return;
1371 	}
1372 
1373 	if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
1374 	{
1375 		pVeh->m_vOrientation[ROLL] = PredictedAngularDecrement(0.95f, angleTimeMod*2.0f, pVeh->m_vOrientation[ROLL]);
1376 	}
1377 
1378 	isLandingOrLanded = (FighterIsLanding( pVeh, parentPS )||FighterIsLanded( pVeh, parentPS ));
1379 
1380 	if (!isLandingOrLanded)
1381 	{ //don't do this stuff while landed.. I guess. I don't want ships spinning in place, looks silly.
1382 		int m = 0;
1383 		float aVelDif;
1384 		float dForVel;
1385 
1386 		FighterWingMalfunctionCheck( pVeh, parentPS );
1387 
1388 		while (m < 3)
1389 		{
1390 			aVelDif = pVeh->m_vFullAngleVelocity[m];
1391 
1392 			if (aVelDif != 0.0f)
1393 			{
1394 				dForVel = (aVelDif*0.1f)*pVeh->m_fTimeModifier;
1395 				if (dForVel > 1.0f || dForVel < -1.0f)
1396 				{
1397 					pVeh->m_vOrientation[m] += dForVel;
1398 					pVeh->m_vOrientation[m] = AngleNormalize180(pVeh->m_vOrientation[m]);
1399 					if (m == PITCH)
1400 					{ //don't pitch downward into ground even more.
1401 						if (pVeh->m_vOrientation[m] > 90.0f && (pVeh->m_vOrientation[m]-dForVel) < 90.0f)
1402 						{
1403 							pVeh->m_vOrientation[m] = 90.0f;
1404 							pVeh->m_vFullAngleVelocity[m] = -pVeh->m_vFullAngleVelocity[m];
1405 						}
1406 					}
1407 					pVeh->m_vFullAngleVelocity[m] -= dForVel;
1408 				}
1409 				else
1410 				{
1411 					pVeh->m_vFullAngleVelocity[m] = 0.0f;
1412 				}
1413 			}
1414 
1415 			m++;
1416 		}
1417 	}
1418 	else
1419 	{ //clear decr/incr angles once landed.
1420 		VectorClear(pVeh->m_vFullAngleVelocity);
1421 	}
1422 
1423 	curRoll = pVeh->m_vOrientation[ROLL];
1424 
1425 	// If we're landed, we shouldn't be able to do anything but take off.
1426 	if ( isLandingOrLanded //going slow enough to start landing
1427 		&& !pVeh->m_iRemovedSurfaces
1428 		&& parentPS->electrifyTime<curTime)//not spiraling out of control
1429 	{
1430 		if ( parentPS->speed > 0.0f )
1431 		{//Uh... what?  Why?
1432 			if ( pVeh->m_LandTrace.fraction < 0.3f )
1433 			{
1434 				pVeh->m_vOrientation[PITCH] = 0.0f;
1435 			}
1436 			else
1437 			{
1438 				pVeh->m_vOrientation[PITCH] = PredictedAngularDecrement(0.83f, angleTimeMod*10.0f, pVeh->m_vOrientation[PITCH]);
1439 			}
1440 		}
1441 		if ( pVeh->m_LandTrace.fraction > 0.1f
1442 			|| pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE )
1443 		{//off the ground, at least (or not on a valid landing surf)
1444 			// Dampen the turn rate based on the current height.
1445 			FighterYawAdjust(pVeh, riderPS, parentPS);
1446 		}
1447 #ifdef VEH_CONTROL_SCHEME_4
1448 		else
1449 		{
1450 			VectorClear( pVeh->m_vPrevRiderViewAngles );
1451 			pVeh->m_vPrevRiderViewAngles[YAW] = AngleNormalize180(riderPS->viewangles[YAW]);
1452 		}
1453 #endif// VEH_CONTROL_SCHEME_4
1454 	}
1455 	else if ( (pVeh->m_iRemovedSurfaces||parentPS->electrifyTime>=curTime)//spiralling out of control
1456 		&& (!(pVeh->m_pParentEntity->s.number%4)||!(pVeh->m_pParentEntity->s.number%5)) )
1457 	{//no yaw control
1458 	}
1459 	else if ( pVeh->m_pPilot && pVeh->m_pPilot->s.number < MAX_CLIENTS && parentPS->speed > 0.0f )//&& !( pVeh->m_ucmd.forwardmove > 0 && pVeh->m_LandTrace.fraction != 1.0f ) )
1460 	{
1461 #ifdef VEH_CONTROL_SCHEME_4
1462 		if ( 0  )
1463 #else// VEH_CONTROL_SCHEME_4
1464 		if ( BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
1465 #endif// VEH_CONTROL_SCHEME_4
1466 		{
1467 			VectorCopy( riderPS->viewangles, pVeh->m_vOrientation );
1468 			VectorCopy( riderPS->viewangles, parentPS->viewangles );
1469 
1470 			curRoll = pVeh->m_vOrientation[ROLL];
1471 
1472 			FighterNoseMalfunctionCheck( pVeh, parentPS );
1473 
1474 			//VectorCopy( pVeh->m_vOrientation, parentPS->viewangles );
1475 		}
1476 		else
1477 		{
1478 			/*
1479 			float fTurnAmt[3];
1480 			//PITCH
1481 			fTurnAmt[PITCH] = riderPS->viewangles[PITCH] * 0.08f;
1482 			//YAW
1483 			fTurnAmt[YAW] = riderPS->viewangles[YAW] * 0.065f;
1484 			fTurnAmt[YAW] *= fTurnAmt[YAW];
1485 			// Dampen the turn rate based on the current height.
1486 			if ( riderPS->viewangles[YAW] < 0 )
1487 			{//must keep it negative because squaring a negative makes it positive
1488 				fTurnAmt[YAW] = -fTurnAmt[YAW];
1489 			}
1490 			fTurnAmt[YAW] *= pVeh->m_LandTrace.fraction;
1491 			//ROLL
1492 			fTurnAmt[2] = 0.0f;
1493 			*/
1494 
1495 			//Actal YAW
1496 			/*
1497 			pVeh->m_vOrientation[ROLL] = curRoll;
1498 			FighterRollAdjust(pVeh, riderPS, parentPS);
1499 			curRoll = pVeh->m_vOrientation[ROLL];
1500 			*/
1501 			FighterYawAdjust(pVeh, riderPS, parentPS);
1502 
1503 			// If we are not hitting the ground, allow the fighter to pitch up and down.
1504 			if ( !FighterOverValidLandingSurface( pVeh )
1505 				|| parentPS->speed > MIN_LANDING_SPEED )
1506 			//if ( ( pVeh->m_LandTrace.fraction >= 1.0f || pVeh->m_ucmd.forwardmove != 0 ) && pVeh->m_LandTrace.fraction >= 0.0f )
1507 			{
1508 				float fYawDelta;
1509 
1510 				FighterPitchAdjust(pVeh, riderPS, parentPS);
1511 #ifdef VEH_CONTROL_SCHEME_4
1512 				FighterPitchClamp( pVeh, riderPS, parentPS, curTime );
1513 #endif// VEH_CONTROL_SCHEME_4
1514 
1515 				FighterNoseMalfunctionCheck( pVeh, parentPS );
1516 
1517 				// Adjust the roll based on the turn amount and dampen it a little.
1518 				fYawDelta = AngleSubtract(pVeh->m_vOrientation[YAW], pVeh->m_vPrevOrientation[YAW]); //pVeh->m_vOrientation[YAW] - pVeh->m_vPrevOrientation[YAW];
1519 				if ( fYawDelta > 8.0f )
1520 				{
1521 					fYawDelta = 8.0f;
1522 				}
1523 				else if ( fYawDelta < -8.0f )
1524 				{
1525 					fYawDelta = -8.0f;
1526 				}
1527 				curRoll -= fYawDelta;
1528 				curRoll = PredictedAngularDecrement(0.93f, angleTimeMod*2.0f, curRoll);
1529 
1530 				//cap it reasonably
1531 				//NOTE: was hardcoded to 40.0f, now using extern data
1532 #ifdef VEH_CONTROL_SCHEME_4
1533 				if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
1534 				{
1535 					if ( pVeh->m_pVehicleInfo->rollLimit != -1 )
1536 					{
1537 						if (curRoll > pVeh->m_pVehicleInfo->rollLimit )
1538 						{
1539 							curRoll = pVeh->m_pVehicleInfo->rollLimit;
1540 						}
1541 						else if (curRoll < -pVeh->m_pVehicleInfo->rollLimit)
1542 						{
1543 							curRoll = -pVeh->m_pVehicleInfo->rollLimit;
1544 						}
1545 					}
1546 				}
1547 #else// VEH_CONTROL_SCHEME_4
1548 				if ( pVeh->m_pVehicleInfo->rollLimit != -1 )
1549 				{
1550 					if (curRoll > pVeh->m_pVehicleInfo->rollLimit )
1551 					{
1552 						curRoll = pVeh->m_pVehicleInfo->rollLimit;
1553 					}
1554 					else if (curRoll < -pVeh->m_pVehicleInfo->rollLimit)
1555 					{
1556 						curRoll = -pVeh->m_pVehicleInfo->rollLimit;
1557 					}
1558 				}
1559 #endif// VEH_CONTROL_SCHEME_4
1560 			}
1561 		}
1562 	}
1563 
1564 	// If you are directly impacting the ground, even out your pitch.
1565 	if ( isLandingOrLanded )
1566 	{//only if capable of landing
1567 		if ( !isDead
1568 			&& parentPS->electrifyTime<curTime
1569 			&& (!pVeh->m_pVehicleInfo->surfDestruction || !pVeh->m_iRemovedSurfaces ) )
1570 		{//not crashing or spiralling out of control...
1571 			if ( pVeh->m_vOrientation[PITCH] > 0 )
1572 			{
1573 				pVeh->m_vOrientation[PITCH] = PredictedAngularDecrement(0.2f, angleTimeMod*10.0f, pVeh->m_vOrientation[PITCH]);
1574 			}
1575 			else
1576 			{
1577 				pVeh->m_vOrientation[PITCH] = PredictedAngularDecrement(0.75f, angleTimeMod*10.0f, pVeh->m_vOrientation[PITCH]);
1578 			}
1579 		}
1580 	}
1581 
1582 
1583 /*
1584 //NOTE: all this is redundant now since we have the FighterDamageRoutine func...
1585 	if ( isDead )
1586 	{//going to explode
1587 		//FIXME: maybe make it erratically jerk or spin or start and stop?
1588 		if (1)
1589 		{
1590 			pVeh->m_ucmd.rightmove = Q_irand( -64, 64 );
1591 		}
1592 		else
1593 		{
1594 			pVeh->m_ucmd.rightmove = 0;
1595 		}
1596 		pVeh->m_ucmd.forwardmove = Q_irand( -32, 127 );
1597 		pVeh->m_ucmd.upmove = Q_irand( -127, 127 );
1598 		pVeh->m_vOrientation[YAW] += Q_flrand( -10, 10 );
1599 		pVeh->m_vOrientation[PITCH] += pVeh->m_fTimeModifier;
1600 		if ( pVeh->m_vOrientation[PITCH] > 60.0f )
1601 		{
1602 			pVeh->m_vOrientation[PITCH] = 60.0f;
1603 		}
1604 		if ( pVeh->m_LandTrace.fraction != 0.0f )
1605 		{
1606 			parentPS->velocity[2] -= pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier;
1607 		}
1608 	}
1609 */
1610 	// If no one is in this vehicle and it's up in the sky, pitch it forward as it comes tumbling down.
1611 #ifdef _GAME //never gonna happen on client anyway, we can't be getting predicted unless the predicting client is boarded
1612  	if ( !pVeh->m_pVehicleInfo->Inhabited( pVeh )
1613 		&& pVeh->m_LandTrace.fraction >= groundFraction
1614 		&& !FighterIsInSpace( (gentity_t *)parent )
1615 		&& !FighterSuspended( pVeh, parentPS ) )
1616 	{
1617 		pVeh->m_ucmd.upmove = 0;
1618 		//pVeh->m_ucmd.forwardmove = 0;
1619 		pVeh->m_vOrientation[PITCH] += pVeh->m_fTimeModifier;
1620 		if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
1621 		{
1622 			if ( pVeh->m_vOrientation[PITCH] > 60.0f )
1623 			{
1624 				pVeh->m_vOrientation[PITCH] = 60.0f;
1625 			}
1626 		}
1627 	}
1628 #endif
1629 
1630 	if ( !parentPS->hackingTime )
1631 	{//use that roll
1632 		pVeh->m_vOrientation[ROLL] = curRoll;
1633 		//NOTE: this seems really backwards...
1634 		if ( pVeh->m_vOrientation[ROLL] )
1635 		{ //continually adjust the yaw based on the roll..
1636 			if ( (pVeh->m_iRemovedSurfaces||parentPS->electrifyTime>=curTime)//spiralling out of control
1637 				&& (!(pVeh->m_pParentEntity->s.number%4)||!(pVeh->m_pParentEntity->s.number%5)) )
1638 			{//leave YAW alone
1639 			}
1640 			else
1641 			{
1642 				if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
1643 				{
1644 					pVeh->m_vOrientation[YAW] -= ((pVeh->m_vOrientation[ROLL])*0.05f)*pVeh->m_fTimeModifier;
1645 				}
1646 			}
1647 		}
1648 	}
1649 	else
1650 	{//add in strafing roll
1651 		float strafeRoll = (parentPS->hackingTime/MAX_STRAFE_TIME)*pVeh->m_pVehicleInfo->rollLimit;//pVeh->m_pVehicleInfo->bankingSpeed*
1652 		float strafeDif = AngleSubtract(strafeRoll, pVeh->m_vOrientation[ROLL]);
1653 		pVeh->m_vOrientation[ROLL] += (strafeDif*0.1f)*pVeh->m_fTimeModifier;
1654 		if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
1655 		{//cap it reasonably
1656 			if ( pVeh->m_pVehicleInfo->rollLimit != -1
1657 				&& !pVeh->m_iRemovedSurfaces
1658 				&& parentPS->electrifyTime<curTime)
1659 			{
1660 				if (pVeh->m_vOrientation[ROLL] > pVeh->m_pVehicleInfo->rollLimit )
1661 				{
1662 					pVeh->m_vOrientation[ROLL] = pVeh->m_pVehicleInfo->rollLimit;
1663 				}
1664 				else if (pVeh->m_vOrientation[ROLL] < -pVeh->m_pVehicleInfo->rollLimit)
1665 				{
1666 					pVeh->m_vOrientation[ROLL] = -pVeh->m_pVehicleInfo->rollLimit;
1667 				}
1668 			}
1669 		}
1670 	}
1671 
1672 	if (pVeh->m_pVehicleInfo->surfDestruction)
1673 	{
1674 		FighterDamageRoutine(pVeh, parent, parentPS, riderPS, isDead);
1675 	}
1676 	pVeh->m_vOrientation[ROLL] = AngleNormalize180( pVeh->m_vOrientation[ROLL] );
1677 
1678 	/********************************************************************************/
1679 	/*	END	Here is where make sure the vehicle is properly oriented.	END			*/
1680 	/********************************************************************************/
1681 }
1682 
1683 #ifdef _GAME //ONLY on server, not cgame
1684 
1685 // This function makes sure that the vehicle is properly animated.
AnimateVehicle(Vehicle_t * pVeh)1686 static void AnimateVehicle( Vehicle_t *pVeh )
1687 {
1688 	int Anim = -1;
1689 	int iFlags = SETANIM_FLAG_NORMAL;
1690 	qboolean isLanding = qfalse, isLanded = qfalse;
1691 	playerState_t *parentPS = pVeh->m_pParentEntity->playerState;
1692 	int curTime = level.time;
1693 
1694 	if ( parentPS->hyperSpaceTime
1695 		&& curTime - parentPS->hyperSpaceTime < HYPERSPACE_TIME )
1696 	{//Going to Hyperspace
1697 		//close the wings (FIXME: makes sense on X-Wing, not Shuttle?)
1698 		if ( pVeh->m_ulFlags & VEH_WINGSOPEN )
1699 		{
1700 			pVeh->m_ulFlags &= ~VEH_WINGSOPEN;
1701 			Anim = BOTH_WINGS_CLOSE;
1702 		}
1703 	}
1704 	else
1705 	{
1706 		isLanding = FighterIsLanding( pVeh, parentPS );
1707 		isLanded = FighterIsLanded( pVeh, parentPS );
1708 
1709 		// if we're above launch height (way up in the air)...
1710 		if ( !isLanding && !isLanded )
1711 		{
1712 			if ( !( pVeh->m_ulFlags & VEH_WINGSOPEN ) )
1713 			{
1714 				pVeh->m_ulFlags |= VEH_WINGSOPEN;
1715 				pVeh->m_ulFlags &= ~VEH_GEARSOPEN;
1716 				Anim = BOTH_WINGS_OPEN;
1717 			}
1718 		}
1719 		// otherwise we're below launch height and still taking off.
1720 		else
1721 		{
1722 			if ( (pVeh->m_ucmd.forwardmove < 0 || pVeh->m_ucmd.upmove < 0||isLanded)
1723 				&& pVeh->m_LandTrace.fraction <= 0.4f
1724 				&& pVeh->m_LandTrace.plane.normal[2] >= MIN_LANDING_SLOPE )
1725 			{//already landed or trying to land and close to ground
1726 				// Open gears.
1727 				if ( !( pVeh->m_ulFlags & VEH_GEARSOPEN ) )
1728 				{
1729 					if ( pVeh->m_pVehicleInfo->soundLand )
1730 					{//just landed?
1731 						G_EntitySound( ((gentity_t *)(pVeh->m_pParentEntity)), CHAN_AUTO, pVeh->m_pVehicleInfo->soundLand );
1732 					}
1733 					pVeh->m_ulFlags |= VEH_GEARSOPEN;
1734 					Anim = BOTH_GEARS_OPEN;
1735 				}
1736 			}
1737 			else
1738 			{//trying to take off and almost halfway off the ground
1739 				// Close gears (if they're open).
1740 				if ( pVeh->m_ulFlags & VEH_GEARSOPEN )
1741 				{
1742 					pVeh->m_ulFlags &= ~VEH_GEARSOPEN;
1743 					Anim = BOTH_GEARS_CLOSE;
1744 					//iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;
1745 				}
1746 				// If gears are closed, and we are below launch height, close the wings.
1747 				else
1748 				{
1749 					if ( pVeh->m_ulFlags & VEH_WINGSOPEN )
1750 					{
1751 						pVeh->m_ulFlags &= ~VEH_WINGSOPEN;
1752 						Anim = BOTH_WINGS_CLOSE;
1753 					}
1754 				}
1755 			}
1756 		}
1757 	}
1758 
1759 	if ( Anim != -1 )
1760 	{
1761 		BG_SetAnim(pVeh->m_pParentEntity->playerState, bgAllAnims[pVeh->m_pParentEntity->localAnimIndex].anims, SETANIM_BOTH, Anim, iFlags);
1762 	}
1763 }
1764 
1765 // This function makes sure that the rider's in this vehicle are properly animated.
AnimateRiders(Vehicle_t * pVeh)1766 static void AnimateRiders( Vehicle_t *pVeh )
1767 {
1768 }
1769 
1770 #endif //game-only
1771 
1772 #ifdef _CGAME
1773 void AttachRidersGeneric( Vehicle_t *pVeh );
1774 #endif
1775 
G_SetFighterVehicleFunctions(vehicleInfo_t * pVehInfo)1776 void G_SetFighterVehicleFunctions( vehicleInfo_t *pVehInfo )
1777 {
1778 #ifdef _GAME //ONLY in SP or on server, not cgame
1779 	pVehInfo->AnimateVehicle			=		AnimateVehicle;
1780 	pVehInfo->AnimateRiders				=		AnimateRiders;
1781 //	pVehInfo->ValidateBoard				=		ValidateBoard;
1782 //	pVehInfo->SetParent					=		SetParent;
1783 //	pVehInfo->SetPilot					=		SetPilot;
1784 //	pVehInfo->AddPassenger				=		AddPassenger;
1785 //	pVehInfo->Animate					=		Animate;
1786 	pVehInfo->Board						=		Board;
1787 	pVehInfo->Eject						=		Eject;
1788 //	pVehInfo->EjectAll					=		EjectAll;
1789 //	pVehInfo->StartDeathDelay			=		StartDeathDelay;
1790 //	pVehInfo->DeathUpdate				=		DeathUpdate;
1791 //	pVehInfo->RegisterAssets			=		RegisterAssets;
1792 //	pVehInfo->Initialize				=		Initialize;
1793 	pVehInfo->Update					=		Update;
1794 //	pVehInfo->UpdateRider				=		UpdateRider;
1795 #endif //game-only
1796 	pVehInfo->ProcessMoveCommands		=		ProcessMoveCommands;
1797 	pVehInfo->ProcessOrientCommands		=		ProcessOrientCommands;
1798 
1799 #ifdef _CGAME //cgame prediction attachment func
1800 	pVehInfo->AttachRiders				=		AttachRidersGeneric;
1801 #endif
1802 //	pVehInfo->AttachRiders				=		AttachRiders;
1803 //	pVehInfo->Ghost						=		Ghost;
1804 //	pVehInfo->UnGhost					=		UnGhost;
1805 //	pVehInfo->Inhabited					=		Inhabited;
1806 }
1807 
1808 // Following is only in game, not in namespace
1809 #ifdef _GAME
1810 extern void G_AllocateVehicleObject(Vehicle_t **pVeh);
1811 #endif
1812 
1813 // Create/Allocate a new Animal Vehicle (initializing it as well).
G_CreateFighterNPC(Vehicle_t ** pVeh,const char * strType)1814 void G_CreateFighterNPC( Vehicle_t **pVeh, const char *strType )
1815 {
1816 	// Allocate the Vehicle.
1817 #ifdef _GAME
1818 	//these will remain on entities on the client once allocated because the pointer is
1819 	//never stomped. on the server, however, when an ent is freed, the entity struct is
1820 	//memset to 0, so this memory would be lost..
1821     G_AllocateVehicleObject(pVeh);
1822 #else
1823 	if (!*pVeh)
1824 	{ //only allocate a new one if we really have to
1825 		(*pVeh) = (Vehicle_t *) BG_Alloc( sizeof(Vehicle_t) );
1826 	}
1827 #endif
1828 	memset(*pVeh, 0, sizeof(Vehicle_t));
1829 	(*pVeh)->m_pVehicleInfo = &g_vehicleInfo[BG_VehicleGetIndex( strType )];
1830 }
1831