#include #include #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 PLAYER_ALLY 70 /* Above this, player is considered ally. */ #define ALLIANCE_OFFSET 27182 /* Special offset for alliances. */ typedef struct Faction_ { char* name; int* enemies; int nenemies; int* allies; int nallies; int player; /* Standing with player - from -100 to 100. */ } 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. */ static Faction* faction_parse(xmlNodePtr parent); static void alliance_parse(xmlNodePtr parent); static void enemies_parse(xmlNodePtr parent); static Alliance* alliance_get(char* name); /* Extern. */ int pfaction_save(xmlTextWriterPtr writer); int pfaction_load(xmlNodePtr parent); /* 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; } void faction_modPlayer(int f, int mod) { if(faction_isFaction(f)) { faction_stack[f].player += mod; } else { DEBUG("%d is an invalid faction/alliance", f); return; } } int faction_getPlayer(int f) { if(faction_isFaction(f)) { return faction_stack[f].player; } else { DEBUG("%d is an invalid faction/alliance", f); return -1000; } } /* 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. */ /* Player handled seperately. */ if(a == FACTION_PLAYER) { if(faction_isFaction(b)) { if(faction_stack[b].player < 0) return 1; else return 0; } else { DEBUG("areEnemies: %d is an invalid faction/alliance", b); return 0; } } if(b == FACTION_PLAYER) { if(faction_isFaction(a)) { if(faction_stack[a].player < 0) return 1; else return 0; } else { DEBUG("areEnemies: %d is an invalid faction/alliance", a); return 0; } } /* 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; /* We assume player becomes allies with high rating. */ if(a == FACTION_PLAYER) { if(faction_isFaction(b)) { if(faction_stack[b].player > PLAYER_ALLY) return 1; else return 0; } else { DEBUG("%d is an invalid faction/alliance", b); return 0; } } if(b == FACTION_PLAYER) { if(faction_isFaction(a)) { if(faction_stack[a].player > PLAYER_ALLY) return 1; else return 0; } else { DEBUG("%d is an invalid faction/alliance", a); return 0; } } /* D'aww. Player has no allies. */ if((a == FACTION_PLAYER) || (b == FACTION_PLAYER)) return 0; /* Handle a. */ if(faction_isFaction(a)) fa = &faction_stack[a]; else { /* b isn't valid. */ DEBUG("%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("%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-ALLIANCE_OFFSET]; 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) { xmlNodePtr node; int player; Faction* tmp; 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"); player = 0; node = parent->xmlChildrenNode; do { if(xml_isNode(node, "player")) { tmp->player = xml_getInt(node); player = 1; } } while(xml_nextNode(node)); if(player == 0) DEBUG("Faction '%s' missing player tag", tmp->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(!xml_isNode(node, 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; } /* Player faction is hardcoded. */ faction_stack = malloc(sizeof(Faction)); faction_stack[0].name = strdup("Player"); faction_stack[0].nallies = 0; faction_stack[0].nenemies = 0; nfactions++; do { if(xml_isNode(node, XML_FACTION_TAG)) { tmp = faction_parse(node); faction_stack = realloc(faction_stack, sizeof(Faction)*(++nfactions)); memcpy(faction_stack + nfactions - 1, tmp, sizeof(Faction)); free(tmp); } else if(xml_isNode(node, XML_ALLIANCE_ID)) alliance_parse(node); else if(xml_isNode(node, XML_ENEMIES_ID)) 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; } int pfaction_save(xmlTextWriterPtr writer) { int i; xmlw_startElem(writer, "factions"); /* Player is faction 0. */ for(i = 1; i < nfactions; i++) { xmlw_startElem(writer, "faction"); xmlw_attr(writer, "name", "%s", faction_stack[i].name); xmlw_str(writer, "%d", faction_stack[i].player); xmlw_endElem(writer); /* Faction. */ } xmlw_endElem(writer); /* Faction. */ return 0; } int pfaction_load(xmlNodePtr parent) { xmlNodePtr node, cur; char* str; node = parent->xmlChildrenNode; do { if(xml_isNode(node, "factions")) { cur = node->xmlChildrenNode; do { if(xml_isNode(cur, "factions")) { xmlr_attr(cur, "name", str); faction_stack[faction_get(str)].player = xml_getInt(cur); free(str); } } while(xml_nextNode(cur)); } } while(xml_nextNode(node)); return 0; }