1 /*
2  * See Licensing and Copyright notice in naev.h
3  */
4 
5 /**
6  * @file mission.c
7  *
8  * @brief Handles missions.
9  */
10 
11 
12 #include "mission.h"
13 
14 #include "naev.h"
15 
16 #include <stdint.h>
17 #include "nstring.h"
18 #include <stdlib.h>
19 
20 #include "nlua.h"
21 #include "nluadef.h"
22 #include "nlua_faction.h"
23 #include "nlua_ship.h"
24 #include "nlua_misn.h"
25 #include "rng.h"
26 #include "log.h"
27 #include "hook.h"
28 #include "ndata.h"
29 #include "nxml.h"
30 #include "nxml_lua.h"
31 #include "faction.h"
32 #include "player.h"
33 #include "space.h"
34 #include "cond.h"
35 #include "gui_osd.h"
36 #include "npc.h"
37 #include "array.h"
38 #include "land.h"
39 
40 
41 #define XML_MISSION_ID        "Missions" /**< XML document identifier */
42 #define XML_MISSION_TAG       "mission" /**< XML mission tag. */
43 
44 #define MISSION_CHUNK         32 /**< Chunk allocation. */
45 
46 
47 /*
48  * current player missions
49  */
50 static unsigned int mission_id = 0; /**< Mission ID generator. */
51 Mission *player_missions[MISSION_MAX]; /**< Player's active missions. */
52 
53 
54 /*
55  * mission stack
56  */
57 static MissionData *mission_stack = NULL; /**< Unmutable after creation */
58 static int mission_nstack = 0; /**< Missions in stack. */
59 
60 
61 /*
62  * prototypes
63  */
64 /* static */
65 /* Generation. */
66 static unsigned int mission_genID (void);
67 static int mission_init( Mission* mission, MissionData* misn, int genid, int create, unsigned int *id );
68 static void mission_freeData( MissionData* mission );
69 /* Matching. */
70 static int mission_compare( const void* arg1, const void* arg2 );
71 static int mission_meetReq( int mission, int faction,
72       const char* planet, const char* sysname );
73 static int mission_matchFaction( MissionData* misn, int faction );
74 static int mission_location( const char* loc );
75 /* Loading. */
76 static int mission_parse( MissionData* temp, const xmlNodePtr parent );
77 static int missions_parseActive( xmlNodePtr parent );
78 /* externed */
79 int missions_saveActive( xmlTextWriterPtr writer );
80 int missions_loadActive( xmlNodePtr parent );
81 
82 
83 /**
84  * @brief Generates a new id for the mission.
85  *
86  *    @return New id for the mission.
87  */
mission_genID(void)88 static unsigned int mission_genID (void)
89 {
90    unsigned int id;
91    int i;
92    id = ++mission_id; /* default id, not safe if loading */
93 
94    /* we save mission ids, so check for collisions with player's missions */
95    for (i=0; i<MISSION_MAX; i++)
96       if (id == player_missions[i]->id) /* mission id was loaded from save */
97          return mission_genID(); /* recursively try again */
98    return id;
99 }
100 
101 /**
102  * @brief Gets id from mission name.
103  *
104  *    @param name Name to match.
105  *    @return id of the matching mission.
106  */
mission_getID(const char * name)107 int mission_getID( const char* name )
108 {
109    int i;
110 
111    for (i=0; i<mission_nstack; i++)
112       if (strcmp(name,mission_stack[i].name)==0)
113          return i;
114 
115    DEBUG("Mission '%s' not found in stack", name);
116    return -1;
117 }
118 
119 
120 /**
121  * @brief Gets a MissionData based on ID.
122  *
123  *    @param id ID to match.
124  *    @return MissonData matching ID.
125  */
mission_get(int id)126 MissionData* mission_get( int id )
127 {
128    if ((id < 0) || (id >= mission_nstack)) return NULL;
129    return &mission_stack[id];
130 }
131 
132 
133 /**
134  * @brief Gets mission data from a name.
135  */
mission_getFromName(const char * name)136 MissionData* mission_getFromName( const char* name )
137 {
138    int id;
139 
140    id = mission_getID( name );
141    if (id < 0)
142       return NULL;
143 
144    return mission_get( id );
145 }
146 
147 
148 /**
149  * @brief Initializes a mission.
150  *
151  *    @param mission Mission to initialize.
152  *    @param misn Data to use.
153  *    @param genid 1 if should generate id, 0 otherwise.
154  *    @param create 1 if should run create function, 0 otherwise.
155  *    @param[out] id ID of the newly created mission.
156  *    @return 0 on success.
157  */
mission_init(Mission * mission,MissionData * misn,int genid,int create,unsigned int * id)158 static int mission_init( Mission* mission, MissionData* misn, int genid, int create, unsigned int *id )
159 {
160    char *buf;
161    uint32_t bufsize;
162    int ret;
163 
164    /* clear the mission */
165    memset( mission, 0, sizeof(Mission) );
166 
167    /* Create id if needed. */
168    mission->id    = (genid) ? mission_genID() : 0;
169    if (id != NULL)
170       *id         = mission->id;
171    mission->data  = misn;
172    if (create) {
173       mission->title = strdup(misn->name);
174       mission->desc  = strdup("No description.");
175    }
176 
177    /* init Lua */
178    mission->env = nlua_newEnv(1);
179 
180    misn_loadLibs( mission->env ); /* load our custom libraries */
181 
182    /* load the file */
183    buf = ndata_read( misn->lua, &bufsize );
184    if (buf == NULL) {
185       WARN("Mission '%s' Lua script not found.", misn->lua );
186       return -1;
187    }
188    if (nlua_dobufenv(mission->env, buf, bufsize, misn->lua) != 0) {
189       WARN("Error loading mission file: %s\n"
190           "%s\n"
191           "Most likely Lua file has improper syntax, please check",
192             misn->lua, lua_tostring(naevL, -1));
193       free(buf);
194       return -1;
195    }
196    free(buf);
197 
198    /* run create function */
199    if (create) {
200       /* Failed to create. */
201       ret = misn_run( mission, "create");
202       if (ret) {
203          mission_cleanup(mission);
204          return ret;
205       }
206    }
207 
208    return 0;
209 }
210 
211 
212 /**
213  * @brief Small wrapper for misn_run.
214  *
215  *    @param mission Mission to accept.
216  *    @return -1 on error, 1 on misn.finish() call, 2 if mission got deleted
217  *            and 0 normally.
218  *
219  * @sa misn_run
220  */
mission_accept(Mission * mission)221 int mission_accept( Mission* mission )
222 {
223    return misn_run( mission, "accept" );
224 }
225 
226 
227 /**
228  * @brief Checks to see if mission is already running.
229  *
230  *    @param misn Mission to check if is already running.
231  *    @return 1 if already running, 0 if isn't.
232  */
mission_alreadyRunning(MissionData * misn)233 int mission_alreadyRunning( MissionData* misn )
234 {
235    int i;
236    for (i=0; i<MISSION_MAX; i++)
237       if (player_missions[i]->data == misn)
238          return 1;
239    return 0;
240 }
241 
242 
243 /**
244  * @brief Checks to see if a mission meets the requirements.
245  *
246  *    @param mission ID of the mission to check.
247  *    @param faction Faction of the current planet.
248  *    @param planet Name of the current planet.
249  *    @param sysname Name of the current system.
250  *    @return 1 if requirements are met, 0 if they aren't.
251  */
mission_meetReq(int mission,int faction,const char * planet,const char * sysname)252 static int mission_meetReq( int mission, int faction,
253       const char* planet, const char* sysname )
254 {
255    MissionData* misn;
256    int c;
257 
258    misn = mission_get( mission );
259    if (misn == NULL) /* In case it doesn't exist */
260       return 0;
261 
262    /* If planet, must match planet. */
263    if ((misn->avail.planet != NULL) && (strcmp(misn->avail.planet,planet)!=0))
264       return 0;
265 
266    /* If system, must match system. */
267    if ((misn->avail.system != NULL) && (strcmp(misn->avail.system,sysname)!=0))
268       return 0;
269 
270    /* Match faction. */
271    if ((faction >= 0) && !mission_matchFaction(misn,faction))
272       return 0;
273 
274    /* Must not be already done or running if unique. */
275    if (mis_isFlag(misn,MISSION_UNIQUE) &&
276          (player_missionAlreadyDone(mission) ||
277           mission_alreadyRunning(misn)))
278       return 0;
279 
280    /* Must meet Lua condition. */
281    if (misn->avail.cond != NULL) {
282       c = cond_check(misn->avail.cond);
283       if (c < 0) {
284          WARN("Conditional for mission '%s' failed to run", misn->name);
285          return 0;
286       }
287       else if (!c)
288          return 0;
289    }
290 
291    /* Must meet previous mission requirements. */
292    if ((misn->avail.done != NULL) &&
293          (player_missionAlreadyDone( mission_getID(misn->avail.done) ) == 0))
294       return 0;
295 
296   return 1;
297 }
298 
299 
300 /**
301  * @brief Runs missions matching location, all Lua side and one-shot.
302  *
303  *    @param loc Location to match.
304  *    @param faction Faction of the planet.
305  *    @param planet Name of the current planet.
306  *    @param sysname Name of the current system.
307  */
missions_run(int loc,int faction,const char * planet,const char * sysname)308 void missions_run( int loc, int faction, const char* planet, const char* sysname )
309 {
310    MissionData* misn;
311    Mission mission;
312    int i;
313    double chance;
314 
315    for (i=0; i<mission_nstack; i++) {
316       misn = &mission_stack[i];
317       if (misn->avail.loc != loc)
318          continue;
319 
320       if (!mission_meetReq(i, faction, planet, sysname))
321          continue;
322 
323       chance = (double)(misn->avail.chance % 100)/100.;
324       if (chance == 0.) /* We want to consider 100 -> 100% not 0% */
325          chance = 1.;
326 
327       if (RNGF() < chance) {
328          mission_init( &mission, misn, 1, 1, NULL );
329          mission_cleanup(&mission); /* it better clean up for itself or we do it */
330       }
331    }
332 }
333 
334 
335 /**
336  * @brief Starts a mission.
337  *
338  *  Mission must still call misn.accept() to actually get added to the player's
339  * active missions.
340  *
341  *    @param name Name of the mission to start.
342  *    @param[out] id ID of the newly created mission.
343  *    @return 0 on success, >0 on forced exit (misn.finish), <0 on error.
344  */
mission_start(const char * name,unsigned int * id)345 int mission_start( const char *name, unsigned int *id )
346 {
347    Mission mission;
348    MissionData *mdat;
349    int ret;
350 
351    /* Try to get the mission. */
352    mdat = mission_get( mission_getID(name) );
353    if (mdat == NULL)
354       return -1;
355 
356    /* Try to run the mission. */
357    ret = mission_init( &mission, mdat, 1, 1, id );
358    /* Add to mission giver if necessary. */
359    if (landed && (ret==0) && (mdat->avail.loc==MIS_AVAIL_BAR))
360       npc_patchMission( &mission );
361    else
362       mission_cleanup( &mission ); /* Clean up in case not accepted. */
363 
364    return ret;
365 }
366 
367 
368 /**
369  * @brief Adds a system marker to a mission.
370  */
mission_addMarker(Mission * misn,int id,int sys,SysMarker type)371 int mission_addMarker( Mission *misn, int id, int sys, SysMarker type )
372 {
373    MissionMarker *marker;
374    int i, n, m;
375 
376    /* Create array. */
377    if (misn->markers == NULL)
378       misn->markers = array_create( MissionMarker );
379 
380    /* Avoid ID collisions. */
381    if (id < 0) {
382       m = -1;
383       n = array_size( misn->markers );
384       for (i=0; i<n; i++)
385          if (misn->markers[i].id > m)
386             m = misn->markers[i].id;
387       id = m+1;
388    }
389 
390    /* Create the marker. */
391    marker      = &array_grow( &misn->markers );
392    marker->id  = id;
393    marker->sys = sys;
394    marker->type = type;
395 
396    return marker->id;
397 }
398 
399 
400 /**
401  * @brief Marks all active systems that need marking.
402  */
mission_sysMark(void)403 void mission_sysMark (void)
404 {
405    int i, j, n;
406    MissionMarker *m;
407 
408    /* Clear markers. */
409    space_clearMarkers();
410 
411    for (i=0; i<MISSION_MAX; i++) {
412       /* Must be a valid player mission. */
413       if (player_missions[i]->id == 0)
414          continue;
415       /* Must have markers. */
416       if (player_missions[i]->markers == NULL)
417          continue;
418 
419       n = array_size( player_missions[i]->markers );
420       for (j=0; j<n; j++) {
421          m = &player_missions[i]->markers[j];
422 
423          /* Add the individual markers. */
424          space_addMarker( m->sys, m->type );
425       }
426    }
427 }
428 
429 
430 /**
431  * @brief Marks the system of the computer mission to reflect where it will head to.
432  *
433  * Does not modify other markers.
434  *
435  *    @param misn Mission to mark.
436  */
mission_sysComputerMark(Mission * misn)437 void mission_sysComputerMark( Mission* misn )
438 {
439    StarSystem *sys;
440    int i, n;
441 
442    /* Clear markers. */
443    space_clearComputerMarkers();
444 
445    /* Set all the markers. */
446    if (misn->markers != NULL) {
447       n = array_size(misn->markers);
448       for (i=0; i<n; i++) {
449          sys = system_getIndex( misn->markers[i].sys );
450          sys_setFlag( sys,SYSTEM_CMARKED );
451       }
452    }
453 }
454 
455 
456 /**
457  * @brief Links cargo to the mission for posterior cleanup.
458  *
459  *    @param misn Mission to link cargo to.
460  *    @param cargo_id ID of cargo to link.
461  *    @return 0 on success.
462  */
mission_linkCargo(Mission * misn,unsigned int cargo_id)463 int mission_linkCargo( Mission* misn, unsigned int cargo_id )
464 {
465    misn->ncargo++;
466    misn->cargo = realloc( misn->cargo, sizeof(unsigned int) * misn->ncargo);
467    misn->cargo[ misn->ncargo-1 ] = cargo_id;
468 
469    return 0;
470 }
471 
472 
473 /**
474  * @brief Unlinks cargo from the mission, removes it from the player.
475  *
476  *    @param misn Mission to unlink cargo from.
477  *    @param cargo_id ID of cargo to unlink.
478  *    @return returns 0 on success.
479  */
mission_unlinkCargo(Mission * misn,unsigned int cargo_id)480 int mission_unlinkCargo( Mission* misn, unsigned int cargo_id )
481 {
482    int i;
483    for (i=0; i<misn->ncargo; i++)
484       if (misn->cargo[i] == cargo_id)
485          break;
486 
487    if (i>=misn->ncargo) { /* not found */
488       DEBUG("Mission '%s' attempting to unlink inexistant cargo %d.",
489             misn->title, cargo_id);
490       return 1;
491    }
492 
493    /* shrink cargo size - no need to realloc */
494    memmove( &misn->cargo[i], &misn->cargo[i+1],
495          sizeof(unsigned int) * (misn->ncargo-i-1) );
496    misn->ncargo--;
497 
498    return 0;
499 }
500 
501 
502 /**
503  * @brief Cleans up a mission.
504  *
505  *    @param misn Mission to clean up.
506  */
mission_cleanup(Mission * misn)507 void mission_cleanup( Mission* misn )
508 {
509    int i, ret;
510 
511    /* Hooks and missions. */
512    if (misn->id != 0) {
513       hook_rmMisnParent( misn->id ); /* remove existing hooks */
514       npc_rm_parentMission( misn ); /* remove existing npc */
515    }
516 
517    /* Cargo. */
518    if (misn->cargo != NULL) {
519       for (i=0; i<misn->ncargo; i++) { /* must unlink all the cargo */
520          if (player.p != NULL) { /* Only remove if player exists. */
521             ret = pilot_rmMissionCargo( player.p, misn->cargo[i], 0 );
522             if (ret)
523                WARN("Failed to remove mission cargo '%d' for mission '%s'.", misn->cargo[i], misn->title);
524          }
525       }
526       free(misn->cargo);
527    }
528    if (misn->osd > 0)
529       osd_destroy(misn->osd);
530    /*
531     * XXX With the way the mission code works, this function is called on a
532     * Mission struct of all zeros. Looking at the implementation, luaL_ref()
533     * never returns 0, but this is probably undefined behavior.
534     */
535    if (misn->env != LUA_NOREF && misn->env != 0)
536       nlua_freeEnv(misn->env);
537 
538    /* Data. */
539    if (misn->title != NULL)
540       free(misn->title);
541    if (misn->desc != NULL)
542       free(misn->desc);
543    if (misn->reward != NULL)
544       free(misn->reward);
545    if (misn->portrait != NULL)
546       gl_freeTexture(misn->portrait);
547    if (misn->npc != NULL)
548       free(misn->npc);
549 
550    /* Markers. */
551    if (misn->markers != NULL)
552       array_free( misn->markers );
553 
554    /* Claims. */
555    if (misn->claims != NULL)
556       claim_destroy( misn->claims );
557 
558    /* Clear the memory. */
559    memset( misn, 0, sizeof(Mission) );
560 }
561 
562 
563 /**
564  * @brief Puts the specified mission at the end of the player_missions array.
565  *
566  *    @param pos Mission's position within player_missions
567  */
mission_shift(int pos)568 void mission_shift( int pos )
569 {
570    Mission *misn;
571 
572    if (pos >= (MISSION_MAX-1))
573       return;
574 
575    /* Store specified mission. */
576    misn = player_missions[pos];
577 
578    /* Move other missions down. */
579    memmove( &player_missions[pos], &player_missions[pos+1],
580       sizeof(Mission*) * (MISSION_MAX - pos - 1) );
581 
582    /* Put the specified mission at the end of the array. */
583    player_missions[MISSION_MAX - 1] = misn;
584 }
585 
586 
587 /**
588  * @brief Frees MissionData.
589  *
590  *    @param mission MissionData to free.
591  */
mission_freeData(MissionData * mission)592 static void mission_freeData( MissionData* mission )
593 {
594    if (mission->name)
595       free(mission->name);
596    if (mission->lua)
597       free(mission->lua);
598    if (mission->avail.planet)
599       free(mission->avail.planet);
600    if (mission->avail.system)
601       free(mission->avail.system);
602    if (mission->avail.factions)
603       free(mission->avail.factions);
604    if (mission->avail.cond)
605       free(mission->avail.cond);
606    if (mission->avail.done)
607       free(mission->avail.done);
608 
609    /* Clear the memory. */
610 #ifdef DEBUGGING
611    memset( mission, 0, sizeof(MissionData) );
612 #endif /* DEBUGGING */
613 }
614 
615 
616 /**
617  * @brief Checks to see if a mission matches the faction requirements.
618  *
619  *    @param misn Mission to check.
620  *    @param faction Faction to check against.
621  *    @return 1 if it meets the faction requirement, 0 if it doesn't.
622  */
mission_matchFaction(MissionData * misn,int faction)623 static int mission_matchFaction( MissionData* misn, int faction )
624 {
625    int i;
626 
627    /* No faction always accepted. */
628    if (misn->avail.nfactions <= 0)
629       return 1;
630 
631    /* Check factions. */
632    for (i=0; i<misn->avail.nfactions; i++)
633       if (faction == misn->avail.factions[i])
634          return 1;
635 
636    return 0;
637 }
638 
639 
640 /**
641  * @brief Activates mission claims.
642  */
missions_activateClaims(void)643 void missions_activateClaims (void)
644 {
645    int i;
646 
647    for (i=0; i<MISSION_MAX; i++)
648       if (player_missions[i]->claims != NULL)
649          claim_activate( player_missions[i]->claims );
650 }
651 
652 
653 /**
654  * @brief Compares to missions to see which has more priority.
655  */
mission_compare(const void * arg1,const void * arg2)656 static int mission_compare( const void* arg1, const void* arg2 )
657 {
658    Mission *m1, *m2;
659 
660    /* Get arguments. */
661    m1 = (Mission*) arg1;
662    m2 = (Mission*) arg2;
663 
664    /* Check priority - lower is more important. */
665    if (m1->data->avail.priority < m2->data->avail.priority)
666       return +1;
667    else if (m1->data->avail.priority > m2->data->avail.priority)
668       return -1;
669 
670    /* Compare NPC. */
671    if ((m1->npc != NULL) && (m2->npc != NULL))
672       return strcmp( m1->npc, m2->npc );
673 
674    /* Compare title. */
675    if ((m1->title != NULL) && (m2->title != NULL))
676       return strcmp( m1->title, m2->title );
677 
678    /* Tied. */
679    return 0.;
680 }
681 
682 
683 /**
684  * @brief Generates a mission list. This runs create() so won't work with all
685  *        missions.
686  *
687  *    @param[out] n Missions created.
688  *    @param faction Faction of the planet.
689  *    @param planet Name of the planet.
690  *    @param sysname Name of the current system.
691  *    @param loc Location
692  *    @return The stack of Missions created with n members.
693  */
missions_genList(int * n,int faction,const char * planet,const char * sysname,int loc)694 Mission* missions_genList( int *n, int faction,
695       const char* planet, const char* sysname, int loc )
696 {
697    int i,j, m, alloced;
698    double chance;
699    int rep;
700    Mission* tmp;
701    MissionData* misn;
702 
703    /* Missions can't be generated by tutorial. */
704    if (player_isTut()) {
705       *n = 0;
706       return NULL;
707    }
708 
709    /* Find available missions. */
710    tmp      = NULL;
711    m        = 0;
712    alloced  = 0;
713    for (i=0; i<mission_nstack; i++) {
714       misn = &mission_stack[i];
715       if (misn->avail.loc == loc) {
716 
717          /* Must meet requirements. */
718          if (!mission_meetReq(i, faction, planet, sysname))
719             continue;
720 
721          /* Must hit chance. */
722          chance = (double)(misn->avail.chance % 100)/100.;
723          if (chance == 0.) /* We want to consider 100 -> 100% not 0% */
724             chance = 1.;
725          rep = MAX(1, misn->avail.chance / 100);
726 
727          for (j=0; j<rep; j++) /* random chance of rep appearances */
728             if (RNGF() < chance) {
729                m++;
730                /* Extra allocation. */
731                if (m > alloced) {
732                   if (alloced == 0)
733                      alloced = 32;
734                   else
735                      alloced *= 2;
736                   tmp      = realloc( tmp, sizeof(Mission) * alloced );
737                }
738                /* Initialize the mission. */
739                if (mission_init( &tmp[m-1], misn, 1, 1, NULL ))
740                   m--;
741             }
742       }
743    }
744 
745    /* Sort. */
746    if (tmp != NULL) {
747       qsort( tmp, m, sizeof(Mission), mission_compare );
748       (*n) = m;
749    }
750    else
751       (*n) = 0;
752 
753    return tmp;
754 }
755 
756 
757 /**
758  * @brief Gets location based on a human readable string.
759  *
760  *    @param loc String to get the location of.
761  *    @return Location matching loc.
762  */
mission_location(const char * loc)763 static int mission_location( const char* loc )
764 {
765    if (strcmp(loc,"None")==0) return MIS_AVAIL_NONE;
766    else if (strcmp(loc,"Computer")==0) return MIS_AVAIL_COMPUTER;
767    else if (strcmp(loc,"Bar")==0) return MIS_AVAIL_BAR;
768    else if (strcmp(loc,"Outfit")==0) return MIS_AVAIL_OUTFIT;
769    else if (strcmp(loc,"Shipyard")==0) return MIS_AVAIL_SHIPYARD;
770    else if (strcmp(loc,"Land")==0) return MIS_AVAIL_LAND;
771    else if (strcmp(loc,"Commodity")==0) return MIS_AVAIL_COMMODITY;
772    else if (strcmp(loc,"Space")==0) return MIS_AVAIL_SPACE;
773    return -1;
774 }
775 
776 
777 /**
778  * @brief Parses a node of a mission.
779  *
780  *    @param temp Data to load into.
781  *    @param parent Node containing the mission.
782  *    @return 0 on success.
783  */
mission_parse(MissionData * temp,const xmlNodePtr parent)784 static int mission_parse( MissionData* temp, const xmlNodePtr parent )
785 {
786    xmlNodePtr cur, node;
787 
788 #ifdef DEBUGGING
789    /* To check if mission is valid. */
790    int ret;
791    char *buf;
792    uint32_t len;
793 #endif /* DEBUGGING */
794 
795    /* Clear memory. */
796    memset( temp, 0, sizeof(MissionData) );
797 
798    /* Defaults. */
799    temp->avail.priority = 5;
800 
801    /* get the name */
802    temp->name = xml_nodeProp(parent,"name");
803    if (temp->name == NULL)
804       WARN("Mission in "MISSION_DATA_PATH" has invalid or no name");
805 
806    node = parent->xmlChildrenNode;
807 
808    char str[PATH_MAX] = "\0";
809 
810    do { /* load all the data */
811 
812       /* Only handle nodes. */
813       xml_onlyNodes(node);
814 
815       if (xml_isNode(node,"lua")) {
816          nsnprintf( str, PATH_MAX, MISSION_LUA_PATH"%s.lua", xml_get(node) );
817          temp->lua = strdup( str );
818          str[0] = '\0';
819 
820 #ifdef DEBUGGING
821          /* Check to see if syntax is valid. */
822          buf = ndata_read( temp->lua, &len );
823          ret = luaL_loadbuffer(naevL, buf, len, temp->name );
824          if (ret == LUA_ERRSYNTAX) {
825             WARN("Mission Lua '%s' of mission '%s' syntax error: %s",
826                   temp->name, temp->lua, lua_tostring(naevL,-1) );
827          } else {
828             lua_pop(naevL, 1);
829          }
830          free(buf);
831 #endif /* DEBUGGING */
832 
833          continue;
834       }
835       else if (xml_isNode(node,"flags")) { /* set the various flags */
836          cur = node->children;
837          do {
838             xml_onlyNodes(cur);
839             if (xml_isNode(cur,"unique")) {
840                mis_setFlag(temp,MISSION_UNIQUE);
841                continue;
842             }
843             WARN("Mission '%s' has unknown flag node '%s'.", temp->name, cur->name);
844          } while (xml_nextNode(cur));
845          continue;
846       }
847       else if (xml_isNode(node,"avail")) { /* mission availability */
848          cur = node->children;
849          do {
850             xml_onlyNodes(cur);
851             if (xml_isNode(cur,"location")) {
852                temp->avail.loc = mission_location( xml_get(cur) );
853                continue;
854             }
855             xmlr_int(cur,"chance",temp->avail.chance);
856             xmlr_strd(cur,"planet",temp->avail.planet);
857             xmlr_strd(cur,"system",temp->avail.system);
858             if (xml_isNode(cur,"faction")) {
859                temp->avail.factions = realloc( temp->avail.factions,
860                      sizeof(int) * ++temp->avail.nfactions );
861                temp->avail.factions[temp->avail.nfactions-1] =
862                      faction_get( xml_get(cur) );
863                continue;
864             }
865             xmlr_strd(cur,"cond",temp->avail.cond);
866             xmlr_strd(cur,"done",temp->avail.done);
867             xmlr_int(cur,"priority",temp->avail.priority);
868             WARN("Mission '%s' has unknown avail node '%s'.", temp->name, cur->name);
869          } while (xml_nextNode(cur));
870          continue;
871       }
872 
873       DEBUG("Unknown node '%s' in mission '%s'",node->name,temp->name);
874    } while (xml_nextNode(node));
875 
876 #define MELEMENT(o,s) \
877    if (o) WARN("Mission '%s' missing/invalid '"s"' element", temp->name)
878    MELEMENT(temp->lua==NULL,"lua");
879    MELEMENT(temp->avail.loc==-1,"location");
880    MELEMENT((temp->avail.loc!=MIS_AVAIL_NONE) && (temp->avail.chance==0),"chance");
881 #undef MELEMENT
882 
883    return 0;
884 }
885 
886 
887 /**
888  * @brief Loads all the mission data.
889  *
890  *    @return 0 on success.
891  */
missions_load(void)892 int missions_load (void)
893 {
894    int i, m;
895    uint32_t bufsize;
896    char *buf;
897 
898    for (i=0; i<MISSION_MAX; i++)
899       player_missions[i] = calloc(1, sizeof(Mission));
900 
901    buf = ndata_read( MISSION_DATA_PATH, &bufsize );
902 
903    xmlNodePtr node;
904    xmlDocPtr doc = xmlParseMemory( buf, bufsize );
905 
906    node = doc->xmlChildrenNode;
907    if (!xml_isNode(node,XML_MISSION_ID)) {
908       ERR("Malformed '"MISSION_DATA_PATH"' file: missing root element '"XML_MISSION_ID"'");
909       return -1;
910    }
911 
912    node = node->xmlChildrenNode; /* first mission node */
913    if (node == NULL) {
914       ERR("Malformed '"MISSION_DATA_PATH"' file: does not contain elements");
915       return -1;
916    }
917 
918    m = 0;
919    do {
920       if (xml_isNode(node,XML_MISSION_TAG)) {
921 
922          /* See if must grow. */
923          mission_nstack++;
924          if (mission_nstack > m) {
925             m += MISSION_CHUNK;
926             mission_stack = realloc(mission_stack, sizeof(MissionData)*m);
927          }
928 
929          /* Load it. */
930          mission_parse( &mission_stack[mission_nstack-1], node );
931       }
932    } while (xml_nextNode(node));
933 
934    /* Shrink to minimum. */
935    mission_stack = realloc(mission_stack, sizeof(MissionData)*mission_nstack);
936 
937    /* Clean up. */
938    xmlFreeDoc(doc);
939    free(buf);
940 
941    DEBUG("Loaded %d Mission%s", mission_nstack, (mission_nstack==1) ? "" : "s" );
942 
943    return 0;
944 }
945 
946 
947 /**
948  * @brief Frees all the mission data.
949  */
missions_free(void)950 void missions_free (void)
951 {
952    int i;
953 
954    /* Free all the player missions. */
955    missions_cleanup();
956 
957    /* Free the mission data. */
958    for (i=0; i<mission_nstack; i++)
959       mission_freeData( &mission_stack[i] );
960    free( mission_stack );
961    mission_stack = NULL;
962    mission_nstack = 0;
963 
964    /* Free the player mission stack. */
965    for (i=0; i<MISSION_MAX; i++)
966       free(player_missions[i]);
967 }
968 
969 
970 /**
971  * @brief Cleans up all the player's active missions.
972  */
missions_cleanup(void)973 void missions_cleanup (void)
974 {
975    int i;
976 
977    for (i=0; i<MISSION_MAX; i++)
978       mission_cleanup( player_missions[i] );
979 }
980 
981 
982 /**
983  * @brief Saves the player's active missions.
984  *
985  *    @param writer XML Write to use to save missions.
986  *    @return 0 on success.
987  */
missions_saveActive(xmlTextWriterPtr writer)988 int missions_saveActive( xmlTextWriterPtr writer )
989 {
990    int i,j,n;
991    int nitems;
992    char **items;
993 
994    xmlw_startElem(writer,"missions");
995 
996    for (i=0; i<MISSION_MAX; i++) {
997       if (player_missions[i]->id != 0) {
998          xmlw_startElem(writer,"mission");
999 
1000          /* data and id are attributes because they must be loaded first */
1001          xmlw_attr(writer,"data","%s",player_missions[i]->data->name);
1002          xmlw_attr(writer,"id","%u",player_missions[i]->id);
1003 
1004          xmlw_elem(writer,"title","%s",player_missions[i]->title);
1005          xmlw_elem(writer,"desc","%s",player_missions[i]->desc);
1006          xmlw_elem(writer,"reward","%s",player_missions[i]->reward);
1007 
1008          /* Markers. */
1009          xmlw_startElem( writer, "markers" );
1010          if (player_missions[i]->markers != NULL) {
1011             n = array_size( player_missions[i]->markers );
1012             for (j=0; j<n; j++) {
1013                xmlw_startElem(writer,"marker");
1014                xmlw_attr(writer,"id","%d",player_missions[i]->markers[j].id);
1015                xmlw_attr(writer,"type","%d",player_missions[i]->markers[j].type);
1016                xmlw_str(writer,"%s", system_getIndex(player_missions[i]->markers[j].sys)->name);
1017                xmlw_endElem(writer); /* "marker" */
1018             }
1019          }
1020          xmlw_endElem( writer ); /* "markers" */
1021 
1022          /* Cargo */
1023          xmlw_startElem(writer,"cargos");
1024          for (j=0; j<player_missions[i]->ncargo; j++)
1025             xmlw_elem(writer,"cargo","%u", player_missions[i]->cargo[j]);
1026          xmlw_endElem(writer); /* "cargos" */
1027 
1028          /* OSD. */
1029          if (player_missions[i]->osd > 0) {
1030             xmlw_startElem(writer,"osd");
1031 
1032             /* Save attributes. */
1033             items = osd_getItems(player_missions[i]->osd, &nitems);
1034             xmlw_attr(writer,"title","%s",osd_getTitle(player_missions[i]->osd));
1035             xmlw_attr(writer,"nitems","%d",nitems);
1036             xmlw_attr(writer,"active","%d",osd_getActive(player_missions[i]->osd));
1037 
1038             /* Save messages. */
1039             for (j=0; j<nitems; j++)
1040                xmlw_elem(writer,"msg","%s",items[j]);
1041 
1042             xmlw_endElem(writer); /* "osd" */
1043          }
1044 
1045          /* Claims. */
1046          xmlw_startElem(writer,"claims");
1047          claim_xmlSave( writer, player_missions[i]->claims );
1048          xmlw_endElem(writer); /* "claims" */
1049 
1050          /* Write Lua magic */
1051          xmlw_startElem(writer,"lua");
1052          nxml_persistLua( player_missions[i]->env, writer );
1053          xmlw_endElem(writer); /* "lua" */
1054 
1055          xmlw_endElem(writer); /* "mission" */
1056       }
1057    }
1058 
1059    xmlw_endElem(writer); /* "missions" */
1060 
1061    return 0;
1062 }
1063 
1064 
1065 /**
1066  * @brief Loads the player's active missions from a save.
1067  *
1068  *    @param parent Node containing the player's active missions.
1069  *    @return 0 on success.
1070  */
missions_loadActive(xmlNodePtr parent)1071 int missions_loadActive( xmlNodePtr parent )
1072 {
1073    xmlNodePtr node;
1074 
1075    /* cleanup old missions */
1076    missions_cleanup();
1077 
1078    node = parent->xmlChildrenNode;
1079    do {
1080       if (xml_isNode(node,"missions"))
1081          if (missions_parseActive( node ) < 0) return -1;
1082    } while (xml_nextNode(node));
1083 
1084    return 0;
1085 }
1086 
1087 
1088 /**
1089  * @brief Parses the actual individual mission nodes.
1090  *
1091  *    @param parent Parent node to parse.
1092  *    @return 0 on success.
1093  */
missions_parseActive(xmlNodePtr parent)1094 static int missions_parseActive( xmlNodePtr parent )
1095 {
1096    Mission *misn;
1097    MissionData *data;
1098    int m, i;
1099    char *buf;
1100    char *title;
1101    const char **items;
1102    int nitems;
1103    int id, sys, type;
1104    StarSystem *ssys;
1105 
1106    xmlNodePtr node, cur, nest;
1107 
1108    m = 0; /* start with mission 0 */
1109    node = parent->xmlChildrenNode;
1110    do {
1111       if (xml_isNode(node,"mission")) {
1112          misn = player_missions[m];
1113 
1114          /* process the attributes to create the mission */
1115          xmlr_attr(node,"data",buf);
1116          data = mission_get(mission_getID(buf));
1117          if (data == NULL) {
1118             WARN("Mission '%s' from savegame not found in game - ignoring.", buf);
1119             free(buf);
1120             continue;
1121          }
1122          else {
1123             if (mission_init( misn, data, 0, 0, NULL )) {
1124                WARN("Mission '%s' from savegame failed to load properly - ignoring.", buf);
1125                free(buf);
1126                continue;
1127             }
1128             misn->accepted = 1;
1129          }
1130          free(buf);
1131 
1132          /* this will orphan an identifier */
1133          xmlr_attr(node,"id",buf);
1134          misn->id = atol(buf);
1135          free(buf);
1136 
1137          cur = node->xmlChildrenNode;
1138          do {
1139 
1140             xmlr_strd(cur,"title",misn->title);
1141             xmlr_strd(cur,"desc",misn->desc);
1142             xmlr_strd(cur,"reward",misn->reward);
1143 
1144             /* Get the markers. */
1145             if (xml_isNode(cur,"markers")) {
1146                nest = cur->xmlChildrenNode;
1147                do {
1148                   if (xml_isNode(nest,"marker")) {
1149                      /* Get ID. */
1150                      xmlr_attr(nest,"id",buf);
1151                      id = (buf != NULL) ? atoi(buf) : -1;
1152                      if (buf != NULL)
1153                         free(buf);
1154                      /* Get type. */
1155                      xmlr_attr(nest,"type",buf);
1156                      type = (buf != NULL) ? atoi(buf) : -1;
1157                      if (buf != NULL)
1158                         free(buf);
1159                      /* Get system. */
1160                      ssys = system_get( xml_get( nest ));
1161                      if (ssys == NULL) {
1162                         WARN( "System Marker to '%s' does not exist", xml_get( nest ) );
1163                         continue;
1164                      }
1165                      sys = system_index( ssys );
1166                      mission_addMarker( misn, id, sys, type );
1167                   }
1168                } while (xml_nextNode(nest));
1169             }
1170 
1171             /* Cargo. */
1172             if (xml_isNode(cur,"cargos")) {
1173                nest = cur->xmlChildrenNode;
1174                do {
1175                   if (xml_isNode(nest,"cargo"))
1176                      mission_linkCargo( misn, xml_getLong(nest) );
1177                } while (xml_nextNode(nest));
1178             }
1179 
1180             /* OSD. */
1181             if (xml_isNode(cur,"osd")) {
1182                xmlr_attr(cur,"nitems",buf);
1183                if (buf != NULL) {
1184                   nitems = atoi(buf);
1185                   free(buf);
1186                }
1187                else
1188                   continue;
1189                xmlr_attr(cur,"title",title);
1190                items = malloc( nitems * sizeof(char*) );
1191                i = 0;
1192                nest = cur->xmlChildrenNode;
1193                do {
1194                   if (xml_isNode(nest,"msg")) {
1195                      if (i > nitems) {
1196                         WARN("Inconsistency with 'nitems' in savefile.");
1197                         break;
1198                      }
1199                      items[i] = xml_get(nest);
1200                      i++;
1201                   }
1202                } while (xml_nextNode(nest));
1203 
1204                /* Create the osd. */
1205                misn->osd = osd_create( title, nitems, items, data->avail.priority );
1206                free(items);
1207                free(title);
1208 
1209                /* Set active. */
1210                xmlr_attr(cur,"active",buf);
1211                if (buf != NULL) {
1212                   osd_active( misn->osd, atoi(buf) );
1213                   free(buf);
1214                }
1215             }
1216 
1217             /* Claims. */
1218             if (xml_isNode(cur,"claims"))
1219                misn->claims = claim_xmlLoad( cur );
1220 
1221             if (xml_isNode(cur,"lua"))
1222                /* start the unpersist routine */
1223                nxml_unpersistLua( misn->env, cur );
1224 
1225          } while (xml_nextNode(cur));
1226 
1227 
1228 
1229          m++; /* next mission */
1230          if (m >= MISSION_MAX) break; /* full of missions, must be an error */
1231       }
1232    } while (xml_nextNode(node));
1233 
1234    return 0;
1235 }
1236 
1237 
1238