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