1 /*
2  * See Licensing and Copyright notice in naev.h
3  */
4 
5 /**
6  * @file npc.c
7  *
8  * @brief Handles NPC stuff.
9  */
10 
11 
12 #include "npc.h"
13 
14 #include "naev.h"
15 
16 #include "nstring.h"
17 
18 #include "log.h"
19 #include "land.h"
20 #include "opengl.h"
21 #include "array.h"
22 #include "dialogue.h"
23 #include "event.h"
24 #include <lua.h>
25 
26 
27 /**
28  * @brief NPC types.
29  */
30 typedef enum NPCtype_ {
31    NPC_TYPE_NULL, /**< Inexistant NPC. */
32    NPC_TYPE_GIVER, /**< Mission giver NPC. */
33    NPC_TYPE_MISSION, /**< NPC generated by a mission. */
34    NPC_TYPE_EVENT /**< NPC generated by an event. */
35 } NPCtype;
36 
37 /**
38  * @brief Minimum needed NPC data for event.
39  */
40 typedef struct NPCevtData_ {
41    unsigned int id; /**< ID of the event. */
42    char *func; /**< Function to run. */
43 } NPCevtData;
44 /**
45  * @brief Minimum needed NPC data for mission.
46  */
47 typedef struct NPCmisnData_ {
48    Mission *misn; /**< Mission information. */
49    char *func; /**< Function to run. */
50 } NPCmisnData;
51 /**
52  * @brief The bar NPC.
53  */
54 typedef struct NPC_s {
55    unsigned int id; /**< ID of the NPC. */
56    NPCtype type; /**< Type of the NPC. */
57    int priority; /**< NPC priority, 5 is average, 0 is highest, 10 is lowest. */
58    char *name; /**< Name of the NPC. */
59    glTexture *portrait; /**< Portrait of the NPC. */
60    char *desc; /**< NPC description. */
61    union {
62       Mission g; /**< Mission information (for mission giver). */
63       NPCmisnData m; /**< Mission information (for mission generated NPC). */
64       NPCevtData e; /**< Event data (for event generated NPC). */
65    } u; /**< Type-specific data. */
66 } NPC_t;
67 
68 static unsigned int npc_array_idgen = 0; /**< ID generator. */
69 static NPC_t *npc_array  = NULL; /**< Missions at the spaceport bar. */
70 
71 
72 /*
73  * Prototypes.
74  */
75 static unsigned int npc_add( NPC_t *npc );
76 static unsigned int npc_add_giver( Mission *misn );
77 static int npc_rm( NPC_t *npc );
78 static NPC_t *npc_arrayGet( unsigned int id );
79 static void npc_free( NPC_t *npc );
80 
81 
82 /**
83  * @brief Adds an NPC to the spaceport bar.
84  */
npc_add(NPC_t * npc)85 static unsigned int npc_add( NPC_t *npc )
86 {
87    NPC_t *new_npc;
88 
89    /* Must be landed. */
90    if (!landed) {
91       npc_free( npc );
92       return 0;
93    }
94 
95    /* Create if needed. */
96    if (npc_array == NULL)
97       npc_array = array_create( NPC_t );
98 
99    /* Grow. */
100    new_npc = &array_grow( &npc_array );
101 
102    /* Copy over. */
103    *new_npc = *npc;
104 
105    /* Set ID. */
106    new_npc->id = ++npc_array_idgen;
107    return new_npc->id;
108 }
109 
110 
111 /**
112  * @brief Adds a mission giver NPC to the mission computer.
113  */
npc_add_giver(Mission * misn)114 static unsigned int npc_add_giver( Mission *misn )
115 {
116    NPC_t npc;
117 
118    /* Set up the data. */
119    npc.type       = NPC_TYPE_GIVER;
120    npc.name       = strdup(misn->npc);
121    npc.priority   = misn->data->avail.priority;
122    npc.portrait   = gl_dupTexture(misn->portrait);
123    npc.desc       = strdup(misn->desc);
124    npc.u.g        = *misn;
125 
126    return npc_add( &npc );
127 }
128 
129 
130 /**
131  * @brief Adds a mission NPC to the mission computer.
132  */
npc_add_mission(Mission * misn,const char * func,const char * name,int priority,const char * portrait,const char * desc)133 unsigned int npc_add_mission( Mission *misn, const char *func, const char *name,
134       int priority, const char *portrait, const char *desc )
135 {
136    NPC_t npc;
137 
138    /* The data. */
139    npc.type       = NPC_TYPE_MISSION;
140    npc.name       = strdup( name );
141    npc.priority   = priority;
142    npc.portrait   = gl_newImage( portrait, 0 );
143    npc.desc       = strdup( desc );
144    npc.u.m.misn   = misn;
145    npc.u.m.func   = strdup( func );
146 
147    return npc_add( &npc );
148 }
149 
150 
151 /**
152  * @brief Adds a event NPC to the mission computer.
153  */
npc_add_event(unsigned int evt,const char * func,const char * name,int priority,const char * portrait,const char * desc)154 unsigned int npc_add_event( unsigned int evt, const char *func, const char *name,
155       int priority, const char *portrait, const char *desc )
156 {
157    NPC_t npc;
158 
159    /* The data. */
160    npc.type       = NPC_TYPE_EVENT;
161    npc.name       = strdup( name );
162    npc.priority   = priority;
163    npc.portrait   = gl_newImage( portrait, 0 );
164    npc.desc       = strdup( desc );
165    npc.u.e.id     = evt;
166    npc.u.e.func   = strdup( func );
167 
168    return npc_add( &npc );
169 }
170 
171 
172 /**
173  * @brief Removes an npc from the spaceport bar.
174  */
npc_rm(NPC_t * npc)175 static int npc_rm( NPC_t *npc )
176 {
177    npc_free(npc);
178 
179    array_erase( &npc_array, &npc[0], &npc[1] );
180 
181    return 0;
182 }
183 
184 
185 /**
186  * @brief Gets an NPC by ID.
187  */
npc_arrayGet(unsigned int id)188 static NPC_t *npc_arrayGet( unsigned int id )
189 {
190    int i;
191    for (i=0; i<array_size( npc_array ); i++)
192       if (npc_array[i].id == id)
193          return &npc_array[i];
194    return NULL;
195 }
196 
197 
198 /**
199  * @brief removes an event NPC.
200  */
npc_rm_event(unsigned int id,unsigned int evt)201 int npc_rm_event( unsigned int id, unsigned int evt )
202 {
203    NPC_t *npc;
204 
205    /* Get the NPC. */
206    npc = npc_arrayGet( id );
207    if (npc == NULL)
208       return -1;
209 
210    /* Doesn't match type. */
211    if (npc->type != NPC_TYPE_EVENT)
212       return -1;
213 
214    /* Doesn't belong to the event.. */
215    if (npc->u.e.id != evt)
216       return -1;
217 
218    /* Remove the NPC. */
219    return npc_rm( npc );
220 }
221 
222 
223 /**
224  * @brief removes a mission NPC.
225  */
npc_rm_mission(unsigned int id,Mission * misn)226 int npc_rm_mission( unsigned int id, Mission *misn )
227 {
228    NPC_t *npc;
229 
230    /* Get the NPC. */
231    npc = npc_arrayGet( id );
232    if (npc == NULL)
233       return -1;
234 
235    /* Doesn't match type. */
236    if (npc->type != NPC_TYPE_MISSION)
237       return -1;
238 
239    /* Doesn't belong to the mission. */
240    if (misn->id != npc->u.m.misn->id)
241       return -1;
242 
243    /* Remove the NPC. */
244    return npc_rm( npc );
245 }
246 
247 
248 /**
249  * @brief Removes all the npc belonging to an event.
250  */
npc_rm_parentEvent(unsigned int id)251 int npc_rm_parentEvent( unsigned int id )
252 {
253    int i, n;
254    NPC_t *npc;
255 
256    if (npc_array == NULL)
257       return 0;
258 
259    n = 0;
260    for (i=0; i<array_size(npc_array); i++) {
261       npc = &npc_array[i];
262       if (npc->type != NPC_TYPE_EVENT)
263          continue;
264       if (npc->u.e.id != id )
265          continue;
266 
267       /* Invalidates iterators. */
268       npc_rm( npc );
269       i--;
270       n++;
271    }
272 
273    return n;
274 }
275 
276 
277 /**
278  * @brief Removes all the npc belonging to a mission.
279  */
npc_rm_parentMission(Mission * misn)280 int npc_rm_parentMission( Mission *misn )
281 {
282    int i, n;
283    NPC_t *npc;
284 
285    if (npc_array == NULL)
286       return 0;
287 
288    n = 0;
289    for (i=0; i<array_size(npc_array); i++) {
290       npc = &npc_array[i];
291       if (npc->type != NPC_TYPE_MISSION)
292          continue;
293       if (npc->u.m.misn->id != misn->id )
294          continue;
295 
296       /* Invalidates iterators. */
297       npc_rm( npc );
298       i--;
299       n++;
300    }
301 
302    return n;
303 }
304 
305 
306 /**
307  * @brief NPC compare function.
308  */
npc_compare(const void * arg1,const void * arg2)309 static int npc_compare( const void *arg1, const void *arg2 )
310 {
311    const NPC_t *npc1, *npc2;
312    int ret;
313 
314    npc1 = (NPC_t*)arg1;
315    npc2 = (NPC_t*)arg2;
316 
317    /* Compare priority. */
318    if (npc1->priority > npc2->priority)
319       return +1;
320    else if (npc1->priority < npc2->priority)
321       return -1;
322 
323    /* Compare name. */
324    ret = strcmp( npc1->name, npc2->name );
325    if (ret != 0)
326       return ret;
327 
328    /* Compare ID. */
329    if (npc1->id > npc2->id)
330       return +1;
331    else if (npc1->id < npc2->id)
332       return -1;
333    return 0;
334 }
335 
336 
337 /**
338  * @brief Sorts the NPCs.
339  */
npc_sort(void)340 void npc_sort (void)
341 {
342    if (npc_array != NULL)
343       qsort( npc_array, array_size(npc_array), sizeof(NPC_t), npc_compare );
344 }
345 
346 
347 /**
348  * @brief Generates the bar missions.
349  */
npc_generate(void)350 void npc_generate (void)
351 {
352    int i;
353    Mission *missions;
354    int nmissions;
355 
356    /* Get the missions. */
357    missions = missions_genList( &nmissions,
358          land_planet->faction, land_planet->name, cur_system->name,
359          MIS_AVAIL_BAR );
360 
361    /* Add to the bar NPC stack - may be not empty. */
362    for (i=0; i<nmissions; i++)
363       npc_add_giver( &missions[i] );
364 
365    /* Clean up. */
366    if (missions != NULL)
367       free( missions );
368 
369    /* Sort NPC. */
370    npc_sort();
371 }
372 
373 
374 /**
375  * @brief Patches a new mission bar npc into the bar system.
376  *
377  * @note Do not reuse the pointer once it's fed.
378  *
379  *    @param misn Mission to patch in.
380  */
npc_patchMission(Mission * misn)381 void npc_patchMission( Mission *misn )
382 {
383    /* Add mission giver. */
384    npc_add_giver( misn );
385 
386    /* Sort NPC. */
387    npc_sort();
388 }
389 
390 
391 /**
392  * @brief Frees a single npc.
393  */
npc_free(NPC_t * npc)394 static void npc_free( NPC_t *npc )
395 {
396    /* Common free stuff. */
397    free(npc->name);
398    gl_freeTexture(npc->portrait);
399    free(npc->desc);
400 
401    /* Type-specific free stuff. */
402    switch (npc->type) {
403       case NPC_TYPE_GIVER:
404          mission_cleanup(&npc->u.g);
405          break;
406 
407       case NPC_TYPE_MISSION:
408          free(npc->u.m.func);
409          break;
410 
411       case NPC_TYPE_EVENT:
412          free(npc->u.e.func);
413          break;
414 
415       default:
416          WARN("Freeing NPC of invalid type.");
417          return;
418    }
419 }
420 
421 
422 /**
423  * @brief Cleans up the spaceport bar NPC.
424  */
npc_clear(void)425 void npc_clear (void)
426 {
427    int i;
428 
429    if (npc_array == NULL)
430       return;
431 
432    /* First pass to clear the data. */
433    for (i=0; i<array_size( npc_array ); i++)
434       npc_free( &npc_array[i] );
435 
436    /* Resize down. */
437    array_resize( &npc_array, 0 );
438 }
439 
440 
441 /**
442  * @brief Frees the NPC stuff.
443  */
npc_freeAll(void)444 void npc_freeAll (void)
445 {
446    /* Clear the NPC. */
447    npc_clear();
448 
449    /* Free the array. */
450    if (npc_array != NULL)
451       array_free( npc_array );
452    npc_array = NULL;
453 }
454 
455 
456 /**
457  * @brief Get the size of the npc array.
458  */
npc_getArraySize(void)459 int npc_getArraySize (void)
460 {
461    if (npc_array == 0)
462       return 0;
463 
464    return array_size( npc_array );
465 }
466 
467 
468 /**
469  * @brief Get the npc array names for an image array.
470  *
471  *    @param names Name array to fill.
472  *    @param n Number to fill with.
473  */
npc_getNameArray(char ** names,int n)474 int npc_getNameArray( char **names, int n )
475 {
476    int i;
477 
478    if (npc_array == 0)
479       return 0;
480 
481    /* Create the array. */
482    for (i=0; i<MIN(n,array_size(npc_array)); i++)
483       names[i] = strdup( npc_array[i].name );
484 
485    return i;
486 }
487 
488 
489 /**
490  * @brief Get the npc array textures for an image array.
491  *
492  *    @param tex Texture array to fill.
493  *    @param n Number to fill with.
494  */
npc_getTextureArray(glTexture ** tex,int n)495 int npc_getTextureArray( glTexture **tex, int n )
496 {
497    int i;
498 
499    if (npc_array == 0)
500       return 0;
501 
502    /* Create the array. */
503    for (i=0; i<MIN(n,array_size(npc_array)); i++)
504       tex[i] = npc_array[i].portrait;
505 
506    return i;
507 }
508 
509 
510 /**
511  * @brief Get the name of an NPC.
512  */
npc_getName(int i)513 const char *npc_getName( int i )
514 {
515    /* Make sure in bounds. */
516    if ((i<0) || (i>=array_size(npc_array)))
517       return NULL;
518 
519    return npc_array[i].name;
520 }
521 
522 
523 /**
524  * @brief Get the texture of an NPC.
525  */
npc_getTexture(int i)526 glTexture *npc_getTexture( int i )
527 {
528    /* Make sure in bounds. */
529    if ((i<0) || (i>=array_size(npc_array)))
530       return NULL;
531 
532    return npc_array[i].portrait;
533 }
534 
535 
536 /**
537  * @brief Gets the NPC description.
538  */
npc_getDesc(int i)539 const char *npc_getDesc( int i )
540 {
541    /* Make sure in bounds. */
542    if ((i<0) || (i>=array_size(npc_array)))
543       return NULL;
544 
545    return npc_array[i].desc;
546 }
547 
548 
549 /**
550  * @brief Approaches a mission giver guy.
551  *
552  *    @brief Returns 1 on destroyed, 0 on not destroyed.
553  */
npc_approach_giver(NPC_t * npc)554 static int npc_approach_giver( NPC_t *npc )
555 {
556    int i;
557    int ret;
558    Mission *misn;
559 
560    /* Make sure player can accept the mission. */
561    for (i=0; i<MISSION_MAX; i++)
562       if (player_missions[i]->data == NULL)
563          break;
564    if (i >= MISSION_MAX) {
565       dialogue_alert("You have too many active missions.");
566       return -1;
567    }
568 
569    /* Get mission. */
570    misn = &npc->u.g;
571    ret  = mission_accept( misn );
572    if ((ret==0) || (ret==2) || (ret==-1)) { /* success in accepting the mission */
573       if (ret==-1)
574          mission_cleanup( misn );
575       npc_free( npc );
576       array_erase( &npc_array, &npc[0], &npc[1] );
577       ret = 1;
578    }
579    else
580       ret  = 0;
581 
582    return ret;
583 }
584 
585 
586 /**
587  * @brief Approaches the NPC.
588  *
589  *    @param i Index of the NPC to approach.
590  */
npc_approach(int i)591 int npc_approach( int i )
592 {
593    NPC_t *npc;
594 
595    /* Make sure in bounds. */
596    if ((i<0) || (i>=array_size(npc_array)))
597       return -1;
598 
599    /* Comfortability. */
600    npc = &npc_array[i];
601 
602    /* Handle type. */
603    switch (npc->type) {
604       case NPC_TYPE_GIVER:
605          return npc_approach_giver( npc );
606 
607       case NPC_TYPE_MISSION:
608          misn_runStart( npc->u.m.misn, npc->u.m.func );
609          lua_pushnumber( naevL, npc->id );
610          misn_runFunc( npc->u.m.misn, npc->u.m.func, 1 );
611          break;
612 
613       case NPC_TYPE_EVENT:
614          event_runStart( npc->u.e.id, npc->u.e.func );
615          lua_pushnumber( naevL, npc->id );
616          event_runFunc( npc->u.e.id, npc->u.e.func, 1 );
617          break;
618 
619       default:
620          WARN("Unknown NPC type!");
621          return -1;
622    }
623 
624    return 0;
625 }
626 
627