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