1 /*
2  *
3  *   Copyright (c) 1994, 2002, 2003 Johannes Prix
4  *   Copyright (c) 1994, 2002 Reinhard Prix
5  *   Copyright (c) 2004-2010 Arthur Huillet
6  *
7  *
8  *  This file is part of Freedroid
9  *
10  *  Freedroid is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  Freedroid is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with Freedroid; see the file COPYING. If not, write to the
22  *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23  *  MA  02111-1307  USA
24  *
25  */
26 
27 #define _mission_c
28 
29 #include "system.h"
30 
31 #include "defs.h"
32 #include "struct.h"
33 #include "global.h"
34 #include "map.h"
35 #include "proto.h"
36 
37 /**
38  * This function is responsible for making a new quest diary entry
39  * visible inside the quest browser.
40  */
mission_diary_add(const char * mis_name,const char * diarytext)41 void mission_diary_add(const char *mis_name, const char *diarytext)
42 {
43 	int mis_num = GetMissionIndexByName(mis_name);
44 	int idx = 0;
45 
46 	while (idx < MAX_MISSION_DESCRIPTION_TEXTS && Me.AllMissions[mis_num].mission_diary_texts[idx])
47 		idx++;
48 
49 	if (idx >= MAX_MISSION_DESCRIPTION_TEXTS) {
50 		error_message(__FUNCTION__,
51 			     "There is no more room to append mission diary text \"%s\" to mission name \"%s\". Doing nothing.",
52 			     PLEASE_INFORM, diarytext, mis_name);
53 		return;
54 	}
55 
56 
57 	Me.AllMissions[mis_num].mission_description_time[idx] = Me.current_game_date;
58 
59 	Me.AllMissions[mis_num].mission_diary_texts[idx] = strdup(diarytext);
60 
61 	Me.quest_browser_changed = 1;
62 };
63 
64 /*----------------------------------------------------------------------
65  * This function checks, if the influencer has succeeded in his given
66  * mission.  If not it returns, if yes the EndTitle/Debriefing is
67  * started.
68  ----------------------------------------------------------------------*/
CheckIfMissionIsComplete(void)69 void CheckIfMissionIsComplete(void)
70 {
71 	int mis_num;
72 	static int CheckMissionGrid;
73 	int this_mission_seems_completed;
74 	int checked_one_criterion;
75 
76 #define MIS_COMPLETE_DEBUG 1
77 
78 	// We do not need to check for mission completed EVERY frame
79 	// It will be enough to do it now and then..., e.g. every 50th frame
80 	//
81 	CheckMissionGrid++;
82 	if ((CheckMissionGrid % 50) != 0)
83 		return;
84 
85 	for (mis_num = 0; mis_num < MAX_MISSIONS_IN_GAME; mis_num++) {
86 		// We need not do anything, if the mission has already failed or if
87 		// the mission is already completed or if the mission does not exist
88 		// at all or if the mission was not assigned yet
89 		//
90 		if (Me.AllMissions[mis_num].MissionIsComplete == TRUE)
91 			continue;
92 		if (Me.AllMissions[mis_num].MissionWasFailed == TRUE)
93 			continue;
94 		if (Me.AllMissions[mis_num].MissionExistsAtAll != TRUE)
95 			continue;
96 		if (Me.AllMissions[mis_num].MissionWasAssigned != TRUE)
97 			continue;
98 
99 		DebugPrintf(MIS_COMPLETE_DEBUG, "\nSomething was assigned at all..... mis_num = %d ", mis_num);
100 
101 		this_mission_seems_completed = TRUE;
102 		checked_one_criterion = FALSE;
103 
104 		// Continue if the Mission target KillMarker is given but not fulfilled
105 		//
106 		if (Me.AllMissions[mis_num].KillMarker != (-1)) {
107 			enemy *erot;
108 			BROWSE_ALIVE_BOTS(erot) {
109 				if (erot->marker == Me.AllMissions[mis_num].KillMarker) {
110 					DebugPrintf(MIS_COMPLETE_DEBUG,
111 						    "\nOne of the marked droids is still alive... (%p at %f:%f on %d)\n", erot,
112 						    erot->pos.x, erot->pos.y, erot->pos.z);
113 					this_mission_seems_completed = FALSE;
114 					break;
115 				}
116 
117 			}
118 			checked_one_criterion = TRUE;
119 		}
120 		// Continue if the Mission target must_clear_level is given but not fulfilled
121 		//
122 		if (Me.AllMissions[mis_num].must_clear_level != (-1)) {
123 			enemy *erot;
124 			BROWSE_LEVEL_BOTS(erot, Me.AllMissions[mis_num].must_clear_level) {
125 				if (!is_friendly(erot->faction, FACTION_SELF)) {
126 					this_mission_seems_completed = FALSE;
127 					break;
128 				}
129 			}
130 			checked_one_criterion = TRUE;
131 		}
132 
133 
134 		// If the mission actually had criteria we checked for, and those criteria
135 		// are OK, the mission is finished.
136 		if (checked_one_criterion && this_mission_seems_completed)
137 			CompleteMission(Me.AllMissions[mis_num].mission_name);
138 
139 	}
140 };				// void CheckIfMissionIsComplete
141 
142 /**
143  * This function assigns a new mission to the player, which means
144  * that the status of the mission in the mission array is changed and
145  * perhaps the mission log activated.
146  */
AssignMission(const char * name)147 void AssignMission(const char *name)
148 {
149 	int MissNum = GetMissionIndexByName(name);
150 
151 	Me.AllMissions[MissNum].MissionWasAssigned = TRUE;
152 
153 	if (Me.AllMissions[MissNum].assignment_lua_code)
154 		run_lua(LUA_DIALOG, Me.AllMissions[MissNum].assignment_lua_code);
155 
156 	Me.quest_browser_changed = 1;
157 };
158 
159 /**
160  * This function marks a mission as complete
161  */
CompleteMission(const char * name)162 void CompleteMission(const char *name)
163 {
164 	int MissNum = GetMissionIndexByName(name);
165 
166 	Me.AllMissions[MissNum].MissionIsComplete = TRUE;
167 
168 	if (Me.AllMissions[MissNum].completion_lua_code)
169 		run_lua(LUA_DIALOG, Me.AllMissions[MissNum].completion_lua_code);
170 
171 	Me.quest_browser_changed = 1;
172 }
173 
174 /**
175  * At the start of every new game, the mission info (i.e. which missions
176  * are already assigned, completed, failed, available and such) should
177  * be reset to default state, so that no zombie mission entries can appear.
178  */
clear_tux_mission_info()179 void clear_tux_mission_info()
180 {
181 	int i;
182 	int diary_entry_nr;
183 
184 	for (i = 0; i < MAX_MISSIONS_IN_GAME; i++) {
185 		Me.AllMissions[i].MissionExistsAtAll = FALSE;
186 		Me.AllMissions[i].MissionIsComplete = FALSE;
187 		Me.AllMissions[i].MissionWasFailed = FALSE;
188 		Me.AllMissions[i].MissionWasAssigned = FALSE;
189 
190 		if (Me.AllMissions[i].mission_name) {
191 			free(Me.AllMissions[i].mission_name);
192 			Me.AllMissions[i].mission_name = NULL;
193 		}
194 
195 		for (diary_entry_nr = 0; diary_entry_nr < MAX_MISSION_DESCRIPTION_TEXTS; diary_entry_nr++) {
196 			if (Me.AllMissions[i].mission_diary_texts[diary_entry_nr]) {
197 				free(Me.AllMissions[i].mission_diary_texts[diary_entry_nr]);
198 				Me.AllMissions[i].mission_diary_texts[diary_entry_nr] = NULL;
199 			}
200 
201 			Me.AllMissions[i].mission_description_time[diary_entry_nr] = 0;
202 		}
203 
204 		if (Me.AllMissions[i].completion_lua_code) {
205 			free(Me.AllMissions[i].completion_lua_code);
206 			Me.AllMissions[i].completion_lua_code = NULL;
207 		}
208 
209 		if (Me.AllMissions[i].assignment_lua_code) {
210 			free(Me.AllMissions[i].assignment_lua_code);
211 			Me.AllMissions[i].assignment_lua_code = NULL;
212 		}
213 	}
214 }
215 
216 /**
217  * This function reads the mission specifications from the mission file
218  * which is assumed to be loaded into memory already.
219  */
GetQuestList(char * QuestListFilename)220 void GetQuestList(char *QuestListFilename)
221 {
222 	char *EndOfMissionTargetPointer;
223 	int MissionTargetIndex = 0;
224 	char *MissionTargetPointer;
225 	char fpath[PATH_MAX];
226 	char *MissionFileContents;
227 	char InnerPreservedLetter = 0;
228 	int diary_entry_nr;
229 
230 #define MISSION_TARGET_SUBSECTION_START_STRING "** Start of this mission target subsection **"
231 #define MISSION_TARGET_SUBSECTION_END_STRING "** End of this mission target subsection **"
232 
233 #define MISSION_TARGET_NAME_INITIALIZER "Mission Name=_\""
234 
235 #define MISSION_TARGET_KILL_MARKER "Mission target is to kill droids with marker : "
236 #define MISSION_TARGET_MUST_CLEAR_LEVEL "Mission target is to kill all hostile droids on this level : "
237 
238 #define MISSION_ASSIGNMENT_LUACODE_STRING "Assignment LuaCode={"
239 #define MISSION_COMPLETION_LUACODE_STRING "Completion LuaCode={"
240 
241 	// At first we must load the quest list file given...
242 	//
243 	find_file(QuestListFilename, MAP_DIR, fpath, PLEASE_INFORM | IS_FATAL);
244 	MissionFileContents = ReadAndMallocAndTerminateFile(fpath, "*** END OF QUEST LIST *** LEAVE THIS TERMINATOR IN HERE ***");
245 	MissionTargetPointer = MissionFileContents;
246 
247 	for (MissionTargetIndex = 0; MissionTargetIndex < MAX_MISSIONS_IN_GAME; MissionTargetIndex++) {
248 		Me.AllMissions[MissionTargetIndex].MissionExistsAtAll = FALSE;
249 	}
250 
251 	MissionTargetIndex = 0;
252 	while ((MissionTargetPointer = strstr(MissionTargetPointer, MISSION_TARGET_SUBSECTION_START_STRING)) != NULL) {
253 		EndOfMissionTargetPointer = LocateStringInData(MissionTargetPointer, MISSION_TARGET_SUBSECTION_END_STRING);
254 
255 		if (MissionTargetIndex >= MAX_MISSIONS_IN_GAME)
256 			error_message(__FUNCTION__, "The number of quests specified in %s exceeds MAX_MISSIONS_IN_GAME (%d).",
257 				     PLEASE_INFORM | IS_FATAL, QuestListFilename, MAX_MISSIONS_IN_GAME);
258 
259 		// We need to add an inner terminator here, so that the strstr operation
260 		// below will know where to stop within this subsection.
261 		//
262 		InnerPreservedLetter = *EndOfMissionTargetPointer;
263 		*EndOfMissionTargetPointer = 0;
264 
265 		Me.AllMissions[MissionTargetIndex].MissionExistsAtAll = TRUE;
266 
267 		Me.AllMissions[MissionTargetIndex].mission_name = ReadAndMallocStringFromData(MissionTargetPointer, MISSION_TARGET_NAME_INITIALIZER, "\"");
268 
269 		// From here on we read the details of the mission target, i.e. what the
270 		// influencer has to do, so that the mission can be thought of as completed
271 		//
272 
273 		ReadValueFromStringWithDefault(MissionTargetPointer, MISSION_TARGET_KILL_MARKER, "%d", "-1",
274 				    &Me.AllMissions[MissionTargetIndex].KillMarker, EndOfMissionTargetPointer);
275 
276 		ReadValueFromStringWithDefault(MissionTargetPointer, MISSION_TARGET_MUST_CLEAR_LEVEL, "%d", "-1",
277 				    &Me.AllMissions[MissionTargetIndex].must_clear_level, EndOfMissionTargetPointer);
278 
279 		if (strstr(MissionTargetPointer, MISSION_COMPLETION_LUACODE_STRING)) {
280 			Me.AllMissions[MissionTargetIndex].completion_lua_code =
281 			    ReadAndMallocStringFromData(MissionTargetPointer, MISSION_COMPLETION_LUACODE_STRING, "}");
282 		} else {
283 			Me.AllMissions[MissionTargetIndex].completion_lua_code = NULL;
284 		}
285 
286 		if (strstr(MissionTargetPointer, MISSION_ASSIGNMENT_LUACODE_STRING)) {
287 			Me.AllMissions[MissionTargetIndex].assignment_lua_code =
288 			    ReadAndMallocStringFromData(MissionTargetPointer, MISSION_ASSIGNMENT_LUACODE_STRING, "}");
289 		} else {
290 			Me.AllMissions[MissionTargetIndex].assignment_lua_code = NULL;
291 		}
292 		// Now it is time to read in the mission diary entries, that might
293 		// be displayed in the quest browser later.
294 		//
295 		for (diary_entry_nr = 0; diary_entry_nr < MAX_MISSION_DESCRIPTION_TEXTS; diary_entry_nr++) {
296 			Me.AllMissions[MissionTargetIndex].mission_diary_texts[diary_entry_nr] = NULL;
297 		}
298 
299 		// Now we are done with reading in THIS one mission target
300 		// We need to advance the MissionTargetPointer, so that we avoid doubly
301 		// reading in this mission OR ONE OF THIS MISSIONS VALUES!!!!
302 		//
303 		// And we need of course to advance the array index for mission targets too...
304 		//
305 		MissionTargetPointer = EndOfMissionTargetPointer;	// to avoid double entering the same target
306 		MissionTargetIndex++;	// to avoid overwriting the same entry again
307 
308 		// We restore the termination character we added before, even if that
309 		// is maybe not really necessary...
310 		//
311 		*EndOfMissionTargetPointer = InnerPreservedLetter;
312 
313 	}			// while mission target found...
314 
315 	// Finally we record the number of mission targets scanned and are done with this function
316 	DebugPrintf(1, "\nNUMBER OF MISSION TARGETS FOUND: %d.\n", MissionTargetIndex);
317 	fflush(stdout);
318 	free(MissionFileContents);
319 };				// void Get_Mission_Targets( ... )
320 
321 /**
322  * This function returns the mission that has the specified name.
323  */
GetMissionIndexByName(const char * name)324 int GetMissionIndexByName(const char *name)
325 {
326 	int cidx;
327 
328 	for (cidx = 0; cidx < MAX_MISSIONS_IN_GAME; cidx++) {
329 		const char *n = Me.AllMissions[cidx].mission_name;
330 		if (!n)
331 			continue;
332 
333 		if (!strcmp(n, name))
334 			return cidx;
335 	}
336 
337 	error_message(__FUNCTION__, "Unable to find mission named \"%s\"", PLEASE_INFORM | IS_FATAL, name);
338 	return -1;
339 }
340 
341 #undef _mission_c
342