1 /* KJL 14:30:27 06/05/98 - weapon targeting code */
2 #include "3dc.h"
3 #include "module.h"
4 #include "inline.h"
5 
6 #include "stratdef.h"
7 #include "gamedef.h"
8 #include "gameplat.h"
9 
10 #include "bh_types.h"
11 #include "inventry.h"
12 #include "comp_shp.h"
13 #include "load_shp.h"
14 #include "huddefs.h"
15 
16 #define UseLocalAssert Yes
17 #include "ourasert.h"
18 
19 #include "dynblock.h"
20 #include "dynamics.h"
21 #include "lighting.h"
22 #include "pvisible.h"
23 #include "bh_alien.h"
24 #include "bh_pred.h"
25 #include "bh_xeno.h"
26 #include "bh_paq.h"
27 #include "bh_queen.h"
28 #include "bh_fhug.h"
29 #include "bh_marin.h"
30 #include "bh_debri.h"
31 #include "bh_weap.h"
32 #include "bh_agun.h"
33 #include "weapons.h"
34 #include "avpview.h"
35 
36 #include "psnd.h"
37 #include "vision.h"
38 #include "plat_shp.h"
39 
40 #include "particle.h"
41 #include "psndproj.h"
42 #include "showcmds.h"
43 #include "los.h"
44 #include <math.h>
45 
46 
47 #include "paintball.h"
48 /* for win 95 net support */
49 #include "pldghost.h"
50 #include "pldnet.h"
51 
52 /*KJL****************************************************************************************
53 *  										G L O B A L S 	            					    *
54 ****************************************************************************************KJL*/
55 
56 void SmartTarget_GetCofM(DISPLAYBLOCK *target,VECTORCH *viewSpaceOutput);
57 void GetTargetingPointOfObject(DISPLAYBLOCK *objectPtr, VECTORCH *targetPtr);
58 
59 extern int NumOnScreenBlocks;
60 extern DISPLAYBLOCK *OnScreenBlockList[];
61 extern struct Target PlayersTarget;
62 extern VECTORCH GunMuzzleDirectionInVS;
63 extern VECTORCH GunMuzzleDirectionInWS;
64 extern int NormalFrameTime;
65 extern int Weapon_ThisBurst;
66 
67 /* stuff to do with where a gun is pointing */
68 extern int GunMuzzleSightX, GunMuzzleSightY;
69 /* In 16.16 for smoothness. On-screen coords indicating to where the gun's muzzle is pointing */
70 
71 int SmartTargetSightX, SmartTargetSightY;
72 char CurrentlySmartTargetingObject;
73 DISPLAYBLOCK *SmartTarget_Object;
74 DISPLAYBLOCK *Old_SmartTarget_Object;
75 
76 void CalculateWhereGunIsPointing(TEMPLATE_WEAPON_DATA *twPtr, PLAYER_WEAPON_DATA *weaponPtr);
77 void CalculatePlayersTarget(TEMPLATE_WEAPON_DATA *twPtr, PLAYER_WEAPON_DATA *weaponPtr);
78 DISPLAYBLOCK *SmartTarget_GetNewTarget(void);
79 int SmartTarget_TargetFilter(STRATEGYBLOCK *candidate);
80 
CalculateWhereGunIsPointing(TEMPLATE_WEAPON_DATA * twPtr,PLAYER_WEAPON_DATA * weaponPtr)81 void CalculateWhereGunIsPointing(TEMPLATE_WEAPON_DATA *twPtr, PLAYER_WEAPON_DATA *weaponPtr)
82 {
83 	extern VIEWDESCRIPTORBLOCK *ActiveVDBList[];
84 	VIEWDESCRIPTORBLOCK *VDBPtr = ActiveVDBList[0];
85 
86 	extern SCREENDESCRIPTORBLOCK ScreenDescriptorBlock;
87 	MATRIXCH matrix	= VDBPtr->VDB_Mat;
88 
89 	TransposeMatrixCH(&matrix);
90 
91 //	textprint("Calculating where gun is pointing...\n");
92 
93 	/* unnormalised vector in the direction	which the gun's muzzle is pointing, IN VIEW SPACE */
94 	/* very useful when considering sprites, which lie in a Z-plane in view space */
95  	GunMuzzleDirectionInVS.vz = 65536;
96     GunMuzzleDirectionInVS.vx =
97     	(GunMuzzleSightX-(ScreenDescriptorBlock.SDB_Width<<15))/(VDBPtr->VDB_ProjX);
98 	GunMuzzleDirectionInVS.vy =
99 		(((GunMuzzleSightY-(ScreenDescriptorBlock.SDB_Height<<15))/(VDBPtr->VDB_ProjY))*3)/4;
100 
101 	/* Now fudge for gun judder! */
102  	if ((twPtr->UseStateMovement==0)||(weaponPtr->WeaponIDNumber == WEAPON_MINIGUN)) {
103 		if ((weaponPtr->CurrentState==WEAPONSTATE_FIRING_PRIMARY)
104 			||( (weaponPtr->CurrentState==WEAPONSTATE_FIRING_SECONDARY)&&(weaponPtr->WeaponIDNumber == WEAPON_TWO_PISTOLS) )){
105 			if ((twPtr->PrimaryIsRapidFire)||(weaponPtr->WeaponIDNumber == WEAPON_TWO_PISTOLS)) {
106 
107 				EULER judder;
108 				MATRIXCH juddermat;
109 
110 				if (twPtr->RecoilMaxRandomZ>0) {
111 					weaponPtr->PositionOffset.vz = (FastRandom()%twPtr->RecoilMaxRandomZ) - twPtr->RecoilMaxZ;
112 				}
113 
114 				if ((Weapon_ThisBurst>0)||(weaponPtr->WeaponIDNumber == WEAPON_TWO_PISTOLS)) {
115 
116 					/* jiggle the weapon around when you shoot */
117 					int speed=Approximate3dMagnitude(&Player->ObStrategyBlock->DynPtr->LinVelocity);
118 					/* speed should be between ~0 and ~27000 (jumping alien). ~15000 is a moving marine. */
119 
120 					if (twPtr->RecoilMaxXTilt>0) {
121 						judder.EulerX=(FastRandom()%twPtr->RecoilMaxXTilt)-twPtr->RecoilMaxXTilt/2;
122 					} else {
123 						judder.EulerX=0;
124 					}
125 					if (twPtr->RecoilMaxYTilt>0) {
126 						judder.EulerY=(FastRandom()%twPtr->RecoilMaxYTilt)-twPtr->RecoilMaxYTilt/2;
127 					} else {
128 						judder.EulerY=0;
129 					}
130 					judder.EulerZ=0;
131 
132 					judder.EulerX=MUL_FIXED(judder.EulerX,(ONE_FIXED+(speed<<2)));
133 					judder.EulerY=MUL_FIXED(judder.EulerY,(ONE_FIXED+(speed<<2)));
134 
135 					judder.EulerX&=wrap360;
136 					judder.EulerY&=wrap360;
137 
138 					CreateEulerMatrix(&judder,&juddermat);
139 					RotateVector(&GunMuzzleDirectionInVS,&juddermat);
140 				}
141 			}
142 		} else {
143 			/* Recentre Z offset. */
144 			int linearCenteringSpeed = MUL_FIXED(300,NormalFrameTime);
145 
146 			if (weaponPtr->PositionOffset.vz > 0 )
147 			{
148 				weaponPtr->PositionOffset.vz -= linearCenteringSpeed;
149 				if (weaponPtr->PositionOffset.vz < 0) weaponPtr->PositionOffset.vz = 0;
150 			}
151 			else if (weaponPtr->PositionOffset.vz < 0 )
152 			{
153 				weaponPtr->PositionOffset.vz += linearCenteringSpeed;
154 				if (weaponPtr->PositionOffset.vz > 0) weaponPtr->PositionOffset.vz = 0;
155 			}
156 
157 		}
158 	}
159 
160     GunMuzzleDirectionInWS = GunMuzzleDirectionInVS;
161     /* rotate vector into world space and then normalise */
162     RotateVector(&GunMuzzleDirectionInWS,&matrix);
163 	Normalise(&GunMuzzleDirectionInWS);
164 
165 }
166 
CalculatePlayersTarget(TEMPLATE_WEAPON_DATA * twPtr,PLAYER_WEAPON_DATA * weaponPtr)167 void CalculatePlayersTarget(TEMPLATE_WEAPON_DATA *twPtr, PLAYER_WEAPON_DATA *weaponPtr)
168 {
169 
170 	CalculateWhereGunIsPointing(twPtr,weaponPtr);
171 
172 	FindPolygonInLineOfSight(&GunMuzzleDirectionInWS, &Global_VDB_Ptr->VDB_World, 1,Player);
173 
174 	PlayersTarget.DispPtr =  LOS_ObjectHitPtr;
175 	PlayersTarget.Distance = LOS_Lambda;
176 	PlayersTarget.HModelSection = LOS_HModel_Section;
177 
178 	if (PaintBallMode.IsOn)
179 	{
180 		PaintBallMode.TargetDispPtr = LOS_ObjectHitPtr;
181 		PaintBallMode.TargetPosition = LOS_Point;
182 		PaintBallMode.TargetNormal = LOS_ObjectNormal;
183 	}
184 
185 	//textprint("Exiting CPT - PT.DP is %x, PT.HMS is %x\n",PlayersTarget.DispPtr,PlayersTarget.HModelSection);
186 
187 	if (PlayersTarget.DispPtr) {
188 		if (PlayersTarget.HModelSection) {
189 			if (PlayersTarget.HModelSection->my_controller!=PlayersTarget.DispPtr->HModelControlBlock) {
190 				PlayersTarget.HModelSection=NULL;
191 			}
192 		}
193 	}
194 
195 	/* did we hit anything? */
196 	if (PlayersTarget.DispPtr)
197 	{
198 		PlayersTarget.Position = LOS_Point;
199 	}
200 	else
201 	{
202 		/* pretend the target is right in front of the player, but a very long way off */
203 		PlayersTarget.Position.vx = Global_VDB_Ptr->VDB_World.vx + (Global_VDB_Ptr->VDB_Mat.mat13<<7);
204 		PlayersTarget.Position.vy = Global_VDB_Ptr->VDB_World.vy + (Global_VDB_Ptr->VDB_Mat.mat23<<7);
205 		PlayersTarget.Position.vz = Global_VDB_Ptr->VDB_World.vz + (Global_VDB_Ptr->VDB_Mat.mat33<<7);
206 		PlayersTarget.HModelSection = NULL;
207 	}
208 	if (ShowDebuggingText.Target)
209 	{
210 		PrintDebuggingText("Target Position: %d %d %d\n",PlayersTarget.Position.vx,PlayersTarget.Position.vy,PlayersTarget.Position.vz);
211 	}
212 
213 	if (PlayersTarget.HModelSection) {
214 		GLOBALASSERT(PlayersTarget.DispPtr->HModelControlBlock==PlayersTarget.HModelSection->my_controller);
215 	}
216 
217 	if(AvP.Network!=I_No_Network)
218 	{
219 		AddNetMsg_PredatorLaserSights(&PlayersTarget.Position,&LOS_ObjectNormal,PlayersTarget.DispPtr);
220 	}
221 
222 
223 	/* find position/orientation of predator's targeting sights */
224 	if ( (AvP.PlayerType == I_Predator)
225 	   &&((weaponPtr->WeaponIDNumber == WEAPON_PRED_RIFLE)
226 	    ||(weaponPtr->WeaponIDNumber == WEAPON_PRED_SHOULDERCANNON)) )
227 	{
228 		int i=2;
229 
230 		VECTORCH offset[3] =
231 		{
232 			{0,-50,0},
233 			{43,25,0},
234 			{-43,25,0},
235 		};
236 
237 		MATRIXCH matrix = Global_VDB_Ptr->VDB_Mat;
238 		TransposeMatrixCH(&matrix);
239 
240 		do
241 		{
242 			VECTORCH position = offset[i];
243 
244 		  	RotateVector(&position,&matrix);
245 			position.vx += Global_VDB_Ptr->VDB_World.vx;
246 			position.vy += Global_VDB_Ptr->VDB_World.vy;
247 			position.vz += Global_VDB_Ptr->VDB_World.vz;
248 			FindPolygonInLineOfSight(&GunMuzzleDirectionInWS, &position, 1,Player);
249 			PredatorLaserTarget.Normal[i] = LOS_ObjectNormal;
250 
251 			if (PlayersTarget.DispPtr)
252 			{
253 				PredatorLaserTarget.Position[i] = LOS_Point;
254 			}
255 			else
256 			{
257 				/* pretend the target is right in front of the player, but a very long way off */
258 				PredatorLaserTarget.Position[i].vx = Global_VDB_Ptr->VDB_World.vx + (Global_VDB_Ptr->VDB_Mat.mat13<<7);
259 				PredatorLaserTarget.Position[i].vy = Global_VDB_Ptr->VDB_World.vy + (Global_VDB_Ptr->VDB_Mat.mat23<<7);
260 				PredatorLaserTarget.Position[i].vz = Global_VDB_Ptr->VDB_World.vz + (Global_VDB_Ptr->VDB_Mat.mat33<<7);
261 			}
262 
263 		}
264 		while(i--);
265 		PredatorLaserTarget.ShouldBeDrawn=1;
266   	}
267 	else
268 	{
269 		PredatorLaserTarget.ShouldBeDrawn=0;
270 	}
271 
272 
273 }
274 
CalculateFiringSolution(VECTORCH * firing_pos,VECTORCH * target_pos,VECTORCH * target_vel,int projectile_speed,VECTORCH * solution)275 BOOL CalculateFiringSolution(VECTORCH* firing_pos,VECTORCH* target_pos,VECTORCH* target_vel,int projectile_speed,VECTORCH* solution)
276 {
277 	VECTORCH normal; //normal from firer to target
278 	VECTORCH rotated_vel;
279 	VECTORCH rotated_solution;
280 	MATRIXCH mat;
281 
282 	int distance_to_target;
283 
284 	GLOBALASSERT(firing_pos);
285 	GLOBALASSERT(target_pos);
286 	GLOBALASSERT(target_vel);
287 	GLOBALASSERT(projectile_speed);
288 	GLOBALASSERT(solution);
289 
290 	//get a normalised vector from start to destination
291 	normal=*target_pos;
292 	SubVector(firing_pos,&normal);
293 
294 	if(!normal.vx && !normal.vy && !normal.vz)
295 		return FALSE;
296 
297 	distance_to_target=Approximate3dMagnitude(&normal);
298 	Normalise(&normal);
299 
300 	//calculate a matrix that will rotate the normal to the zaxis
301 	{
302 		//normal will be the third row
303 		VECTORCH row1,row2;
304 
305 
306 		if(normal.vx>30000 || normal.vx<-30000 || normal.vy>30000 || normal.vy<-30000)
307 		{
308 			row1.vx=-normal.vy;
309 			row1.vy=normal.vx;
310 			row1.vz=0;
311 		}
312 		else
313 		{
314 			row1.vx=-normal.vz;
315 			row1.vy=0;
316 			row1.vz=normal.vx;
317 		}
318 		Normalise(&row1);
319 
320 		CrossProduct(&normal,&row1,&row2);
321 
322 		mat.mat11=row1.vx;
323 		mat.mat21=row1.vy;
324 		mat.mat31=row1.vz;
325 
326 		mat.mat12=row2.vx;
327 		mat.mat22=row2.vy;
328 		mat.mat32=row2.vz;
329 
330 		mat.mat13=normal.vx;
331 		mat.mat23=normal.vy;
332 		mat.mat33=normal.vz;
333 
334 	}
335 
336 
337 	//apply the rotation to the velocity
338 	rotated_vel=*target_vel;
339 	RotateVector(&rotated_vel,&mat);
340 
341 	//is the target moving too fast?
342 	if(rotated_vel.vz>=projectile_speed || -rotated_vel.vz>=projectile_speed)
343 	{
344 		return FALSE;
345 	}
346 
347 	//the x and y components of the rotated solution should match the rotated velocity
348 	//(scale down by projectile speed , because we want a normalised direction)
349 
350 	rotated_solution.vx=DIV_FIXED(rotated_vel.vx,projectile_speed);
351 	rotated_solution.vy=DIV_FIXED(rotated_vel.vy,projectile_speed);
352 
353 	//z=1-(x*x+y*y)
354 	{
355 		//not sure we have a fixed point square root
356 		float x=(float)rotated_solution.vx;
357 		float y=(float)rotated_solution.vy;
358 		float z_squared=65536.0*65536.0-(x*x+y*y);
359 		if(z_squared<0)
360 		{
361 			//target moving too fast to hit
362 			return FALSE;
363 		}
364 		rotated_solution.vz=(int)sqrt(z_squared);
365 	}
366 
367 	//finally need to rotated solution back
368 	*solution=rotated_solution;
369 	TransposeMatrixCH(&mat);
370 	RotateVector(solution,&mat);
371 
372 	//normalise solution to be on the safe side
373 	Normalise(solution);
374 
375 	return TRUE;
376 
377 }
378 
SmartTarget(int speed,int projectile_speed)379 void SmartTarget(int speed,int projectile_speed)
380 {
381 	DISPLAYBLOCK *trackedObject;
382 
383 	if (SmartgunMode==I_Track) {
384 		trackedObject=SmartTarget_GetNewTarget();
385 	} else {
386 		trackedObject=NULL;
387 	}
388 
389 //	textprint("Tracking object %x ",trackedObject);
390 	{
391 		int screenX;
392 		int screenY;
393 		CurrentlySmartTargetingObject=0;
394 
395 		/* If there is a valid near object which isn't so close as to cause a division by zero */
396 		if (trackedObject && (trackedObject->ObView.vz!=0))
397 		{
398 			VECTORCH targetView;
399 			extern VIEWDESCRIPTORBLOCK *ActiveVDBList[];
400 			VIEWDESCRIPTORBLOCK *VDBPtr = ActiveVDBList[0];
401 			#if 0
402 			STRATEGYBLOCK *sbPtr = trackedObject->ObStrategyBlock;
403 		   	int offsetX,offsetY;
404 
405 			{
406 			  	/* calculate offset required to aim at the middle torso rather
407 				than the sprite's bollocks */
408 				MATRIXCH mat;
409 				int offsetMag;
410 				{
411 					SHAPEHEADER	*shapePtr = GetShapeData(sbPtr->SBdptr->ObShape);
412 			   		offsetMag = shapePtr->shapeminy/2;
413 				}
414 
415 				offsetX = MUL_FIXED(trackedObject->ObMat.mat21,offsetMag);
416 				offsetY = MUL_FIXED(trackedObject->ObMat.mat22,offsetMag);
417 			}
418 			#endif
419 			/* Set targetView. */
420 			SmartTarget_GetCofM(trackedObject,&targetView);
421 
422 			if(projectile_speed)
423 			{
424 				//get a firing solution so that projectile should hit if target maintains curremt velocity
425 				if(trackedObject->ObStrategyBlock)
426 				{
427 					if(trackedObject->ObStrategyBlock->DynPtr)
428 					{
429 						DYNAMICSBLOCK *dynPtr = trackedObject->ObStrategyBlock->DynPtr;
430 						if(dynPtr->LinVelocity.vx || dynPtr->LinVelocity.vy || dynPtr->LinVelocity.vz)
431 						{
432 
433 							VECTORCH velocity=dynPtr->LinVelocity;
434 							VECTORCH zero={0,0,0};
435 							VECTORCH solution;
436 							//rotate velocity into view space
437 							RotateVector(&velocity,&Global_VDB_Ptr->VDB_Mat);
438 
439 							if(CalculateFiringSolution(&zero,&targetView,&velocity,projectile_speed,&solution))
440 							{
441 								targetView=solution;
442 							}
443 
444 
445 						}
446 					}
447 				}
448 			}
449 
450 			if (targetView.vz>0)
451 			{
452 				screenX = WideMulNarrowDiv
453 								(
454 									//trackedObject->ObView.vx,//+offsetX,
455 									targetView.vx,
456 									VDBPtr->VDB_ProjX,
457 									//trackedObject->ObView.vz
458 									targetView.vz
459 								);
460 			   	screenY = WideMulNarrowDiv
461 			   					(
462 			   						//trackedObject->ObView.vy,//+offsetY,
463 									targetView.vy*4,
464 			   						VDBPtr->VDB_ProjY,
465 									//trackedObject->ObView.vz
466 									(targetView.vz*3)
467 								);
468 		  		CurrentlySmartTargetingObject=1;
469 			}
470 			else
471 			{
472 		   		screenX=0;
473 		   		screenY=0;
474 			}
475 	  	}
476 	   	else
477 	   	{
478 	   		screenX=0;
479 	   		screenY=0;
480 	   	}
481 
482 
483 		{
484 			extern SCREENDESCRIPTORBLOCK ScreenDescriptorBlock;
485 			int targetX,targetY,dx,dy;
486 			int targetingSpeed = NormalFrameTime*speed;
487 
488 			/* KJL 14:08:50 09/20/96 - the targeting is FRI, but care has to be taken
489 			   at very low frame rates to ensure that the sight doesn't jump about
490 			   all over the place. */
491 			if (targetingSpeed > 65536)	targetingSpeed=65536;
492 
493 			targetX = (ScreenDescriptorBlock.SDB_Width>>1);
494 			targetY = (ScreenDescriptorBlock.SDB_Height>>1);
495 
496 			{
497 				int maxRangeX = (ScreenDescriptorBlock.SDB_Width*14)/32;  /* gives nearly 90% of screen */
498 
499 				if ((screenX>-maxRangeX) && (screenX<maxRangeX))
500 				{
501 		  			int maxRangeY = (ScreenDescriptorBlock.SDB_Height*14)/32; /* gives nearly 90% of screen */
502 
503 					if ((screenY>-maxRangeY) && (screenY<maxRangeY))
504 					{
505 						targetX += screenX;
506 						targetY += screenY;
507 					}
508 					else CurrentlySmartTargetingObject=0;
509 				}
510 				else CurrentlySmartTargetingObject=0;
511 			}
512 
513 			if (speed<=0)
514 			{
515     	        SmartTargetSightX = targetX<<16;
516         	    SmartTargetSightY = targetY<<16;
517 			}
518 			else
519 			{
520 				dx = MUL_FIXED( ((targetX<<16)-SmartTargetSightX), targetingSpeed );
521 			  	dy = MUL_FIXED( ((targetY<<16)-SmartTargetSightY), targetingSpeed );
522 
523 				/* If the x-coord difference between the sight and the target is small,
524 				   just move the sight so that it has the same x-coord as the target.
525 				   This stops the sight from hovering a pixel away from where it should be */
526 				if (dx>16384 || dx<-16384) SmartTargetSightX += dx;
527     	        else SmartTargetSightX = targetX<<16;
528 
529 	            /* Similarly for the y-coord */
530 				if (dy>16384 || dy<-16384) SmartTargetSightY += dy;
531         	    else SmartTargetSightY = targetY<<16;
532 			}
533 	 	}
534 
535     }
536 
537 	Old_SmartTarget_Object=SmartTarget_Object;
538 
539    	if (CurrentlySmartTargetingObject) {
540 		SmartTarget_Object=trackedObject;
541 	} else {
542 		SmartTarget_Object=NULL;
543 	}
544 
545 	return;
546 }
547 
548 #define SMART_TRACKABLE_TARGETS 100
549 
SmartTarget_GetNewTarget(void)550 DISPLAYBLOCK *SmartTarget_GetNewTarget(void) {
551 
552 	int a,b,c;
553 	int numberOfObjects = NumOnScreenBlocks;
554 	DISPLAYBLOCK *nearestObjectPtr, *target;
555 	int nearestObjectDist;
556 
557 	DISPLAYBLOCK *track_array[SMART_TRACKABLE_TARGETS];
558 
559 	target=NULL;
560 
561 	nearestObjectPtr=NULL;
562 
563 	for (a=0; a<SMART_TRACKABLE_TARGETS; a++) {
564 		track_array[a]=NULL;
565 	}
566 	a=0;
567 
568 	while (numberOfObjects--)
569 	{
570 		DISPLAYBLOCK* objectPtr = OnScreenBlockList[numberOfObjects];
571 		STRATEGYBLOCK* sbPtr = objectPtr->ObStrategyBlock;
572 
573 		if (sbPtr && sbPtr->DynPtr)
574 		{
575 			VECTORCH viewPos;
576 			/* Arc reject. */
577 			SmartTarget_GetCofM(objectPtr,&viewPos);
578 			if (viewPos.vz>0) {
579 				if (SmartTarget_TargetFilter(sbPtr))	{
580 					if (a<SMART_TRACKABLE_TARGETS) {
581 						track_array[a]=objectPtr;
582 						a++;
583 					} else {
584 						/* Arse. Well, I said I didn't like this way. */
585 					}
586 				}
587 			}
588 		}
589 	}
590 
591 	/* Now, filter the track array. */
592 	b=a;
593 	c=a;
594 
595 
596 	while (b--) {
597 
598 		int notFar;
599 
600 		nearestObjectDist=SMART_TARGETING_RANGE;
601 		nearestObjectPtr=NULL;
602 		notFar=-1;
603 
604 		a=c;
605 
606 		while (a) {
607 
608 			DISPLAYBLOCK* objectPtr;
609 			STRATEGYBLOCK* sbPtr;
610 
611 			a--;
612 
613 			objectPtr = track_array[a];
614 
615 			if (objectPtr) {
616 				/* calc distance to player - no parallel strat support yet */
617 				/* 2d vector from player to object */
618 				int dist = FandVD_Distance_3d(&objectPtr->ObWorld,&Player->ObWorld);
619 
620 				sbPtr = objectPtr->ObStrategyBlock;
621 
622 				if (dist<nearestObjectDist)
623 				{
624 					nearestObjectDist=dist;
625 					nearestObjectPtr=objectPtr;
626 					notFar=a;
627 				}
628 			}
629 
630 		}
631 
632 		if (nearestObjectPtr) {
633 
634 			GLOBALASSERT(notFar!=-1);
635 
636 			if (IsThisObjectVisibleFromThisPosition_WithIgnore(track_array[notFar],Player,&(Global_VDB_Ptr->VDB_World),nearestObjectDist) ) {
637 				/* Valid. */
638 				target=track_array[notFar];
639 				break; // Exit loop
640 			} else {
641 				/* Remove from list. */
642 				track_array[notFar]=NULL;
643 			}
644 		}
645 	}
646 
647 	return(target);
648 
649 }
650 
SmartTarget_GetCofM(DISPLAYBLOCK * target,VECTORCH * viewSpaceOutput)651 void SmartTarget_GetCofM(DISPLAYBLOCK *target,VECTORCH *viewSpaceOutput) {
652 
653 	AVP_BEHAVIOUR_TYPE targetType;
654 
655 	/* Get smartgun aiming point. */
656 
657 	if (target->HModelControlBlock==NULL) {
658 		*viewSpaceOutput=target->ObView;
659 		return;
660 	} else if (target->HModelControlBlock->section_data==NULL) {
661 		/* Always consider extreme possibilities. */
662 		*viewSpaceOutput=target->ObView;
663 		return;
664 	}
665 
666 	/* Must be a hierarchy. */
667 
668 	if (target->ObStrategyBlock->I_SBtype==I_BehaviourNetGhost) {
669 
670 		NETGHOSTDATABLOCK *dataptr;
671 
672 		dataptr=target->ObStrategyBlock->SBdataptr;
673 
674 		targetType=dataptr->type;
675 
676 	} else if (
677 		(target->ObStrategyBlock->I_SBtype==I_BehaviourMarinePlayer)||
678 		(target->ObStrategyBlock->I_SBtype==I_BehaviourAlienPlayer)||
679 		(target->ObStrategyBlock->I_SBtype==I_BehaviourPredatorPlayer)) {
680 
681 		switch(AvP.PlayerType)
682 		{
683 			case I_Alien:
684 				targetType=I_BehaviourAlienPlayer;
685 				break;
686 			case I_Predator:
687 				targetType=I_BehaviourPredatorPlayer;
688 				break;
689 			case I_Marine:
690 				targetType=I_BehaviourMarinePlayer;
691 				break;
692 			default:
693 				GLOBALASSERT(0);
694 				return;
695 				break;
696 		}
697 
698 	} else {
699 		targetType=target->ObStrategyBlock->I_SBtype;
700 	}
701 
702 	/* Now, switch case on targetType. */
703 
704 	switch (targetType) {
705 		case I_BehaviourMarine:
706 		case I_BehaviourMarinePlayer:
707 			{
708 				SECTION_DATA *targetsection;
709 
710 				targetsection=GetThisSectionData(target->HModelControlBlock->section_data,
711 					"chest");
712 
713 				if (targetsection==NULL) {
714 					targetsection=target->HModelControlBlock->section_data;
715 				}
716 
717 				if (targetsection->flags&section_data_view_init) {
718 					*viewSpaceOutput=targetsection->View_Offset;
719 					return;
720 				} else {
721 					/* Whoops. */
722 					*viewSpaceOutput=target->ObView;
723 					return;
724 				}
725 			}
726 			break;
727 		case I_BehaviourPredator:
728 		case I_BehaviourPredatorPlayer:
729 			{
730 				SECTION_DATA *targetsection;
731 
732 				targetsection=GetThisSectionData(target->HModelControlBlock->section_data,
733 					"chest");
734 
735 				if (targetsection==NULL) {
736 					targetsection=target->HModelControlBlock->section_data;
737 				}
738 
739 				if (targetsection->flags&section_data_view_init) {
740 					*viewSpaceOutput=targetsection->View_Offset;
741 					return;
742 				} else {
743 					/* Whoops. */
744 					*viewSpaceOutput=target->ObView;
745 					return;
746 				}
747 			}
748 			break;
749 		case I_BehaviourAlien:
750 		case I_BehaviourAlienPlayer:
751 			{
752 				SECTION_DATA *targetsection;
753 
754 				targetsection=GetThisSectionData(target->HModelControlBlock->section_data,
755 					"chest");
756 
757 				if (targetsection==NULL) {
758 					targetsection=target->HModelControlBlock->section_data;
759 				}
760 
761 				if (targetsection->flags&section_data_view_init) {
762 					*viewSpaceOutput=targetsection->View_Offset;
763 					return;
764 				} else {
765 					/* Whoops. */
766 					*viewSpaceOutput=target->ObView;
767 					return;
768 				}
769 			}
770 			break;
771 		default:
772 			{
773 				/* General case. */
774 				if (target->HModelControlBlock->section_data->flags&section_data_view_init) {
775 					*viewSpaceOutput=target->HModelControlBlock->section_data->View_Offset;
776 					return;
777 				} else {
778 					/* Whoops. */
779 					*viewSpaceOutput=target->ObView;
780 					return;
781 				}
782 			}
783 			break;
784 	}
785 
786 }
787 
788 
789 
SmartTarget_TargetFilter(STRATEGYBLOCK * candidate)790 int SmartTarget_TargetFilter(STRATEGYBLOCK *candidate)
791 {
792 	PLAYER_WEAPON_DATA *weaponPtr = &(PlayerStatusPtr->WeaponSlot[PlayerStatusPtr->SelectedWeaponSlot]);
793 
794 	/* KJL 11:56:33 14/10/98 - the Predator's Shoulder cannon can only track objects when used in the correct
795 	vision mode, e.g. you can only target marines when in infrared mode */
796 	if ((weaponPtr->WeaponIDNumber == WEAPON_PRED_SHOULDERCANNON)
797 		||(weaponPtr->WeaponIDNumber == WEAPON_PRED_DISC))
798 	{
799 		switch (CurrentVisionMode)
800 		{
801 			case VISION_MODE_PRED_SEEALIENS:
802 			{
803 				if (candidate->I_SBtype==I_BehaviourAlien && !NPC_IsDead(candidate))
804 				{
805 					return 1;
806 				}
807 				if (candidate->I_SBtype==I_BehaviourXenoborg && !NPC_IsDead(candidate))
808 				{
809 					return 1;
810 				}
811 				if (candidate->I_SBtype==I_BehaviourMarine && !NPC_IsDead(candidate))
812 				{
813 					MARINE_STATUS_BLOCK *marineStatusPointer;
814 					LOCALASSERT(candidate);
815 					marineStatusPointer = (MARINE_STATUS_BLOCK *)(candidate->SBdataptr);
816 					LOCALASSERT(marineStatusPointer);
817 
818 					if (marineStatusPointer->My_Weapon->Android) {
819 						return(1);
820 					} else {
821 						return(0);
822 					}
823 				}
824 				if (candidate->I_SBtype==I_BehaviourNetGhost)
825 				{
826 			   		NETGHOSTDATABLOCK *ghostDataPtr = (NETGHOSTDATABLOCK *)candidate->SBdataptr;
827 
828 					if (ghostDataPtr->type==I_BehaviourAlienPlayer || ghostDataPtr->type==I_BehaviourAlien)
829 					{
830 						return 1;
831 					}
832 				}
833 				break;
834 			}
835 			case VISION_MODE_PRED_THERMAL:
836 			{
837 				if (candidate->I_SBtype==I_BehaviourMarine && !NPC_IsDead(candidate))
838 				{
839 					MARINE_STATUS_BLOCK *marineStatusPointer;
840 					LOCALASSERT(candidate);
841 					marineStatusPointer = (MARINE_STATUS_BLOCK *)(candidate->SBdataptr);
842 					LOCALASSERT(marineStatusPointer);
843 
844 					if (marineStatusPointer->My_Weapon->Android) {
845 						return(0);
846 					} else {
847 						return(1);
848 					}
849 				}
850 				if (candidate->I_SBtype==I_BehaviourNetGhost)
851 			   	{
852 			   		NETGHOSTDATABLOCK *ghostDataPtr = (NETGHOSTDATABLOCK *)candidate->SBdataptr;
853 
854 					if (ghostDataPtr->type==I_BehaviourMarinePlayer || ghostDataPtr->type==I_BehaviourMarine)
855 					{
856 						return 1;
857 					}
858 				}
859 				break;
860 
861 			}
862 			case VISION_MODE_PRED_SEEPREDTECH:
863 			{
864 				if (candidate->I_SBtype==I_BehaviourPredator && !NPC_IsDead(candidate))
865 				{
866 					return 1;
867 				}
868 				if (candidate->I_SBtype==I_BehaviourNetGhost)
869 				{
870 			   		NETGHOSTDATABLOCK *ghostDataPtr = (NETGHOSTDATABLOCK *)candidate->SBdataptr;
871 
872 					if (ghostDataPtr->type==I_BehaviourPredatorPlayer || ghostDataPtr->type==I_BehaviourPredator)
873 					{
874 						/* Check for game type? */
875 						switch (netGameData.gameType) {
876 							case NGT_Individual:
877 								return(1);
878 								break;
879 							case NGT_CoopDeathmatch:
880 								return(0);
881 								break;
882 							case NGT_LastManStanding:
883 								return(0);
884 								break;
885 							case NGT_PredatorTag:
886 								return(1);
887 								break;
888 							case NGT_Coop:
889 								return(0);
890 								break;
891 							default:
892 								return(1);
893 								break;
894 						}
895 						return (1);
896 					}
897 				}
898 				break;
899 			}
900 			default:
901 				break;
902 		}
903 		return 0;
904 	} else if (weaponPtr->WeaponIDNumber == WEAPON_SMARTGUN) {
905 		/* Don't target marines if Friendly Fire is disabled. */
906 		if (netGameData.disableFriendlyFire && (netGameData.gameType==NGT_CoopDeathmatch || netGameData.gameType==NGT_Coop)) {
907 	   		NETGHOSTDATABLOCK *ghostDataPtr = (NETGHOSTDATABLOCK *)candidate->SBdataptr;
908 			if (ghostDataPtr->type==I_BehaviourMarinePlayer || ghostDataPtr->type==I_BehaviourMarine) {
909 				return(0);
910 			}
911 		}
912 	}
913 
914 	switch (candidate->I_SBtype) {
915 		case I_BehaviourPredator:
916 			/* Valid. */
917 			if (NPC_IsDead(candidate)) {
918 				return(0);
919 			} else {
920 				#if 0
921 				if (NPCPredatorIsCloaked(candidate)) {
922 					return(0);
923 				} else {
924 					return(1);
925 				}
926 				#else
927 				return(1);
928 				#endif
929 			}
930 			break;
931 		case I_BehaviourAlien:
932 		case I_BehaviourQueenAlien:
933 		case I_BehaviourFaceHugger:
934 		case I_BehaviourXenoborg:
935 		case I_BehaviourMarine:
936 		case I_BehaviourSeal:
937 		case I_BehaviourPredatorAlien:
938 			/* Valid. */
939 			if (NPC_IsDead(candidate)) {
940 				return(0);
941 			} else {
942 				return(1);
943 			}
944 			break;
945 		case I_BehaviourNetGhost:
946 			{
947 				NETGHOSTDATABLOCK *dataptr;
948 				dataptr=candidate->SBdataptr;
949 				switch (dataptr->type) {
950 					case I_BehaviourPredatorPlayer:
951 						#if 0
952 						if (dataptr->CloakingEffectiveness) {
953 							return(0);
954 						} else {
955 							return(1);
956 						}
957 						#else
958 						return(1);
959 						#endif
960 						break;
961 					case I_BehaviourMarinePlayer:
962 					case I_BehaviourAlienPlayer:
963 					case I_BehaviourRocket:
964 					case I_BehaviourFrisbee:
965 					case I_BehaviourGrenade:
966 					case I_BehaviourFlareGrenade:
967 					case I_BehaviourFragmentationGrenade:
968 					case I_BehaviourClusterGrenade:
969 					case I_BehaviourNPCPredatorDisc:
970 					case I_BehaviourPredatorDisc_SeekTrack:
971 					case I_BehaviourAlien:
972 						return(1);
973 						break;
974 					case I_BehaviourProximityGrenade:
975 						{
976 							DYNAMICSBLOCK *dynPtr=candidate->DynPtr;
977 
978 							/* Only visible when moving. */
979 
980 							if (!((dynPtr->Position.vx==dynPtr->PrevPosition.vx)
981 								&&(dynPtr->Position.vy==dynPtr->PrevPosition.vy)
982 								&&(dynPtr->Position.vz==dynPtr->PrevPosition.vz) )) {
983 
984 								return(1);
985 
986 							} else {
987 								return(0);
988 							}
989 
990 						}
991 						break;
992 					default:
993 						return(0);
994 						break;
995 				}
996 			}
997 			break;
998 		default:
999 			return(0);
1000 			break;
1001 	}
1002 
1003 }
1004 
GetTargetingPointOfObject_Far(STRATEGYBLOCK * sbPtr,VECTORCH * targetPtr)1005 void GetTargetingPointOfObject_Far(STRATEGYBLOCK *sbPtr, VECTORCH *targetPtr)
1006 {
1007 	/* Can we use the near one?  This is a more general shell. */
1008 	if (sbPtr->SBdptr) {
1009 		GetTargetingPointOfObject(sbPtr->SBdptr,targetPtr);
1010 		return;
1011 	} else {
1012 		if (sbPtr->DynPtr) {
1013 			*targetPtr=sbPtr->DynPtr->Position;
1014 			targetPtr->vy-=500; /* Just to be on the safe side. */
1015 			return;
1016 		} else {
1017 			/* Aw, gawd! I don't know! There's NO position for this! */
1018 			GLOBALASSERT(0);
1019 		}
1020 	}
1021 
1022 }
1023 
GetTargetingPointOfObject(DISPLAYBLOCK * objectPtr,VECTORCH * targetPtr)1024 void GetTargetingPointOfObject(DISPLAYBLOCK *objectPtr, VECTORCH *targetPtr)
1025 {
1026 	/* try to look at the centre of the object */
1027 	if (objectPtr->HModelControlBlock)
1028 	{
1029 		SECTION_DATA *targetSectionPtr;
1030 		SECTION_DATA *firstSectionPtr;
1031 
1032 	  	ProveHModel(objectPtr->HModelControlBlock,objectPtr);
1033 
1034 	  	firstSectionPtr=objectPtr->HModelControlBlock->section_data;
1035 	  	LOCALASSERT(firstSectionPtr);
1036 		LOCALASSERT(firstSectionPtr->flags&section_data_initialised);
1037 
1038 		/* look for the object's torso in preference */
1039 		targetSectionPtr =GetThisSectionData(objectPtr->HModelControlBlock->section_data,"chest");
1040 
1041 		if (targetSectionPtr)
1042 		{
1043 			LOCALASSERT(targetSectionPtr->flags&section_data_initialised);
1044 			*targetPtr = targetSectionPtr->World_Offset;
1045 		}
1046 		else /* just use the top of the hierarchy then */
1047 		{
1048 			*targetPtr = firstSectionPtr->World_Offset;
1049 		}
1050 	}
1051 	else
1052 	{
1053 		/* KJL 12:23:26 15/05/98 - need to consider the player differently */
1054 		if (objectPtr == Player)
1055 		{
1056 			#if 0
1057 			*targetPtr = Global_VDB_Ptr->VDB_World;
1058 			#else
1059 			int height;
1060 
1061 			/* Let's try a little experiment. */
1062 			if (PlayerStatusPtr->IsAlive==0) {
1063 				*targetPtr = Global_VDB_Ptr->VDB_World;
1064 				return;
1065 			}
1066 
1067 			if (PlayerStatusPtr->ShapeState!=PMph_Standing) {
1068 				height=-800;
1069 			} else {
1070 				height=-1500;
1071 			}
1072 
1073 			targetPtr->vx=MUL_FIXED(Player->ObStrategyBlock->DynPtr->OrientMat.mat21,height);
1074 			targetPtr->vy=MUL_FIXED(Player->ObStrategyBlock->DynPtr->OrientMat.mat22,height);
1075 			targetPtr->vz=MUL_FIXED(Player->ObStrategyBlock->DynPtr->OrientMat.mat23,height);
1076 			#if 0
1077 			targetPtr->vx+=Player->ObStrategyBlock->DynPtr->Position.vx;
1078 			targetPtr->vy+=Player->ObStrategyBlock->DynPtr->Position.vy;
1079 			targetPtr->vz+=Player->ObStrategyBlock->DynPtr->Position.vz;
1080 			#else
1081 			targetPtr->vx+=Player->ObWorld.vx;
1082 			targetPtr->vy+=Player->ObWorld.vy;
1083 			targetPtr->vz+=Player->ObWorld.vz;
1084 			#endif
1085 			#endif
1086 		}
1087 		else
1088 		{
1089 			*targetPtr = objectPtr->ObWorld;
1090 		}
1091 
1092 	}
1093 }
1094