1 /**
2 * @file
3 * @brief Campaign mission
4 */
5
6 /*
7 Copyright (C) 2002-2013 UFO: Alien Invasion.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18 See the GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 */
24
25 #include "../../../cl_shared.h"
26 #include "../cp_campaign.h"
27 #include "../cp_ufo.h"
28 #include "../cp_missions.h"
29 #include "../cp_time.h"
30 #include "../cp_alien_interest.h"
31 #include "../cp_xvi.h"
32
33 /**
34 * @brief Intercept mission is over and is a success: change interest values.
35 * @note Intercept mission
36 */
CP_InterceptMissionIsSuccess(mission_t * mission)37 void CP_InterceptMissionIsSuccess (mission_t* mission)
38 {
39 INT_ChangeIndividualInterest(0.3f, INTERESTCATEGORY_RECON);
40 INT_ChangeIndividualInterest(-0.3f, INTERESTCATEGORY_INTERCEPT);
41 INT_ChangeIndividualInterest(0.1f, INTERESTCATEGORY_HARVEST);
42 if (CP_IsXVIStarted())
43 INT_ChangeIndividualInterest(0.1f, INTERESTCATEGORY_XVI);
44
45 CP_MissionRemove(mission);
46 }
47
48 /**
49 * @brief Intercept mission is over and is a failure: change interest values.
50 * @note Intercept mission
51 */
CP_InterceptMissionIsFailure(mission_t * mission)52 void CP_InterceptMissionIsFailure (mission_t* mission)
53 {
54 INT_ChangeIndividualInterest(0.1f, INTERESTCATEGORY_INTERCEPT);
55 INT_ChangeIndividualInterest(0.05f, INTERESTCATEGORY_BUILDING);
56 INT_ChangeIndividualInterest(0.05f, INTERESTCATEGORY_BASE_ATTACK);
57 INT_ChangeIndividualInterest(0.05f, INTERESTCATEGORY_TERROR_ATTACK);
58
59 CP_MissionRemove(mission);
60 }
61
62 #define UFO_EPSILON 0.00001f
63
64 /**
65 * @brief Intercept mission ends: UFO leave earth.
66 * @param[in] mission Pointer to the mission
67 * @param[in] destroyed true if the UFO actually destroyed the installation, false else
68 * @note Intercept mission -- Stage 3
69 */
CP_InterceptMissionLeave(mission_t * mission,bool destroyed)70 void CP_InterceptMissionLeave (mission_t* mission, bool destroyed)
71 {
72 installation_t* installation;
73
74 assert(mission->ufo);
75
76 mission->stage = STAGE_RETURN_TO_ORBIT;
77
78 /* if the mission was an attack of an installation, destroy it */
79 installation = mission->data.installation;
80 if (installation) {
81 vec3_t missionPos;
82
83 Vector2Copy(mission->pos, missionPos);
84 missionPos[2] = installation->pos[2];
85 if (destroyed && VectorCompareEps(missionPos, installation->pos, UFO_EPSILON))
86 INS_DestroyInstallation(installation);
87 }
88
89 CP_MissionDisableTimeLimit(mission);
90 UFO_SetRandomDest(mission->ufo);
91 CP_MissionRemoveFromGeoscape(mission);
92 /* Display UFO on geoscape if it is detected */
93 mission->ufo->landed = false;
94 }
95
96 /**
97 * @brief UFO starts to attack the installation.
98 * @note Intercept mission -- Stage 2
99 */
CP_InterceptAttackInstallation(mission_t * mission)100 static void CP_InterceptAttackInstallation (mission_t* mission)
101 {
102 const date_t minAttackDelay = {0, 3600};
103 const date_t attackDelay = {0, 21600}; /* How long the UFO should stay on earth */
104 installation_t* installation;
105 vec3_t missionPos;
106
107 mission->stage = STAGE_INTERCEPT;
108
109 installation = mission->data.installation;
110 Vector2Copy(mission->pos, missionPos);
111 if (!VectorCompareEps(missionPos, installation->pos, UFO_EPSILON)) {
112 mission->finalDate = ccs.date;
113 return;
114 }
115
116 /* Make round around the position of the mission */
117 UFO_SetRandomDestAround(mission->ufo, mission->pos);
118 mission->finalDate = Date_Add(ccs.date, Date_Random(minAttackDelay, attackDelay));
119 }
120
121 /**
122 * @brief Set Intercept mission: UFO looks for new aircraft target.
123 * @note Intercept mission -- Stage 1
124 */
CP_InterceptAircraftMissionSet(mission_t * mission)125 void CP_InterceptAircraftMissionSet (mission_t* mission)
126 {
127 const date_t minReconDelay = {3, 0};
128 const date_t reconDelay = {6, 0}; /* How long the UFO should stay on earth */
129
130 mission->stage = STAGE_INTERCEPT;
131 mission->finalDate = Date_Add(ccs.date, Date_Random(minReconDelay, reconDelay));
132 }
133
134 /**
135 * @brief Choose Base that will be attacked, and add it to mission description.
136 * @note Base attack mission -- Stage 1
137 * @return Pointer to the base, nullptr if no base set
138 */
CP_InterceptChooseInstallation(const mission_t * mission)139 static installation_t* CP_InterceptChooseInstallation (const mission_t* mission)
140 {
141 float randomNumber, sum = 0.0f;
142 installation_t* installation = nullptr;
143
144 assert(mission);
145
146 /* Choose randomly a base depending on alienInterest values for those bases */
147 INS_Foreach(i) {
148 sum += i->alienInterest;
149 }
150 randomNumber = frand() * sum;
151 INS_Foreach(i) {
152 randomNumber -= i->alienInterest;
153 if (randomNumber < 0) {
154 installation = i;
155 break;
156 }
157 }
158
159 /* Make sure we have a base */
160 assert(installation && (randomNumber < 0));
161
162 return installation;
163 }
164
165 /**
166 * @brief Set Intercept mission: UFO chooses an installation an flies to it.
167 * @note Intercept mission -- Stage 1
168 */
CP_InterceptGoToInstallation(mission_t * mission)169 void CP_InterceptGoToInstallation (mission_t* mission)
170 {
171 installation_t* installation;
172 assert(mission->ufo);
173
174 mission->stage = STAGE_MISSION_GOTO;
175
176 installation = CP_InterceptChooseInstallation(mission);
177 if (!installation) {
178 Com_Printf("CP_InterceptGoToInstallation: no installation found\n");
179 CP_MissionRemove(mission);
180 return;
181 }
182 mission->data.installation = installation;
183
184 Vector2Copy(installation->pos, mission->pos);
185 mission->posAssigned = true;
186
187 CP_MissionDisableTimeLimit(mission);
188 UFO_SendToDestination(mission->ufo, mission->pos);
189 }
190
191 /**
192 * @brief Set Intercept mission: choose between attacking aircraft or installations.
193 * @note Intercept mission -- Stage 1
194 */
CP_InterceptMissionSet(mission_t * mission)195 static void CP_InterceptMissionSet (mission_t* mission)
196 {
197 assert(mission->ufo);
198
199 /* Only large UFOs can attack installations -- if there are installations to attack */
200 switch (mission->ufo->ufotype) {
201 case UFO_HARVESTER:
202 case UFO_CORRUPTER:
203 if (INS_HasAny())
204 CP_InterceptGoToInstallation(mission);
205 break;
206 default:
207 break;
208 }
209
210 CP_InterceptAircraftMissionSet(mission);
211 }
212
213 /**
214 * @brief Fill an array with available UFOs for Intercept mission type.
215 * @param[in] mission Pointer to the mission we are currently creating.
216 * @param[out] ufoTypes Array of ufoType_t that may be used for this mission.
217 * @note Intercept mission -- Stage 0
218 * @return number of elements written in @c ufoTypes
219 */
CP_InterceptMissionAvailableUFOs(const mission_t * mission,ufoType_t * ufoTypes)220 int CP_InterceptMissionAvailableUFOs (const mission_t* mission, ufoType_t* ufoTypes)
221 {
222 int num = 0;
223 /* Probability to get a UFO that targets installations. Note
224 * the probability is this number divided by the number of
225 * possible UFOs. */
226 const float TARGET_INS_PROBABILITY = 0.25;
227
228 if (UFO_ShouldAppearOnGeoscape(UFO_FIGHTER))
229 ufoTypes[num++] = UFO_FIGHTER;
230 if (UFO_ShouldAppearOnGeoscape(UFO_GUNBOAT))
231 ufoTypes[num++] = UFO_GUNBOAT;
232
233 /* don't make attack on installation happens too often */
234 if (frand() < TARGET_INS_PROBABILITY) {
235 if (UFO_ShouldAppearOnGeoscape(UFO_HARVESTER))
236 ufoTypes[num++] = UFO_HARVESTER;
237 if (UFO_ShouldAppearOnGeoscape(UFO_CORRUPTER))
238 ufoTypes[num++] = UFO_CORRUPTER;
239 }
240
241 return num;
242 }
243
244 /**
245 * @brief Determine what action should be performed when a Intercept mission stage ends.
246 * @param[in] mission Pointer to the mission which stage ended.
247 */
CP_InterceptNextStage(mission_t * mission)248 void CP_InterceptNextStage (mission_t* mission)
249 {
250 switch (mission->stage) {
251 case STAGE_NOT_ACTIVE:
252 /* Create Intercept mission */
253 CP_MissionBegin(mission);
254 break;
255 case STAGE_COME_FROM_ORBIT:
256 /* UFO start looking for target */
257 CP_InterceptMissionSet(mission);
258 break;
259 case STAGE_MISSION_GOTO:
260 CP_InterceptAttackInstallation(mission);
261 break;
262 case STAGE_INTERCEPT:
263 assert(mission->ufo);
264 /* Leave earth */
265 if (AIRFIGHT_ChooseWeapon(mission->ufo->weapons, mission->ufo->maxWeapons, mission->ufo->pos, mission->ufo->pos) !=
266 AIRFIGHT_WEAPON_CAN_NEVER_SHOOT && mission->ufo->status == AIR_UFO && !mission->data.installation) {
267 /* UFO is fighting and has still ammo, wait a little bit before leaving (UFO is not attacking an installation) */
268 const date_t AdditionalDelay = {0, 3600}; /* check every hour if there is still ammos */
269 mission->finalDate = Date_Add(ccs.date, AdditionalDelay);
270 } else
271 CP_InterceptMissionLeave(mission, true);
272 break;
273 case STAGE_RETURN_TO_ORBIT:
274 /* mission is over, remove mission */
275 CP_InterceptMissionIsSuccess(mission);
276 break;
277 default:
278 Com_Printf("CP_InterceptNextStage: Unknown stage: %i, removing mission.\n", mission->stage);
279 CP_MissionRemove(mission);
280 break;
281 }
282 }
283