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