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