411 lines
10 KiB
C
411 lines
10 KiB
C
#include <malloc.h>
|
|
#include <string.h>
|
|
|
|
#include "lephisto.h"
|
|
#include "log.h"
|
|
#include "pack.h"
|
|
#include "xml.h"
|
|
#include "faction.h"
|
|
|
|
#define XML_FACTION_ID "Factions" // XML section id.
|
|
#define XML_FACTION_TAG "faction"
|
|
#define XML_ALLIANCE_ID "Alliances"
|
|
#define XML_ALLIANCE_TAG "alliance"
|
|
#define XML_ENEMIES_ID "Enemies"
|
|
#define XML_ENEMIES_TAG "enemies"
|
|
|
|
#define FACTION_DATA "../dat/faction.xml"
|
|
|
|
#define ALLIANCE_OFFSET 27182 // Special offset for alliances.
|
|
|
|
typedef struct Faction_ {
|
|
char* name;
|
|
|
|
int* enemies;
|
|
int nenemies;
|
|
int* allies;
|
|
int nallies;
|
|
} Faction;
|
|
|
|
static Faction* faction_stack = NULL;
|
|
static int nfactions = 0;
|
|
|
|
// Save alliance.
|
|
typedef struct Alliance_ {
|
|
char* name;
|
|
int* factions;
|
|
int nfactions;
|
|
} Alliance;
|
|
|
|
// Stack of alliances.
|
|
static Alliance* alliances = NULL;
|
|
static int nalliances = 0;
|
|
|
|
static Faction* faction_parse(xmlNodePtr parent);
|
|
static void alliance_parse(xmlNodePtr parent);
|
|
static void enemies_parse(xmlNodePtr parent);
|
|
static Alliance* alliance_get(char* name);
|
|
|
|
// Return the faction of name "name".
|
|
int faction_get(const char* name) {
|
|
int i;
|
|
for(i = 0; i < nfactions; i++)
|
|
if(strcmp(faction_stack[i].name, name)==0)
|
|
break;
|
|
|
|
if(i != nfactions)
|
|
return i;
|
|
|
|
DEBUG("Faction '%s' not found in stack.", name);
|
|
return -1;
|
|
}
|
|
|
|
// Return the id of an alliance.
|
|
int faction_getAlliance(char* name) {
|
|
int i;
|
|
for(i = 0; i < nalliances; i++)
|
|
if(strcmp(alliances[i].name, name)==0)
|
|
break;
|
|
|
|
if(i != nalliances)
|
|
return ALLIANCE_OFFSET + i;
|
|
DEBUG("Alliance '%s' not found in stack.", name);
|
|
return -1;
|
|
}
|
|
|
|
char* faction_name(int f) {
|
|
return faction_stack[f].name;
|
|
}
|
|
|
|
// Return the alliance of name 'name'.
|
|
static Alliance* alliance_get(char* name) {
|
|
int i;
|
|
for(i = 0; i < nalliances; i++)
|
|
if(strcmp(alliances[i].name, name)==0)
|
|
break;
|
|
|
|
if(i != nalliances)
|
|
return alliances+i;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Return 1 if Faction a and b are enemies.
|
|
int areEnemies(int a, int b) {
|
|
Faction* fa, *fb;
|
|
int i = 0;
|
|
|
|
if(a == b) return 0; // Luckily our factions aren't masochistic.
|
|
|
|
// Handle a.
|
|
if(faction_isFaction(a)) fa = &faction_stack[a];
|
|
else {
|
|
// a isn't valid.
|
|
DEBUG("areEnemies: %d is an invalid faction/alliance", a);
|
|
return 0;
|
|
}
|
|
|
|
// Handle b.
|
|
if(faction_isFaction(b)) fb = &faction_stack[b];
|
|
else {
|
|
// b isn't valid.
|
|
DEBUG("areEnemies: %d is an invalid faction/alliance", b);
|
|
return 0;
|
|
}
|
|
|
|
if(fa && fb) {
|
|
// Both are factions.
|
|
for(i = 0; i < fa->nenemies; i++)
|
|
if(fa->enemies[i] == b)
|
|
return 1;
|
|
for(i = 0; i < fb->nenemies; i++)
|
|
if(fb->enemies[i] == a)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Return 1 if Faction a and b are allies.
|
|
int areAllies(int a, int b) {
|
|
Faction* fa, *fb;
|
|
int i = 0;
|
|
|
|
// Handle a.
|
|
if(faction_isFaction(a)) fa = &faction_stack[a];
|
|
else {
|
|
// b isn't valid.
|
|
DEBUG("areEnemies: %d is an invalid faction/alliance", a);
|
|
return 0;
|
|
}
|
|
|
|
// Handle b.
|
|
if(faction_isFaction(b)) fb = &faction_stack[b];
|
|
else {
|
|
// b isn't valid.
|
|
DEBUG("areEnemies: %d is an invalid faction/alliance", b);
|
|
return 0;
|
|
}
|
|
|
|
if(a == b) return 0;
|
|
|
|
if(fa && fb) {
|
|
// Both are factions.
|
|
for(i = 0; i < fa->nallies; i++)
|
|
if(fa->allies[i] == b)
|
|
return 1;
|
|
|
|
for(i = 0; i < fb->nallies; i++)
|
|
if(fb->allies[i] == a)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Is faction f part of alliance a?
|
|
int faction_ofAlliance(int f, int a) {
|
|
int i;
|
|
Alliance* aa;
|
|
|
|
if(!faction_isFaction(f)) {
|
|
DEBUG("faction_ofAlliance: invalid alliance '%d'", f);
|
|
return 0;
|
|
}
|
|
|
|
if(!faction_isAlliance(a)) {
|
|
DEBUG("faction_ofAlliance: invalid alliance '%d'", a);
|
|
return 0;
|
|
}
|
|
|
|
aa = &alliances[a];
|
|
|
|
for(i = 0; i < aa->nfactions; i++)
|
|
if(aa->factions[i] == f)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Return true if a s an alliance.
|
|
int faction_isAlliance(int a) {
|
|
if((a < ALLIANCE_OFFSET) || (a >= ALLIANCE_OFFSET + nalliances))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Return true if f is a faction.
|
|
int faction_isFaction(int f) {
|
|
if((f < 0) || (f >= nfactions))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
// Parses a single faction, but does not set the allies/enemies.
|
|
static Faction* faction_parse(xmlNodePtr parent) {
|
|
Faction* tmp = CALLOC_L(Faction);
|
|
tmp->name = (char*)xmlGetProp(parent, (xmlChar*)"name");
|
|
if(tmp->name == NULL)
|
|
WARN("Faction from "FACTION_DATA" has invalid or no name");
|
|
return tmp;
|
|
}
|
|
|
|
// We set allies/enemies here, in the faction_stack.
|
|
static void alliance_parse(xmlNodePtr parent) {
|
|
Alliance* a;
|
|
int* i, j, n, m;
|
|
Faction* ft;
|
|
xmlNodePtr node, cur;
|
|
|
|
node = parent->xmlChildrenNode;
|
|
|
|
do {
|
|
if((node->type == XML_NODE_START) && (strcmp((char*)node->name,
|
|
XML_ALLIANCE_TAG)==0)) {
|
|
// Allocate a new alliance.
|
|
alliances = realloc(alliances, sizeof(Alliance)*(++nalliances));
|
|
alliances[nalliances-1].name = (char*)xmlGetProp(node,(xmlChar*)"name");
|
|
alliances[nalliances-1].factions = NULL;
|
|
alliances[nalliances-1].nfactions = 0;
|
|
|
|
// Parse the current alliance's allies.
|
|
cur = node->xmlChildrenNode;
|
|
do {
|
|
if(strcmp((char*)cur->name, "ally")==0) {
|
|
// Add the faction (and pointers to make things simple).
|
|
a = alliances + nalliances-1;
|
|
i = &a->nfactions;
|
|
(*i)++;
|
|
|
|
// Load the faction.
|
|
a->factions = realloc(a->factions, (*i)*sizeof(int));
|
|
a->factions[(*i)-1] = faction_get((char*)cur->children->content);
|
|
|
|
if(a->factions[(*i)-1] == -1)
|
|
WARN("Faction '%s' in alliance '%s' does not exist in "FACTION_DATA,
|
|
(char*)cur->children->content, a->name);
|
|
}
|
|
} while((cur = cur->next));
|
|
|
|
// Set the crap needed by faction_stack.
|
|
for(j = 0; j < (*i); j++) {
|
|
ft = &faction_stack[a->factions[j]];
|
|
ft->nallies += (*i)-1;
|
|
ft->allies = realloc(ft->allies, (ft->nallies)*sizeof(int));
|
|
for(n = 0, m = 0; n < (*i); n++, m++) {
|
|
// Add as ally for all factions exept self.
|
|
if(n == j) m--;
|
|
else if(n != j)
|
|
ft->allies[ft->nallies-(*i)+1+m] = a->factions[n];
|
|
}
|
|
}
|
|
}
|
|
} while((node = node->next));
|
|
}
|
|
|
|
static void enemies_parse(xmlNodePtr parent) {
|
|
xmlNodePtr node, cur;
|
|
int** f;
|
|
Faction* ft;
|
|
Alliance* a;
|
|
int i, *j, n, m, x, y, z, e;
|
|
char* type;
|
|
|
|
node = parent->xmlChildrenNode;
|
|
|
|
do {
|
|
if((node->type == XML_NODE_START)
|
|
&& (strcmp((char*)node->name, XML_ENEMIES_TAG)==0)) {
|
|
i = 0;
|
|
f = NULL;
|
|
j = NULL;
|
|
|
|
cur = node->xmlChildrenNode;
|
|
do {
|
|
if(strcmp((char*)cur->name,"enemy")==0) {
|
|
type = (char*)xmlGetProp(cur, (xmlChar*)"type");
|
|
|
|
i++;
|
|
j = realloc(j, sizeof(int)*i);
|
|
f = realloc(f, sizeof(int*)*i);
|
|
|
|
if(strcmp(type, "alliance")==0) {
|
|
// Enemy thing is an alliance.
|
|
a = alliance_get((char*)cur->children->content);
|
|
if(a == NULL)
|
|
WARN("Alliance %s not found in stack",
|
|
(char*)cur->children->content);
|
|
j[i-1] = a->nfactions;
|
|
f[i-1] = a->factions;
|
|
}
|
|
else if(strcmp(type,"faction")==0) {
|
|
// Enemy thing is only a faction.
|
|
j[i-1] = 1;
|
|
f[i-1] = malloc(sizeof(int));
|
|
f[i-1][0] = faction_get((char*)cur->children->content);
|
|
if(f[i-1][0] == -1)
|
|
WARN("Faction %s not found in stack",
|
|
(char*)cur->children->content);
|
|
}
|
|
free(type);
|
|
}
|
|
} while((cur = cur->next));
|
|
// Now actually parse and load up the enemies.
|
|
for(n = 0; n < i; n++) { // Unsinged int.
|
|
for(m = 0; m < j[n]; m++) { // Unsigned int.
|
|
// Faction.
|
|
// Add all the faction enemies to nenemies and alloc.
|
|
for(e = 0, x = 0; x < i; x++)
|
|
if(x != n) e += j[x]; // Store the total enemies.
|
|
// Now allocate the memory.
|
|
ft = &faction_stack[f[n][m]];
|
|
ft->nenemies += e;
|
|
ft->enemies = realloc(ft->enemies, sizeof(int)*ft->nenemies);
|
|
|
|
// Add the actualy enemies.
|
|
for(x = 0, z = 0; x < i; x++)
|
|
if(x != n)
|
|
// Make sure it's not from the same group.
|
|
if(x != n)
|
|
for(y = 0; y < j[x]; y++, z++)
|
|
ft->enemies[ft->nenemies-e+z] = f[x][y];
|
|
}
|
|
}
|
|
// Free al the temp memory.
|
|
for(x = 0; x < i; x++)
|
|
if(j[x]==1) free(f[x]); // Free the single malloced factions.
|
|
free(f); // Free the rest.
|
|
free(j);
|
|
}
|
|
} while((node = node->next));
|
|
}
|
|
|
|
|
|
// Load all the factions.
|
|
int factions_load(void) {
|
|
uint32_t bufsize;
|
|
char* buf = pack_readfile(DATA, FACTION_DATA, &bufsize);
|
|
|
|
xmlNodePtr node;
|
|
xmlDocPtr doc = xmlParseMemory(buf, bufsize);
|
|
|
|
Faction* tmp = NULL;
|
|
|
|
node = doc->xmlChildrenNode; // Faction node.
|
|
if(strcmp((char*)node->name, XML_FACTION_ID)) {
|
|
ERR("Malformed "FACTION_DATA" file: missing root element '"XML_FACTION_ID"'");
|
|
return -1;
|
|
}
|
|
|
|
node = node->xmlChildrenNode; // First faction node.
|
|
if(node == NULL) {
|
|
ERR("Malformed "FACTION_DATA" file: does not contain elements");
|
|
return -1;
|
|
}
|
|
|
|
do {
|
|
if(node->type == XML_NODE_START) {
|
|
if(strcmp((char*)node->name, XML_FACTION_TAG)==0) {
|
|
tmp = faction_parse(node);
|
|
faction_stack = realloc(faction_stack, sizeof(Faction)*(++nfactions));
|
|
memcpy(faction_stack+nfactions-1, tmp, sizeof(Faction));
|
|
free(tmp);
|
|
}
|
|
else if(strcmp((char*)node->name, XML_ALLIANCE_ID)==0)
|
|
alliance_parse(node);
|
|
else if(strcmp((char*)node->name, XML_ENEMIES_ID)==0)
|
|
enemies_parse(node);
|
|
}
|
|
} while((node = node->next));
|
|
|
|
xmlFreeDoc(doc);
|
|
free(buf);
|
|
xmlCleanupParser();
|
|
|
|
DEBUG("Loaded %d Faction%s", nfactions, (nfactions==1) ?"": "s");
|
|
|
|
return 0;
|
|
}
|
|
|
|
void factions_free(void) {
|
|
int i;
|
|
// Free alliances.
|
|
for(i = 0; i < nalliances; i++) {
|
|
free(alliances[i].name);
|
|
free(alliances[i].factions);
|
|
}
|
|
free(alliances);
|
|
alliances = NULL;
|
|
nalliances = 0;
|
|
|
|
// Free factions.
|
|
for(i = 0; i < nfactions; i++) {
|
|
free(faction_stack[i].name);
|
|
if(faction_stack[i].nallies > 0) free(faction_stack[i].allies);
|
|
if(faction_stack[i].nenemies > 0) free(faction_stack[i].enemies);
|
|
}
|
|
free(faction_stack);
|
|
faction_stack = NULL;
|
|
nfactions = 0;
|
|
}
|
|
|