1 /*
2  * See Licensing and Copyright notice in naev.h
3  */
4 
5 /**
6  * @file faction.c
7  *
8  * @brief Handles the Naev factions.
9  */
10 
11 
12 #include "faction.h"
13 
14 #include "naev.h"
15 
16 #include <stdlib.h>
17 #include "nstring.h"
18 
19 #include "nxml.h"
20 
21 #include "nlua.h"
22 #include "nluadef.h"
23 #include "opengl.h"
24 #include "log.h"
25 #include "ndata.h"
26 #include "rng.h"
27 #include "colour.h"
28 #include "hook.h"
29 #include "space.h"
30 
31 
32 #define XML_FACTION_ID     "Factions"   /**< XML section identifier */
33 #define XML_FACTION_TAG    "faction" /**< XML tag identifier. */
34 
35 
36 #define CHUNK_SIZE         32 /**< Size of chunk for allocation. */
37 
38 #define FACTION_STATIC        (1<<0) /**< Faction doesn't change standing with player. */
39 #define FACTION_INVISIBLE     (1<<1) /**< Faction isn't exposed to the player. */
40 #define FACTION_KNOWN         (1<<2) /**< Faction is known to the player. */
41 
42 #define faction_setFlag(fa,f) ((fa)->flags |= (f))
43 #define faction_rmFlag(fa,f)  ((fa)->flags &= ~(f))
44 #define faction_isFlag(fa,f)  ((fa)->flags & (f))
45 #define faction_isKnown_(fa)   ((fa)->flags & (FACTION_KNOWN))
46 
47 /**
48  * @struct Faction
49  *
50  * @brief Represents a faction.
51  */
52 typedef struct Faction_ {
53    char *name; /**< Normal Name. */
54    char *longname; /**< Long Name. */
55    char *displayname; /**< Display name. */
56 
57    /* Graphics. */
58    glTexture *logo_small; /**< Small logo. */
59    glTexture *logo_tiny; /**< Tiny logo. */
60    const glColour *colour; /**< Faction specific colour. */
61 
62    /* Enemies */
63    int *enemies; /**< Enemies by ID of the faction. */
64    int nenemies; /**< Number of enemies. */
65 
66    /* Allies */
67    int *allies; /**< Allies by ID of the faction. */
68    int nallies; /**< Number of allies. */
69 
70    /* Player information. */
71    double player_def; /**< Default player standing. */
72    double player; /**< Standing with player - from -100 to 100 */
73 
74    /* Scheduler. */
75    nlua_env sched_env; /**< Lua scheduler script. */
76 
77    /* Behaviour. */
78    nlua_env env; /**< Faction specific environment. */
79 
80    /* Equipping. */
81    nlua_env equip_env; /**< Faction equipper enviornment. */
82 
83    /* Flags. */
84    unsigned int flags; /**< Flags affecting the faction. */
85 } Faction;
86 
87 static Faction* faction_stack = NULL; /**< Faction stack. */
88 int faction_nstack = 0; /**< Number of factions in the faction stack. */
89 
90 
91 /*
92  * Prototypes
93  */
94 /* static */
95 static void faction_sanitizePlayer( Faction* faction );
96 static void faction_modPlayerLua( int f, double mod, const char *source, int secondary );
97 static int faction_parse( Faction* temp, xmlNodePtr parent );
98 static void faction_parseSocial( xmlNodePtr parent );
99 /* externed */
100 int pfaction_save( xmlTextWriterPtr writer );
101 int pfaction_load( xmlNodePtr parent );
102 
103 
104 /**
105  * @brief Gets a faction ID by name.
106  *
107  *    @param name Name of the faction to seek.
108  *    @return ID of the faction.
109  */
faction_get(const char * name)110 int faction_get( const char* name )
111 {
112    int i;
113 
114    /* Escorts are part of the "player" faction. */
115    if (strcmp(name, "Escort") == 0)
116       return FACTION_PLAYER;
117 
118    if (name != NULL) {
119       for (i=0; i<faction_nstack; i++)
120          if (strcmp(faction_stack[i].name, name)==0)
121             break;
122 
123       if (i != faction_nstack)
124          return i;
125    }
126 
127    WARN("Faction '%s' not found in stack.", name);
128    return -1;
129 }
130 
131 
132 /**
133  * @brief Gets all the factions.
134  */
faction_getAll(int * n)135 int* faction_getAll( int *n )
136 {
137    int i;
138    int *f;
139    int m;
140 
141    /* Set up. */
142    f  = malloc( sizeof(int) * faction_nstack );
143 
144    /* Get IDs. */
145    m = 0;
146    for (i=0; i<faction_nstack; i++)
147       if (!faction_isFlag( &faction_stack[i], FACTION_INVISIBLE ))
148          f[m++] = i;
149 
150    *n = m;
151    return f;
152 }
153 
154 /**
155  * @brief Gets all the known factions.
156  */
faction_getKnown(int * n)157 int* faction_getKnown( int *n )
158 {
159    int i;
160    int *f;
161    int m;
162 
163    /* Set up. */
164    f  = malloc( sizeof(int) * faction_nstack );
165 
166    /* Get IDs. */
167    m = 0;
168    for (i=0; i<faction_nstack; i++)
169       if (!faction_isFlag( &faction_stack[i], FACTION_INVISIBLE ) && faction_isKnown_( &faction_stack[i] ))
170          f[m++] = i;
171 
172    *n = m;
173    return f;
174 }
175 
176 /**
177  * @brief Clears the known factions.
178  */
faction_clearKnown()179 void faction_clearKnown()
180 {
181    int i;
182 
183    for ( i=0; i<faction_nstack; i++)
184       if ( faction_isKnown_( &faction_stack[i] ))
185          faction_rmFlag( &faction_stack[i], FACTION_KNOWN );
186 }
187 
188 /**
189  * @brief Is the faction invisible?
190  */
faction_isInvisible(int id)191 int faction_isInvisible( int id )
192 {
193    return faction_isFlag( &faction_stack[id], FACTION_INVISIBLE );
194 }
195 
196 /**
197  * @brief Sets the faction's invisible state
198  */
faction_setInvisible(int id,int state)199 int faction_setInvisible( int id, int state )
200 {
201    if (!faction_isFaction(id)) {
202       WARN("Faction id '%d' is invalid.",id);
203       return -1;
204    }
205    if (state)
206       faction_setFlag( &faction_stack[id], FACTION_INVISIBLE );
207    else
208       faction_rmFlag( &faction_stack[id], FACTION_INVISIBLE );
209 
210    return 0;
211 }
212 
213 /**
214  * @brief Is the faction known?
215  */
faction_isKnown(int id)216 int faction_isKnown( int id )
217 {
218    return faction_isKnown_( &faction_stack[id] );
219 }
220 
221 /**
222  * @brief Sets the factions known state
223  */
faction_setKnown(int id,int state)224 int faction_setKnown( int id, int state )
225 {
226    if (state)
227       faction_setFlag( &faction_stack[id], FACTION_KNOWN );
228    else
229       faction_rmFlag( &faction_stack[id], FACTION_KNOWN );
230 
231    return 0;
232 }
233 
234 /**
235  * @brief Gets a factions "real" name.
236  *
237  *    @param f Faction to get the name of.
238  *    @return Name of the faction.
239  */
faction_name(int f)240 char* faction_name( int f )
241 {
242    if (!faction_isFaction(f)) {
243       WARN("Faction id '%d' is invalid.",f);
244       return NULL;
245    }
246    /* Don't want player to see their escorts as "Player" faction. */
247    if (f == FACTION_PLAYER)
248       return "Escort";
249 
250    return faction_stack[f].name;
251 }
252 
253 
254 /**
255  * @brief Gets a factions short name.
256  *
257  *    @param f Faction to get the name of.
258  *    @return Name of the faction.
259  */
faction_shortname(int f)260 char* faction_shortname( int f )
261 {
262    if (!faction_isFaction(f)) {
263       WARN("Faction id '%d' is invalid.",f);
264       return NULL;
265    }
266    /* Don't want player to see their escorts as "Player" faction. */
267    if (f == FACTION_PLAYER)
268       return "Escort";
269 
270    /* Possibly get display name. */
271    if (faction_stack[f].displayname != NULL)
272       return faction_stack[f].displayname;
273 
274    return faction_stack[f].name;
275 }
276 
277 
278 /**
279  * @brief Gets the faction's long name (formal).
280  *
281  *    @param f Faction to get the name of.
282  *    @return The faction's long name.
283  */
faction_longname(int f)284 char* faction_longname( int f )
285 {
286    if (!faction_isFaction(f)) {
287       WARN("Faction id '%d' is invalid.",f);
288       return NULL;
289    }
290    if (faction_stack[f].longname != NULL)
291       return faction_stack[f].longname;
292    return faction_stack[f].name;
293 }
294 
295 
296 /**
297  * @brief Gets the faction's small logo (64x64 or smaller).
298  *
299  *    @param f Faction to get the logo of.
300  *    @return The faction's small logo image.
301  */
faction_logoSmall(int f)302 glTexture* faction_logoSmall( int f )
303 {
304    if (!faction_isFaction(f)) {
305       WARN("Faction id '%d' is invalid.",f);
306       return NULL;
307    }
308 
309    return faction_stack[f].logo_small;
310 }
311 
312 
313 /**
314  * @brief Gets the faction's tiny logo (24x24 or smaller).
315  *
316  *    @param f Faction to get the logo of.
317  *    @return The faction's tiny logo image.
318  */
faction_logoTiny(int f)319 glTexture* faction_logoTiny( int f )
320 {
321    if (!faction_isFaction(f)) {
322       WARN("Faction id '%d' is invalid.",f);
323       return NULL;
324    }
325 
326    return faction_stack[f].logo_tiny;
327 }
328 
329 
330 /**
331  * @brief Gets the colour of the faction
332  *
333  *    @param f Faction to get the colour of.
334  *    @return The faction's colour
335  */
faction_colour(int f)336 const glColour* faction_colour( int f )
337 {
338    if (!faction_isFaction(f)) {
339       WARN("Faction id '%d' is invalid.",f);
340       return NULL;
341    }
342 
343    return faction_stack[f].colour;
344 }
345 
346 
347 /**
348  * @brief Gets the list of enemies of a faction.
349  *
350  *    @param f Faction to get enemies of.
351  *    @param[out] Number of enemies.
352  *    @return The enemies of the faction.
353  */
faction_getEnemies(int f,int * n)354 int* faction_getEnemies( int f, int *n )
355 {
356    int i, nenemies;
357    int *enemies;
358 
359    if (!faction_isFaction(f)) {
360       WARN("Faction id '%d' is invalid.",f);
361       return NULL;
362    }
363 
364    /* Player's faction ratings can change, so regenerate each call. */
365    if (f == FACTION_PLAYER) {
366       nenemies = 0;
367       enemies = malloc(sizeof(int)*faction_nstack);
368 
369       for (i=0; i<faction_nstack; i++)
370          if (faction_isPlayerEnemy(i))
371             enemies[nenemies++] = i;
372 
373       enemies = realloc(enemies, sizeof(int)*nenemies);
374 
375       free(faction_stack[f].enemies);
376       faction_stack[f].enemies = enemies;
377       faction_stack[f].nenemies = nenemies;
378    }
379 
380    *n = faction_stack[f].nenemies;
381    return faction_stack[f].enemies;
382 }
383 
384 
385 /**
386  * @brief Gets the list of allies of a faction.
387  *
388  *    @param f Faction to get allies of.
389  *    @param[out] Number of allies.
390  *    @return The allies of the faction.
391  */
faction_getAllies(int f,int * n)392 int* faction_getAllies( int f, int *n )
393 {
394    int i, nallies;
395    int *allies;
396 
397    if (!faction_isFaction(f)) {
398       WARN("Faction id '%d' is invalid.",f);
399       return NULL;
400    }
401 
402    /* Player's faction ratings can change, so regenerate each call. */
403    if (f == FACTION_PLAYER) {
404       nallies = 0;
405       allies = malloc(sizeof(int)*faction_nstack);
406 
407       for (i=0; i<faction_nstack; i++)
408          if (faction_isPlayerFriend(i))
409             allies[nallies++] = i;
410 
411       allies = realloc(allies, sizeof(int)*nallies);
412 
413       free(faction_stack[f].allies);
414       faction_stack[f].allies = allies;
415       faction_stack[f].nallies = nallies;
416    }
417 
418    *n = faction_stack[f].nallies;
419    return faction_stack[f].allies;
420 }
421 
422 
423 /**
424  * @brief Adds an enemy to the faction's enemies list.
425  *
426  *    @param f The faction to add an enemy to.
427  *    @param o The other faction to make an enemy.
428  */
faction_addEnemy(int f,int o)429 void faction_addEnemy( int f, int o)
430 {
431    Faction *ff;
432    int i;
433 
434    if (f==o) return;
435 
436    if (faction_isFaction(f))
437       ff = &faction_stack[f];
438    else { /* f is invalid */
439       WARN("addEnemy: %d is an invalid faction", f);
440       return;
441    }
442 
443    if (!faction_isFaction(o)) { /* o is invalid */
444       WARN("addEnemy: %d is an invalid faction", o);
445       return;
446    }
447 
448    /* player cannot be made an enemy this way */
449    if (f==FACTION_PLAYER) {
450       WARN("addEnemy: %d is the player faction", f);
451       return;
452    }
453    if (o==FACTION_PLAYER) {
454       WARN("addEnemy: %d is the player faction", o);
455       return;
456    }
457 
458    for (i=0;i<ff->nenemies;i++) {
459       if (ff->enemies[i] == o)
460          return;
461    }
462 
463    ff->nenemies++;
464    ff->enemies = realloc(ff->enemies, sizeof(int)*ff->nenemies);
465    ff->enemies[ff->nenemies-1] = o;
466 }
467 
468 
469 /**
470  * @brief Removes an enemy from the faction's enemies list.
471  *
472  *    @param f The faction to remove an enemy from.
473  *    @param o The other faction to remove as an enemy.
474  */
faction_rmEnemy(int f,int o)475 void faction_rmEnemy( int f, int o)
476 {
477    Faction *ff;
478    int i;
479 
480    if (f==o) return;
481 
482    if (faction_isFaction(f))
483       ff = &faction_stack[f];
484    else { /* f is invalid */
485       WARN("rmEnemy: %d is an invalid faction", f);
486       return;
487    }
488 
489    for (i=0;i<ff->nenemies;i++) {
490       if (ff->enemies[i] == o) {
491          ff->enemies[i] = ff->enemies[ff->nenemies-1];
492          ff->nenemies--;
493          ff->enemies = realloc(ff->enemies, sizeof(int)*ff->nenemies);
494          return;
495       }
496    }
497 }
498 
499 
500 /**
501  * @brief Adds an ally to the faction's allies list.
502  *
503  *    @param f The faction to add an ally to.
504  *    @param o The other faction to make an ally.
505  */
faction_addAlly(int f,int o)506 void faction_addAlly( int f, int o)
507 {
508    Faction *ff;
509    int i;
510 
511    if (f==o) return;
512 
513    if (faction_isFaction(f))
514       ff = &faction_stack[f];
515    else { /* f is invalid */
516       WARN("addAlly: %d is an invalid faction", f);
517       return;
518    }
519 
520    if (!faction_isFaction(o)) { /* o is invalid */
521       WARN("addAlly: %d is an invalid faction", o);
522       return;
523    }
524 
525    /* player cannot be made an ally this way */
526    if (f==FACTION_PLAYER) {
527       WARN("addAlly: %d is the player faction", f);
528       return;
529    }
530    if (o==FACTION_PLAYER) {
531       WARN("addAlly: %d is the player faction", o);
532       return;
533    }
534 
535    for (i=0;i<ff->nallies;i++) {
536       if (ff->allies[i] == o)
537          return;
538    }
539 
540    ff->nallies++;
541    ff->allies = realloc(ff->allies, sizeof(int)*ff->nallies);
542    ff->allies[ff->nallies-1] = o;
543 }
544 
545 
546 /**
547  * @brief Removes an ally from the faction's allies list.
548  *
549  *    @param f The faction to remove an ally from.
550  *    @param o The other faction to remove as an ally.
551  */
faction_rmAlly(int f,int o)552 void faction_rmAlly( int f, int o)
553 {
554    Faction *ff;
555    int i;
556 
557    if (f==o) return;
558 
559    if (faction_isFaction(f))
560       ff = &faction_stack[f];
561    else { /* f is invalid */
562       WARN("rmAlly: %d is an invalid faction", f);
563       return;
564    }
565 
566    for (i=0;i<ff->nallies;i++) {
567       if (ff->allies[i] == o) {
568          ff->allies[i] = ff->allies[ff->nallies-1];
569          ff->nallies--;
570          ff->allies = realloc(ff->allies, sizeof(int)*ff->nallies);
571          return;
572       }
573    }
574 }
575 
576 
577 /**
578  * @brief Gets the state associated to the faction scheduler.
579  */
faction_getScheduler(int f)580 nlua_env faction_getScheduler( int f )
581 {
582    if (!faction_isFaction(f)) {
583       WARN("Faction id '%d' is invalid.",f);
584       return LUA_NOREF;
585    }
586 
587    return faction_stack[f].sched_env;
588 }
589 
590 
591 /**
592  * @brief Gets the equipper state associated to the faction scheduler.
593  */
faction_getEquipper(int f)594 nlua_env faction_getEquipper( int f )
595 {
596    if (!faction_isFaction(f)) {
597       WARN("Faction id '%d' is invalid.",f);
598       return LUA_NOREF;
599    }
600 
601    return faction_stack[f].equip_env;
602 }
603 
604 
605 /**
606  * @brief Sanitizes player faction standing.
607  *
608  *    @param faction Faction to sanitize.
609  */
faction_sanitizePlayer(Faction * faction)610 static void faction_sanitizePlayer( Faction* faction )
611 {
612    if (faction->player > 100.)
613       faction->player = 100.;
614    else if (faction->player < -100.)
615       faction->player = -100.;
616 }
617 
618 
619 /**
620  * @brief Mods player using the power of Lua.
621  */
faction_modPlayerLua(int f,double mod,const char * source,int secondary)622 static void faction_modPlayerLua( int f, double mod, const char *source, int secondary )
623 {
624    Faction *faction;
625    double old, delta;
626    HookParam hparam[3];
627 
628    faction = &faction_stack[f];
629 
630    /* Make sure it's not static. */
631    if (faction_isFlag(faction, FACTION_STATIC))
632       return;
633 
634    old   = faction->player;
635 
636    if (faction->env == LUA_NOREF)
637       faction->player += mod;
638    else {
639 
640       /* Set up the function:
641        * faction_hit( current, amount, source, secondary ) */
642       nlua_getenv( faction->env, "faction_hit" );
643       lua_pushnumber(  naevL, faction->player );
644       lua_pushnumber(  naevL, mod );
645       lua_pushstring(  naevL, source );
646       lua_pushboolean( naevL, secondary );
647 
648       /* Call function. */
649       if (nlua_pcall( faction->env, 4, 1 )) { /* An error occurred. */
650          WARN("Faction '%s': %s", faction->name, lua_tostring(naevL,-1));
651          lua_pop( naevL, 1 );
652          return;
653       }
654 
655       /* Parse return. */
656       if (!lua_isnumber( naevL, -1 ))
657          WARN( "Lua script for faction '%s' did not return a number from 'faction_hit(...)'.", faction->name );
658       else
659          faction->player = lua_tonumber( naevL, -1 );
660       lua_pop( naevL, 1 );
661    }
662 
663    /* Sanitize just in case. */
664    faction_sanitizePlayer( faction );
665 
666    /* Run hook if necessary. */
667    delta = faction->player - old;
668    if (fabs(delta) > 1e-10) {
669       hparam[0].type    = HOOK_PARAM_FACTION;
670       hparam[0].u.lf    = f;
671       hparam[1].type    = HOOK_PARAM_NUMBER;
672       hparam[1].u.num   = delta;
673       hparam[2].type    = HOOK_PARAM_SENTINEL;
674       hooks_runParam( "standing", hparam );
675 
676       /* Tell space the faction changed. */
677       space_factionChange();
678    }
679 }
680 
681 
682 /**
683  * @brief Modifies the player's standing with a faction.
684  *
685  * Affects enemies and allies too.
686  *
687  *    @param f Faction to modify player's standing.
688  *    @param mod Modifier to modify by.
689  */
faction_modPlayer(int f,double mod,const char * source)690 void faction_modPlayer( int f, double mod, const char *source )
691 {
692    int i;
693    Faction *faction;
694 
695    if (!faction_isFaction(f)) {
696       WARN("%d is an invalid faction", f);
697       return;
698    }
699    faction = &faction_stack[f];
700 
701    /* Modify faction standing with parent faction. */
702    faction_modPlayerLua( f, mod, source, 0 );
703 
704    /* Now mod allies to a lesser degree */
705    for (i=0; i<faction->nallies; i++)
706       /* Modify faction standing */
707       faction_modPlayerLua( faction->allies[i], mod, source, 1 );
708 
709    /* Now mod enemies */
710    for (i=0; i<faction->nenemies; i++)
711       /* Modify faction standing. */
712       faction_modPlayerLua( faction->enemies[i], -mod, source, 1 );
713 }
714 
715 /**
716  * @brief Modifies the player's standing without affecting others.
717  *
718  * Does not affect allies nor enemies.
719  *
720  *    @param f Faction whose standing to modify.
721  *    @param mod Amount to modify standing by.
722  *
723  * @sa faction_modPlayer
724  */
faction_modPlayerSingle(int f,double mod,const char * source)725 void faction_modPlayerSingle( int f, double mod, const char *source )
726 {
727    if (!faction_isFaction(f)) {
728       WARN("%d is an invalid faction", f);
729       return;
730    }
731 
732    faction_modPlayerLua( f, mod, source, 0 );
733 }
734 
735 
736 /**
737  * @brief Modifies the player's standing without affecting others.
738  *
739  * Does not affect allies nor enemies and does not run through the Lua script.
740  *
741  *    @param f Faction whose standing to modify.
742  *    @param mod Amount to modify standing by.
743  *
744  * @sa faction_modPlayer
745  */
faction_modPlayerRaw(int f,double mod)746 void faction_modPlayerRaw( int f, double mod )
747 {
748    Faction *faction;
749    HookParam hparam[3];
750 
751    if (!faction_isFaction(f)) {
752       WARN("%d is an invalid faction", f);
753       return;
754    }
755 
756    faction = &faction_stack[f];
757    faction->player += mod;
758    /* Run hook if necessary. */
759    hparam[0].type    = HOOK_PARAM_FACTION;
760    hparam[0].u.lf    = f;
761    hparam[1].type    = HOOK_PARAM_NUMBER;
762    hparam[1].u.num   = mod;
763    hparam[2].type    = HOOK_PARAM_SENTINEL;
764    hooks_runParam( "standing", hparam );
765 
766    /* Sanitize just in case. */
767    faction_sanitizePlayer( faction );
768 
769    /* Tell space the faction changed. */
770    space_factionChange();
771 }
772 
773 
774 /**
775  * @brief Sets the player's standing with a faction.
776  *
777  *    @param f Faction to set the player's standing for.
778  *    @param value Value to set the player's standing to.
779  */
faction_setPlayer(int f,double value)780 void faction_setPlayer( int f, double value )
781 {
782    Faction *faction;
783    HookParam hparam[3];
784    double mod;
785 
786    if (!faction_isFaction(f)) {
787       WARN("%d is an invalid faction", f);
788       return;
789    }
790 
791    faction = &faction_stack[f];
792    mod = value - faction->player;
793    faction->player = value;
794    /* Run hook if necessary. */
795    hparam[0].type    = HOOK_PARAM_FACTION;
796    hparam[0].u.lf    = f;
797    hparam[1].type    = HOOK_PARAM_NUMBER;
798    hparam[1].u.num   = mod;
799    hparam[2].type    = HOOK_PARAM_SENTINEL;
800    hooks_runParam( "standing", hparam );
801 
802    /* Sanitize just in case. */
803    faction_sanitizePlayer( faction );
804 
805    /* Tell space the faction changed. */
806    space_factionChange();
807 }
808 
809 
810 /**
811  * @brief Gets the player's standing with a faction.
812  *
813  *    @param f Faction to get player's standing from.
814  *    @return The standing the player has with the faction.
815  */
faction_getPlayer(int f)816 double faction_getPlayer( int f )
817 {
818    if (faction_isFaction(f))
819       return faction_stack[f].player;
820    else {
821       WARN("%d is an invalid faction", f);
822       return -1000;
823    }
824 }
825 
826 
827 /**
828  * @brief Gets the player's default standing with a faction.
829  *
830  *    @param f Faction to get player's default standing from.
831  *    @return The default standing the player has with the faction.
832  */
faction_getPlayerDef(int f)833 double faction_getPlayerDef( int f )
834 {
835    if (faction_isFaction(f))
836       return faction_stack[f].player_def;
837    else {
838       WARN("%d is an invalid faction", f);
839       return -1000;
840    }
841 }
842 
843 
844 /**
845  * @brief Gets whether or not the player is a friend of the faction.
846  *
847  *    @param f Faction to check friendliness of.
848  *    @return 1 if the player is a friend, 0 otherwise.
849  */
faction_isPlayerFriend(int f)850 int faction_isPlayerFriend( int f )
851 {
852    Faction *faction;
853    int r;
854 
855    faction = &faction_stack[f];
856 
857    if ( faction->env == LUA_NOREF )
858       return 0;
859    else
860    {
861 
862       /* Set up the function:
863        * faction_player_friend( standing ) */
864       nlua_getenv( faction->env, "faction_player_friend" );
865       lua_pushnumber( naevL, faction->player );
866 
867       /* Call function. */
868       if ( nlua_pcall( faction->env, 1, 1 ) )
869       {
870          /* An error occurred. */
871          WARN( "Faction '%s': %s", faction->name, lua_tostring( naevL, -1 ) );
872          lua_pop( naevL, 1 );
873          return 0;
874       }
875 
876       /* Parse return. */
877       if ( !lua_isboolean( naevL, -1 ) )
878       {
879          WARN( "Lua script for faction '%s' did not return a boolean from 'faction_player_friend(...)'.", faction->name );
880          r = 0;
881       }
882       else
883          r = lua_toboolean( naevL, -1 );
884       lua_pop( naevL, 1 );
885 
886       return r;
887    }
888 }
889 
890 
891 /**
892  * @brief Gets whether or not the player is an enemy of the faction.
893  *
894  *    @param f Faction to check hostility of.
895  *    @return 1 if the player is an enemy, 0 otherwise.
896  */
faction_isPlayerEnemy(int f)897 int faction_isPlayerEnemy( int f )
898 {
899    Faction *faction;
900    int r;
901 
902    faction = &faction_stack[f];
903 
904    if ( faction->env == LUA_NOREF )
905       return 0;
906    else
907    {
908 
909       /* Set up the function:
910        * faction_player_enemy( standing ) */
911       nlua_getenv( faction->env, "faction_player_enemy" );
912       lua_pushnumber( naevL, faction->player );
913 
914       /* Call function. */
915       if ( nlua_pcall( faction->env, 1, 1 ) )
916       {
917          /* An error occurred. */
918          WARN( "Faction '%s': %s", faction->name, lua_tostring( naevL, -1 ) );
919          lua_pop( naevL, 1 );
920          return 0;
921       }
922 
923       /* Parse return. */
924       if ( !lua_isboolean( naevL, -1 ) )
925       {
926          WARN( "Lua script for faction '%s' did not return a boolean from 'faction_player_enemy(...)'.", faction->name );
927          r = 0;
928       }
929       else
930          r = lua_toboolean( naevL, -1 );
931       lua_pop( naevL, 1 );
932 
933       return r;
934    }
935 }
936 
937 
938 /**
939  * @brief Gets the colour of the faction based on it's standing with the player.
940  *
941  * Used to unify the colour checks all over.
942  *
943  *    @param f Faction to get the colour of based on player's standing.
944  *    @return Pointer to the colour.
945  */
faction_getColour(int f)946 const glColour* faction_getColour( int f )
947 {
948    if (f<0) return &cInert;
949    else if (areAllies(FACTION_PLAYER,f)) return &cFriend;
950    else if (areEnemies(FACTION_PLAYER,f)) return &cHostile;
951    else return &cNeutral;
952 }
953 
954 
955 /**
956  * @brief Gets the faction character associated to it's standing with the player.
957  *
958  * Use this to do something like "\e%c", faction_getColourChar( some_faction ) in the
959  *  font print routines.
960  *
961  *    @param f Faction to get the colour of based on player's standing.
962  *    @return The character associated to the faction.
963  */
faction_getColourChar(int f)964 char faction_getColourChar( int f )
965 {
966    if (f<0) return 'I';
967    else if (areEnemies(FACTION_PLAYER,f)) return 'H';
968    else if (areAllies(FACTION_PLAYER,f)) return 'F';
969    else return 'N';
970 }
971 
972 
973 /**
974  * @brief Gets the player's standing in human readable form.
975  *
976  *    @param f Faction to get standing of.
977  *    @return Human readable player's standing.
978  */
faction_getStandingText(int f)979 const char *faction_getStandingText( int f )
980 {
981    Faction *faction;
982    const char *r;
983 
984    faction = &faction_stack[f];
985 
986    if ( faction->env == LUA_NOREF )
987       return "???";
988    else
989    {
990 
991       /* Set up the function:
992        * faction_standing_text( standing ) */
993       nlua_getenv( faction->env, "faction_standing_text" );
994       lua_pushnumber( naevL, faction->player );
995 
996       /* Call function. */
997       if ( nlua_pcall( faction->env, 1, 1 ) )
998       {
999          /* An error occurred. */
1000          WARN( "Faction '%s': %s", faction->name, lua_tostring( naevL, -1 ) );
1001          lua_pop( naevL, 1 );
1002          return "???";
1003       }
1004 
1005       /* Parse return. */
1006       if ( !lua_isstring( naevL, -1 ) )
1007       {
1008          WARN( "Lua script for faction '%s' did not return a string from 'faction_standing_text(...)'.", faction->name );
1009          r = "???";
1010       }
1011       else
1012          r = lua_tostring( naevL, -1 );
1013       lua_pop( naevL, 1 );
1014 
1015       return r;
1016    }
1017 }
1018 
1019 
1020 /**
1021  * @brief Gets the broad faction standing.
1022  *
1023  *    @param f Faction to get broad standing of.
1024  *    @param bribed Whether or not the respective pilot is bribed.
1025  *    @param override If positive sets to ally, if negative sets to hostile.
1026  *    @return Human readable broad player's standing.
1027  */
faction_getStandingBroad(int f,int bribed,int override)1028 const char *faction_getStandingBroad( int f, int bribed, int override )
1029 {
1030    Faction *faction;
1031    const char *r;
1032 
1033    faction = &faction_stack[f];
1034 
1035    if ( faction->env == LUA_NOREF )
1036       return "???";
1037    else
1038    {
1039       /* Set up the function:
1040        * faction_standing_broad( standing, bribed, override ) */
1041       nlua_getenv( faction->env, "faction_standing_broad" );
1042       lua_pushnumber( naevL, faction->player );
1043       lua_pushboolean( naevL, bribed );
1044       lua_pushnumber( naevL, override );
1045 
1046       /* Call function. */
1047       if ( nlua_pcall( faction->env, 3, 1 ) )
1048       {
1049          /* An error occurred. */
1050          WARN( "Faction '%s': %s", faction->name, lua_tostring( naevL, -1 ) );
1051          lua_pop( naevL, 1 );
1052          return "???";
1053       }
1054 
1055       /* Parse return. */
1056       if ( !lua_isstring( naevL, -1 ) )
1057       {
1058          WARN( "Lua script for faction '%s' did not return a string from 'faction_standing_broad(...)'.", faction->name );
1059          r = "???";
1060       }
1061       else
1062          r = lua_tostring( naevL, -1 );
1063       lua_pop( naevL, 1 );
1064 
1065       return r;
1066    }
1067 }
1068 
1069 
1070 /**
1071  * @brief Checks whether two factions are enemies.
1072  *
1073  *    @param a Faction A.
1074  *    @param b Faction B.
1075  *    @return 1 if A and B are enemies, 0 otherwise.
1076  */
areEnemies(int a,int b)1077 int areEnemies( int a, int b)
1078 {
1079    Faction *fa, *fb;
1080    int i;
1081 
1082    if (a==b) return 0; /* luckily our factions aren't masochistic */
1083 
1084    /* handle a */
1085    if (faction_isFaction(a))
1086       fa = &faction_stack[a];
1087    else { /* a is invalid */
1088       WARN("areEnemies: %d is an invalid faction", a);
1089       return 0;
1090    }
1091 
1092    /* handle b */
1093    if (faction_isFaction(b))
1094       fb = &faction_stack[b];
1095    else { /* b is invalid */
1096       WARN("areEnemies: %d is an invalid faction", b);
1097       return 0;
1098    }
1099 
1100    /* player handled separately */
1101    if (a==FACTION_PLAYER) {
1102       return faction_isPlayerEnemy(b);
1103    }
1104    else if (b==FACTION_PLAYER) {
1105       return faction_isPlayerEnemy(a);
1106    }
1107 
1108    for (i=0;i<fa->nenemies;i++)
1109       if (fa->enemies[i] == b)
1110          return 1;
1111    for (i=0;i<fb->nenemies;i++)
1112       if(fb->enemies[i] == a)
1113          return 1;
1114 
1115    return 0;
1116 }
1117 
1118 
1119 /**
1120  * @brief Checks whether two factions are allies or not.
1121  *
1122  *    @param a Faction A.
1123  *    @param b Faction B.
1124  *    @return 1 if A and B are allies, 0 otherwise.
1125  */
areAllies(int a,int b)1126 int areAllies( int a, int b )
1127 {
1128    Faction *fa, *fb;
1129    int i;
1130 
1131    /* If they are the same they must be allies. */
1132    if (a==b) return 1;
1133 
1134    /* handle a */
1135    if (faction_isFaction(a))
1136       fa = &faction_stack[a];
1137    else { /* a is invalid */
1138       WARN("%d is an invalid faction", a);
1139       return 0;
1140    }
1141 
1142    /* handle b */
1143    if (faction_isFaction(b))
1144       fb = &faction_stack[b];
1145    else { /* b is invalid */
1146       WARN("%d is an invalid faction", b);
1147       return 0;
1148    }
1149 
1150    /* we assume player becomes allies with high rating */
1151    if (a==FACTION_PLAYER) {
1152       return faction_isPlayerFriend(b);
1153    }
1154    else if (b==FACTION_PLAYER) {
1155       return faction_isPlayerFriend(a);
1156    }
1157 
1158    for (i=0;i<fa->nallies;i++)
1159       if (fa->allies[i] == b)
1160          return 1;
1161    for (i=0;i<fb->nallies;i++)
1162       if(fb->allies[i] == a)
1163          return 1;
1164 
1165    return 0;
1166 }
1167 
1168 
1169 /**
1170  * @brief Checks whether or not a faction is valid.
1171  *
1172  *    @param f Faction to check for validity.
1173  *    @return 1 if faction is valid, 0 otherwise.
1174  */
faction_isFaction(int f)1175 int faction_isFaction( int f )
1176 {
1177    if ((f<0) || (f>=faction_nstack))
1178       return 0;
1179    return 1;
1180 }
1181 
1182 
1183 /**
1184  * @brief Parses a single faction, but doesn't set the allies/enemies bit.
1185  *
1186  *    @param temp Faction to load data into.
1187  *    @param parent Parent node to extract faction from.
1188  *    @return Faction created from parent node.
1189  */
faction_parse(Faction * temp,xmlNodePtr parent)1190 static int faction_parse( Faction* temp, xmlNodePtr parent )
1191 {
1192    xmlNodePtr node;
1193    int player;
1194    char buf[PATH_MAX], *dat, *ctmp;
1195    glColour *col;
1196    uint32_t ndat;
1197 
1198    /* Clear memory. */
1199    memset( temp, 0, sizeof(Faction) );
1200    temp->equip_env = LUA_NOREF;
1201    temp->env = LUA_NOREF;
1202    temp->sched_env = LUA_NOREF;
1203 
1204    temp->name = xml_nodeProp(parent,"name");
1205    if (temp->name == NULL)
1206       WARN("Faction from "FACTION_DATA_PATH" has invalid or no name");
1207 
1208    player = 0;
1209    node = parent->xmlChildrenNode;
1210    do {
1211 
1212       /* Only care about nodes. */
1213       xml_onlyNodes(node);
1214 
1215       /* Can be 0 or negative, so we have to take that into account. */
1216       if (xml_isNode(node,"player")) {
1217          temp->player_def = xml_getFloat(node);
1218          player = 1;
1219          continue;
1220       }
1221 
1222       xmlr_strd(node,"longname",temp->longname);
1223       xmlr_strd(node,"display",temp->displayname);
1224       if (xml_isNode(node, "colour")) {
1225          ctmp = xml_get(node);
1226          if (ctmp != NULL)
1227             temp->colour = col_fromName(xml_raw(node));
1228          /* If no named colour is present, RGB attributes are used. */
1229          else {
1230             /* Initialize in case a colour channel is absent. */
1231             col = calloc( 1, sizeof(glColour) );
1232 
1233             xmlr_attr(node,"r",ctmp);
1234             if (ctmp != NULL) {
1235                col->r = atof(ctmp);
1236                free(ctmp);
1237             }
1238 
1239             xmlr_attr(node,"g",ctmp);
1240             if (ctmp != NULL) {
1241                col->g = atof(ctmp);
1242                free(ctmp);
1243             }
1244 
1245             xmlr_attr(node,"b",ctmp);
1246             if (ctmp != NULL) {
1247                col->b = atof(ctmp);
1248                free(ctmp);
1249             }
1250 
1251             col->a = 1.;
1252             temp->colour = col;
1253          }
1254          continue;
1255       }
1256 
1257       if (xml_isNode(node, "spawn")) {
1258          if (temp->sched_env != LUA_NOREF)
1259             WARN("Faction '%s' has duplicate 'spawn' tag.", temp->name);
1260          nsnprintf( buf, sizeof(buf), "dat/factions/spawn/%s.lua", xml_raw(node) );
1261          temp->sched_env = nlua_newEnv(1);
1262          nlua_loadStandard( temp->sched_env);
1263          dat = ndata_read( buf, &ndat );
1264          if (nlua_dobufenv(temp->sched_env, dat, ndat, buf) != 0) {
1265             WARN("Failed to run spawn script: %s\n"
1266                   "%s\n"
1267                   "Most likely Lua file has improper syntax, please check",
1268                   buf, lua_tostring(naevL,-1));
1269             nlua_freeEnv( temp->sched_env );
1270             temp->sched_env = LUA_NOREF;
1271          }
1272          free(dat);
1273          continue;
1274       }
1275 
1276       if (xml_isNode(node, "standing")) {
1277          if (temp->env != LUA_NOREF)
1278             WARN("Faction '%s' has duplicate 'standing' tag.", temp->name);
1279          nsnprintf( buf, sizeof(buf), "dat/factions/standing/%s.lua", xml_raw(node) );
1280          temp->env = nlua_newEnv(1);
1281          nlua_loadStandard( temp->env );
1282          dat = ndata_read( buf, &ndat );
1283          if (nlua_dobufenv(temp->env, dat, ndat, buf) != 0) {
1284             WARN("Failed to run standing script: %s\n"
1285                   "%s\n"
1286                   "Most likely Lua file has improper syntax, please check",
1287                   buf, lua_tostring(naevL,-1));
1288             nlua_freeEnv( temp->env );
1289             temp->env = LUA_NOREF;
1290          }
1291          free(dat);
1292          continue;
1293       }
1294 
1295       if (xml_isNode(node, "known")) {
1296          faction_setFlag(temp, FACTION_KNOWN);
1297          continue;
1298       }
1299 
1300       if (xml_isNode(node, "equip")) {
1301          if (temp->equip_env != LUA_NOREF)
1302             WARN("Faction '%s' has duplicate 'equip' tag.", temp->name);
1303          nsnprintf( buf, sizeof(buf), "dat/factions/equip/%s.lua", xml_raw(node) );
1304          temp->equip_env = nlua_newEnv(1);
1305          nlua_loadStandard( temp->equip_env );
1306          dat = ndata_read( buf, &ndat );
1307          if (nlua_dobufenv(temp->equip_env, dat, ndat, buf) != 0) {
1308             WARN("Failed to run equip script: %s\n"
1309                   "%s\n"
1310                   "Most likely Lua file has improper syntax, please check",
1311                   buf, lua_tostring(naevL, -1));
1312             nlua_freeEnv( temp->equip_env );
1313             temp->equip_env = LUA_NOREF;
1314          }
1315          free(dat);
1316          continue;
1317       }
1318 
1319       if (xml_isNode(node,"logo")) {
1320          if (temp->logo_small != NULL)
1321             WARN("Faction '%s' has duplicate 'logo' tag.", temp->name);
1322          nsnprintf( buf, PATH_MAX, FACTION_LOGO_PATH"%s_small.png", xml_get(node));
1323          temp->logo_small = gl_newImage(buf, 0);
1324          nsnprintf( buf, PATH_MAX, FACTION_LOGO_PATH"%s_tiny.png", xml_get(node));
1325          temp->logo_tiny = gl_newImage(buf, 0);
1326          continue;
1327       }
1328 
1329       if (xml_isNode(node,"static")) {
1330          faction_setFlag(temp, FACTION_STATIC);
1331          continue;
1332       }
1333 
1334       if (xml_isNode(node,"invisible")) {
1335          faction_setFlag(temp, FACTION_INVISIBLE);
1336          continue;
1337       }
1338 
1339       /* Avoid warnings. */
1340       if (xml_isNode(node,"allies") || xml_isNode(node,"enemies"))
1341          continue;
1342 
1343       DEBUG("Unknown node '%s' in faction '%s'",node->name,temp->name);
1344    } while (xml_nextNode(node));
1345 
1346    if (player==0)
1347       DEBUG("Faction '%s' missing player tag.", temp->name);
1348    if ((temp->env==LUA_NOREF) && !faction_isFlag( temp, FACTION_STATIC ))
1349       WARN("Faction '%s' has no Lua and isn't static!", temp->name);
1350 
1351    return 0;
1352 }
1353 
1354 
1355 /**
1356  * @brief Parses the social tidbits of a faction: allies and enemies.
1357  *
1358  *    @param parent Node containing the faction.
1359  */
faction_parseSocial(xmlNodePtr parent)1360 static void faction_parseSocial( xmlNodePtr parent )
1361 {
1362    xmlNodePtr node, cur;
1363    char *buf;
1364    Faction *base;
1365    int mod;
1366    int mem;
1367 
1368    buf = xml_nodeProp(parent,"name");
1369    base = &faction_stack[faction_get(buf)];
1370    free(buf);
1371 
1372    node = parent->xmlChildrenNode;
1373    do {
1374 
1375       /* Grab the allies */
1376       if (xml_isNode(node,"allies")) {
1377          cur = node->xmlChildrenNode;
1378 
1379          mem = 0;
1380          do {
1381             if (xml_isNode(cur,"ally")) {
1382                mod = faction_get(xml_get(cur));
1383                base->nallies++;
1384                if (base->nallies > mem) {
1385                   mem += CHUNK_SIZE;
1386                   base->allies = realloc(base->allies, sizeof(int)*mem);
1387                }
1388                base->allies[base->nallies-1] = mod;
1389             }
1390          } while (xml_nextNode(cur));
1391          if (base->nallies > 0)
1392             base->allies = realloc(base->allies, sizeof(int)*base->nallies);
1393       }
1394 
1395       /* Grab the enemies */
1396       if (xml_isNode(node,"enemies")) {
1397          cur = node->xmlChildrenNode;
1398 
1399          mem = 0;
1400          do {
1401             if (xml_isNode(cur,"enemy")) {
1402                mod = faction_get(xml_get(cur));
1403                base->nenemies++;
1404                if (base->nenemies > mem) {
1405                   mem += CHUNK_SIZE;
1406                   base->enemies = realloc(base->enemies, sizeof(int)*mem);
1407                }
1408                base->enemies[base->nenemies-1] = mod;
1409             }
1410          } while (xml_nextNode(cur));
1411          if (base->nenemies > 0)
1412             base->enemies = realloc(base->enemies, sizeof(int)*base->nenemies);
1413       }
1414    } while (xml_nextNode(node));
1415 }
1416 
1417 
1418 /**
1419  * @brief Resets the player's standing with the factions to default.
1420  */
factions_reset(void)1421 void factions_reset (void)
1422 {
1423    int i;
1424    for (i=0; i<faction_nstack; i++)
1425       faction_stack[i].player = faction_stack[i].player_def;
1426 }
1427 
1428 
1429 /**
1430  * @brief Loads up all the factions from the data file.
1431  *
1432  *    @return 0 on success.
1433  */
factions_load(void)1434 int factions_load (void)
1435 {
1436    int mem;
1437    uint32_t bufsize;
1438    char *buf = ndata_read( FACTION_DATA_PATH, &bufsize);
1439 
1440    xmlNodePtr factions, node;
1441    xmlDocPtr doc = xmlParseMemory( buf, bufsize );
1442 
1443    node = doc->xmlChildrenNode; /* Factions node */
1444    if (!xml_isNode(node,XML_FACTION_ID)) {
1445       ERR("Malformed "FACTION_DATA_PATH" file: missing root element '"XML_FACTION_ID"'");
1446       return -1;
1447    }
1448 
1449    factions = node->xmlChildrenNode; /* first faction node */
1450    if (factions == NULL) {
1451       ERR("Malformed "FACTION_DATA_PATH" file: does not contain elements");
1452       return -1;
1453    }
1454 
1455    /* player faction is hard-coded */
1456    faction_stack = calloc( 1, sizeof(Faction) );
1457    faction_stack[0].name = strdup("Player");
1458    faction_stack[0].flags = FACTION_STATIC | FACTION_INVISIBLE;
1459    faction_stack[0].equip_env = LUA_NOREF;
1460    faction_stack[0].env = LUA_NOREF;
1461    faction_stack[0].sched_env = LUA_NOREF;
1462    faction_nstack++;
1463 
1464    /* First pass - gets factions */
1465    node = factions;
1466    mem = 0;
1467    do {
1468       if (xml_isNode(node,XML_FACTION_TAG)) {
1469          /* See if must grow memory.  */
1470          faction_nstack++;
1471          if (faction_nstack > mem) {
1472             mem += CHUNK_SIZE;
1473             faction_stack = realloc(faction_stack, sizeof(Faction)*mem);
1474          }
1475 
1476          /* Load faction. */
1477          faction_parse(&faction_stack[faction_nstack-1], node);
1478       }
1479    } while (xml_nextNode(node));
1480 
1481    /* Shrink to minimum size. */
1482    faction_stack = realloc(faction_stack, sizeof(Faction)*faction_nstack);
1483 
1484    /* Second pass - sets allies and enemies */
1485    node = factions;
1486    do {
1487       if (xml_isNode(node,XML_FACTION_TAG))
1488          faction_parseSocial(node);
1489    } while (xml_nextNode(node));
1490 
1491 #ifdef DEBUGGING
1492    int i, j, k, r;
1493    Faction *f, *sf;
1494 
1495    /* Third pass, makes sure allies/enemies are sane. */
1496    for (i=0; i<faction_nstack; i++) {
1497       f = &faction_stack[i];
1498 
1499       /* First run over allies and make sure it's mutual. */
1500       for (j=0; j < f->nallies; j++) {
1501          sf = &faction_stack[ f->allies[j] ];
1502 
1503          r = 0;
1504          for (k=0; k < sf->nallies; k++)
1505             if (sf->allies[k] == i)
1506                r = 1;
1507 
1508          if (r == 0)
1509             WARN("Faction: %s and %s aren't completely mutual allies!",
1510                   f->name, sf->name );
1511       }
1512 
1513       /* Now run over enemies. */
1514       for (j=0; j < f->nenemies; j++) {
1515          sf = &faction_stack[ f->enemies[j] ];
1516 
1517          r = 0;
1518          for (k=0; k < sf->nenemies; k++)
1519             if (sf->enemies[k] == i)
1520                r = 1;
1521 
1522          if (r == 0)
1523             WARN("Faction: %s and %s aren't completely mutual enemies!",
1524                   f->name, sf->name );
1525       }
1526    }
1527 #endif /* DEBUGGING */
1528 
1529    xmlFreeDoc(doc);
1530    free(buf);
1531 
1532    DEBUG("Loaded %d Faction%s", faction_nstack, (faction_nstack==1) ? "" : "s" );
1533 
1534    return 0;
1535 }
1536 
1537 
1538 /**
1539  * @brief Frees the factions.
1540  */
factions_free(void)1541 void factions_free (void)
1542 {
1543    int i;
1544 
1545    /* free factions */
1546    for (i=0; i<faction_nstack; i++) {
1547       free(faction_stack[i].name);
1548       if (faction_stack[i].longname != NULL)
1549          free(faction_stack[i].longname);
1550       if (faction_stack[i].displayname != NULL)
1551          free(faction_stack[i].displayname);
1552       if (faction_stack[i].logo_small != NULL)
1553          gl_freeTexture(faction_stack[i].logo_small);
1554       if (faction_stack[i].logo_tiny != NULL)
1555          gl_freeTexture(faction_stack[i].logo_tiny);
1556       if (faction_stack[i].nallies > 0)
1557          free(faction_stack[i].allies);
1558       if (faction_stack[i].nenemies > 0)
1559          free(faction_stack[i].enemies);
1560       if (faction_stack[i].sched_env != LUA_NOREF)
1561          nlua_freeEnv( faction_stack[i].sched_env );
1562       if (faction_stack[i].env != LUA_NOREF)
1563          nlua_freeEnv( faction_stack[i].env );
1564       if (faction_stack[i].equip_env != LUA_NOREF)
1565          nlua_freeEnv( faction_stack[i].equip_env );
1566    }
1567    free(faction_stack);
1568    faction_stack = NULL;
1569    faction_nstack = 0;
1570 }
1571 
1572 
1573 /**
1574  * @brief Saves player's standings with the factions.
1575  *
1576  *    @param writer The xml writer to use.
1577  *    @return 0 on success.
1578  */
pfaction_save(xmlTextWriterPtr writer)1579 int pfaction_save( xmlTextWriterPtr writer )
1580 {
1581    int i;
1582 
1583    xmlw_startElem(writer,"factions");
1584 
1585    for (i=1; i<faction_nstack; i++) { /* player is faction 0 */
1586       /* Must not be static. */
1587       if (faction_isFlag( &faction_stack[i], FACTION_STATIC ))
1588          continue;
1589 
1590       xmlw_startElem(writer,"faction");
1591 
1592       xmlw_attr(writer,"name","%s",faction_stack[i].name);
1593       xmlw_elem(writer, "standing", "%f", faction_stack[i].player);
1594 
1595       if (faction_isKnown_(&faction_stack[i]))
1596          xmlw_elemEmpty(writer, "known");
1597 
1598       xmlw_endElem(writer); /* "faction" */
1599    }
1600 
1601    xmlw_endElem(writer); /* "factions" */
1602 
1603    return 0;
1604 }
1605 
1606 
1607 /**
1608  * @brief Loads the player's faction standings.
1609  *
1610  *    @param parent Parent xml node to read from.
1611  *    @return 0 on success.
1612  */
pfaction_load(xmlNodePtr parent)1613 int pfaction_load( xmlNodePtr parent )
1614 {
1615    xmlNodePtr node, cur, sub;
1616    char *str;
1617    int faction;
1618 
1619    node = parent->xmlChildrenNode;
1620 
1621    do {
1622       if (xml_isNode(node,"factions")) {
1623          cur = node->xmlChildrenNode;
1624          do {
1625             if (xml_isNode(cur,"faction")) {
1626                xmlr_attr(cur, "name", str);
1627                faction = faction_get(str);
1628 
1629                if (faction != -1) { /* Faction is valid. */
1630 
1631                   sub = cur->xmlChildrenNode;
1632                   do {
1633                      if (xml_isNode(sub,"standing")) {
1634 
1635                         /* Must not be static. */
1636                         if (!faction_isFlag( &faction_stack[faction], FACTION_STATIC ))
1637                            faction_stack[faction].player = xml_getFloat(sub);
1638                         continue;
1639                      }
1640                      if (xml_isNode(sub,"known")) {
1641                         faction_setFlag(&faction_stack[faction], FACTION_KNOWN);
1642                         continue;
1643                      }
1644                   } while (xml_nextNode(sub));
1645                }
1646                free(str);
1647             }
1648          } while (xml_nextNode(cur));
1649       }
1650    } while (xml_nextNode(node));
1651 
1652    return 0;
1653 }
1654 
1655 
1656 /**
1657  * @brief Returns an array of faction ids.
1658  *
1659  *    @param *n Writes the number of elements.
1660  *    @param which Which factions to get. (0,1,2,3 : all, friendly, neutral, hostile)
1661  *    @return A pointer to an array, or NULL.
1662  */
faction_getGroup(int * n,int which)1663 int *faction_getGroup( int *n, int which )
1664 {
1665    int *group;
1666    int i;
1667 
1668    /* Set defaults. */
1669    group = NULL;
1670    *n = 0;
1671 
1672    switch(which) {
1673       case 0: /* 'all' */
1674          *n = faction_nstack;
1675          group = malloc(sizeof(int) * *n);
1676          for(i = 0; i < faction_nstack; i++)
1677             group[i] = i;
1678          break;
1679 
1680       case 1: /* 'friendly' */
1681          for(i = 0; i < faction_nstack; i++)
1682             if(areAllies(FACTION_PLAYER, i)) {
1683                (*n)++;
1684                group = realloc(group, sizeof(int) * *n);
1685                group[*n - 1] = i;
1686             }
1687          break;
1688 
1689       case 2: /* 'neutral' */
1690          for(i = 0; i < faction_nstack; i++)
1691             if(!areAllies(FACTION_PLAYER, i) && !areEnemies(FACTION_PLAYER, i)) {
1692                (*n)++;
1693                group = realloc(group, sizeof(int) * *n);
1694                group[*n - 1] = i;
1695             }
1696          break;
1697 
1698       case 3: /* 'hostile' */
1699          for(i = 0; i < faction_nstack; i++)
1700             if(areEnemies(FACTION_PLAYER, i)) {
1701                (*n)++;
1702                group = realloc(group, sizeof(int) * *n);
1703                group[*n - 1] = i;
1704             }
1705          break;
1706 
1707       default:
1708          /* Defaults have already been set. */
1709          break;
1710    }
1711 
1712    return group;
1713 }
1714