1 /************************************************************************************
2 
3 	AstroMenace
4 	Hardcore 3D space scroll-shooter with spaceship upgrade possibilities.
5 	Copyright (c) 2006-2019 Mikhail Kurinnoi, Viewizard
6 
7 
8 	AstroMenace is free software: you can redistribute it and/or modify
9 	it under the terms of the GNU General Public License as published by
10 	the Free Software Foundation, either version 3 of the License, or
11 	(at your option) any later version.
12 
13 	AstroMenace 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 AstroMenace. If not, see <https://www.gnu.org/licenses/>.
20 
21 
22 	Website: https://viewizard.com/
23 	Project: https://github.com/viewizard/astromenace
24 	E-mail: viewizard@viewizard.com
25 
26 *************************************************************************************/
27 
28 #include "../object3d.h"
29 #include "../space_ship/space_ship.h"
30 #include "../ground_object/ground_object.h"
31 #include "../projectile/projectile.h"
32 #include "../space_object/space_object.h"
33 
34 // NOTE switch to nested namespace definition (namespace A::B::C { ... }) (since C++17)
35 namespace viewizard {
36 namespace astromenace {
37 
38 namespace {
39 
40 constexpr float RadToDeg = 180.0f / 3.14159f; // convert radian to degree
41 
42 } // unnamed namespace
43 
44 
45 /*
46  * Find angles to aim on target with prediction.
47  */
GetWeaponOnTargetOrientation(eObjectStatus WeaponStatus,const sVECTOR3D & WeaponLocation,const sVECTOR3D & TargetingComputerLocation,const sVECTOR3D & CurrentWeaponRotation,float MinTargetingDistance,const float (& CurrentWeaponRotationMatrix)[9],sVECTOR3D & NeedAngle,bool NeedCenterOrientation,int WeaponType)48 void GetWeaponOnTargetOrientation(eObjectStatus WeaponStatus, const sVECTOR3D &WeaponLocation,
49 				  const sVECTOR3D &TargetingComputerLocation, const sVECTOR3D &CurrentWeaponRotation,
50 				  float MinTargetingDistance, const float (&CurrentWeaponRotationMatrix)[9],
51 				  sVECTOR3D &NeedAngle, bool NeedCenterOrientation, int WeaponType)
52 {
53 	float tmpDistanceToLockedTarget2{1000.0f * 1000.0f};
54 	bool TargetLocked{false};
55 
56 	sVECTOR3D Orientation{0.0f, 0.0f, 1.0f};
57 	vw_Matrix33CalcPoint(Orientation, CurrentWeaponRotationMatrix);
58 	sVECTOR3D PointUp{0.0f, 1.0f, 0.0f};
59 	vw_Matrix33CalcPoint(PointUp, CurrentWeaponRotationMatrix);
60 	sVECTOR3D PointRight{1.0f, 0.0f, 0.0f};
61 	vw_Matrix33CalcPoint(PointRight, CurrentWeaponRotationMatrix);
62 
63 	// vertical plane (left/right), note, OpenGL use right-handed coordinate system
64 	float A, B, C, D;
65 	vw_GetPlaneABCD(A, B, C, D, TargetingComputerLocation,
66 			TargetingComputerLocation + Orientation, TargetingComputerLocation + PointUp);
67 	float A2B2C2D2NormalLength = vw_sqrtf(A * A + B * B + C * C);
68 
69 	// vertical plane (ahead/behind), note, OpenGL use right-handed coordinate system
70 	float A2, B2, C2, D2;
71 	vw_GetPlaneABCD(A2, B2, C2, D2, TargetingComputerLocation,
72 			TargetingComputerLocation + PointRight, TargetingComputerLocation + PointUp);
73 
74 	// vertical plane (ahead/behind), note, OpenGL use right-handed coordinate system
75 	float A3, B3, C3, D3;
76 	vw_GetPlaneABCD(A3, B3, C3, D3, TargetingComputerLocation,
77 			TargetingComputerLocation + Orientation, TargetingComputerLocation + PointRight);
78 	float A3B3C3D3NormalLength = vw_sqrtf(A3 * A3 + B3 * B3 + C3 * C3);
79 
80 	// the idea of tmpDistanceFactorByObjectType is provide targeting priority, we increase factor
81 	// for different types of objects, so, we need next type objects position closer than previous
82 	// in order to change target, but, reset factor to 1.0f in case we lock and check same type
83 	// objects (or don't have any target yet)
84 	float tmpDistanceFactorByObjectType{1.0f};
85 
86 	auto FindTargetCalculateAngles = [&] (const sVECTOR3D &TargetLocation, const sVECTOR3D &TargetOrientation,
87 					      sVECTOR3D TargetGeometryCenter, const float (&TargetRotationMat)[9],
88 					      const float TargetSpeed, const float TargetRadius) {
89 		vw_Matrix33CalcPoint(TargetGeometryCenter, TargetRotationMat);
90 		sVECTOR3D RealLocation = TargetLocation + TargetGeometryCenter;
91 
92 		if ((TargetSpeed != 0.0f) &&
93 		    (WeaponType != 0) &&
94 		    // not a beam
95 		    (WeaponType != 11) && (WeaponType != 12) && (WeaponType != 14) &&
96 		    // not a missile
97 		    (WeaponType != 16) && (WeaponType != 17) && (WeaponType != 18) && (WeaponType != 19)) {
98 			float ProjectileSpeed = GetProjectileSpeed(WeaponType);
99 			float CurrentDist = (RealLocation - TargetingComputerLocation).Length();
100 			float ObjCurrentTime = CurrentDist / ProjectileSpeed;
101 			sVECTOR3D FutureLocation = TargetOrientation ^ (TargetSpeed * ObjCurrentTime);
102 			RealLocation = RealLocation + FutureLocation;
103 		}
104 
105 		// check, that target is ahead of weapon and opposite to it
106 		if ((fabs(A * RealLocation.x +
107 			  B * RealLocation.y +
108 			  C * RealLocation.z + D) <= TargetRadius) &&
109 		    ((A2 * RealLocation.x +
110 		      B2 * RealLocation.y +
111 		      C2 * RealLocation.z + D2) > MinTargetingDistance)) {
112 
113 			sVECTOR3D tmpTargetAngle = CurrentWeaponRotation;
114 			sVECTOR3D tmpDistance = RealLocation - WeaponLocation;
115 			float tmpLength2 = tmpDistance.x * tmpDistance.x +
116 					   tmpDistance.y * tmpDistance.y +
117 					   tmpDistance.z * tmpDistance.z;
118 			float tmpLength = vw_sqrtf(tmpLength2);
119 
120 			if ((tmpLength > 0.0f) && (A3B3C3D3NormalLength > 0.0f)) {
121 				// see "Angle between line and plane" (geometry) for more info about what we are doing here
122 				float tmpSineOfAngle = (A3 * tmpDistance.x + B3 * tmpDistance.y + C3 * tmpDistance.z) /
123 						       (tmpLength * A3B3C3D3NormalLength);
124 				vw_Clamp(tmpSineOfAngle, -1.0f, 1.0f); // arc sine is computed in the interval [-1, +1]
125 				tmpTargetAngle.x = CurrentWeaponRotation.x - asinf(tmpSineOfAngle) * RadToDeg;
126 			}
127 
128 			if (NeedCenterOrientation &&
129 			    (tmpLength > 0.0f) && (A2B2C2D2NormalLength > 0.0f)) {
130 				// see "Angle between line and plane" (geometry) for more info about what we are doing here
131 				float tmpSineOfAngle = (A * tmpDistance.x + B * tmpDistance.y + C * tmpDistance.z) /
132 						       (tmpLength * A2B2C2D2NormalLength);
133 				vw_Clamp(tmpSineOfAngle, -1.0f, 1.0f); // arc sine is computed in the interval [-1, +1]
134 				tmpTargetAngle.y = CurrentWeaponRotation.y - asinf(tmpSineOfAngle) * RadToDeg;
135 			}
136 
137 			if ((tmpDistanceToLockedTarget2 / tmpDistanceFactorByObjectType > tmpLength2) &&
138 			    (fabsf(tmpTargetAngle.x) < 45.0f)) {
139 				NeedAngle = tmpTargetAngle;
140 				tmpDistanceToLockedTarget2 = tmpLength2;
141 				TargetLocked = true;
142 				tmpDistanceFactorByObjectType = 1.0f;
143 			}
144 		}
145 	};
146 
147 	ForEachSpaceShip([&] (const cSpaceShip &tmpShip) {
148 		if ((NeedCheckCollision(tmpShip)) &&
149 		    ObjectsStatusFoe(WeaponStatus, tmpShip.ObjectStatus))
150 			FindTargetCalculateAngles(tmpShip.Location, tmpShip.Orientation, tmpShip.GeometryCenter,
151 						  tmpShip.CurrentRotationMat, tmpShip.Speed, tmpShip.Radius);
152 	});
153 
154 	if (TargetLocked)
155 		tmpDistanceFactorByObjectType = 5.0f;
156 	ForEachGroundObject([&] (const cGroundObject &tmpGround) {
157 		if (NeedCheckCollision(tmpGround) &&
158 		    ObjectsStatusFoe(WeaponStatus, tmpGround.ObjectStatus))
159 			FindTargetCalculateAngles(tmpGround.Location, tmpGround.Orientation, tmpGround.GeometryCenter,
160 						  tmpGround.CurrentRotationMat, tmpGround.Speed, tmpGround.Radius);
161 	});
162 
163 	if (TargetLocked)
164 		tmpDistanceFactorByObjectType = 10.0f;
165 	ForEachSpaceObject([&] (const cSpaceObject &tmpSpace) {
166 		if (NeedCheckCollision(tmpSpace) &&
167 		    ObjectsStatusFoe(WeaponStatus, tmpSpace.ObjectStatus))
168 			FindTargetCalculateAngles(tmpSpace.Location, tmpSpace.Orientation, tmpSpace.GeometryCenter,
169 						  tmpSpace.CurrentRotationMat, tmpSpace.Speed, tmpSpace.Radius);
170 	});
171 }
172 
173 } // astromenace namespace
174 } // viewizard namespace
175