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