#include #include #include "race.h" #include "alchemy.h" #include "build.h" #include "building.h" #include "equipment.h" #include "faction.h" #include "group.h" #include "item.h" #include "names.h" #include "pathfinder.h" #include "region.h" #include "ship.h" #include "skill.h" #include "spell.h" #include "terrain.h" #include "unit.h" /* util includes */ #include #include #include #include #include #include #include #include #include /* attrib includes */ #include /* libc includes */ #include #include #include #include #include #include #include /** external variables **/ race *races; int num_races = 0; static int rc_changes = 1; const char *racenames[MAXRACES] = { "dwarf", "elf", "goblin", "human", "troll", "demon", "insect", "halfling", "cat", "aquarian", "orc", "snotling", "undead", "youngdragon", "dragon", "wyrm", "ent", "catdragon", "dracoid", "irongolem", "stonegolem", "shadowdemon", "shadowmaster", "mountainguard", "alp", "toad", "braineater", "peasant", "wolf", "songdragon", "seaserpent", "shadowknight", "skeleton", "skeletonlord", "zombie", "juju", "ghoul", "ghast", "template", "clone" }; #define MAX_OPTIONS 4 typedef struct rcoption { unsigned char key[MAX_OPTIONS]; variant value[MAX_OPTIONS]; } rcoption; enum { RCO_NONE, RCO_SCARE, /* races that scare and eat peasants */ RCO_OTHER, /* may recruit from another race */ RCO_STAMINA, /* every n levels of stamina add +1 RC */ RCO_HUNGER, /* custom hunger.damage override (char *) */ RCO_TRADELUX, RCO_TRADEHERB }; static void rc_setoption(race *rc, int k, const char *value) { unsigned char key = (unsigned char)k; variant *v = NULL; if (!rc->options) { rc->options = malloc(sizeof(rcoption)); if (!rc->options) abort(); rc->options->key[0] = key; rc->options->key[1] = RCO_NONE; v = rc->options->value; } else { int i; for (i=0;!v && i < MAX_OPTIONS;++i) { if (rc->options->key[i]==key) { v = rc->options->value+i; break; } if (rc->options->key[i]==RCO_NONE) { v = rc->options->value+i; rc->options->key[i] = key; if (i+1 < MAX_OPTIONS) { rc->options->key[i+1]=RCO_NONE; } break; } } } assert(v); if (key == RCO_SCARE) { v->i = atoi(value); } else if (key == RCO_STAMINA) { v->i = atoi(value); } else if (key == RCO_OTHER) { v->v = rc_get_or_create(value); } else if (key == RCO_HUNGER) { v->v = str_strdup(value); } else if (key == RCO_TRADEHERB) { v->i = atoi(value); } else if (key == RCO_TRADELUX) { v->i = atoi(value); } } static variant *rc_getoption(const race *rc, int key) { if (rc->options) { int i; for (i=0;i!=MAX_OPTIONS && rc->options->key[i]!=RCO_NONE;++i) { if (rc->options->key[i]==key) { return rc->options->value+i; } } } return NULL; } const struct race *findrace(const char *s, const struct locale *lang) { void **tokens = get_translations(lang, UT_RACES); variant token; assert(lang); if (tokens && findtoken(*tokens, s, &token) == E_TOK_SUCCESS) { return (const struct race *)token.v; } return NULL; } const struct race *get_race(race_t rt) { const char * name; assert(rt >= 0); assert(rt < MAXRACES); name = racenames[rt]; if (!name) { return NULL; } return rc_find(name); } typedef struct xref { race_t id; const race *rc; } rc_xref; int cmp_xref(const void *a, const void *b) { const rc_xref *l = (const rc_xref *)a; const rc_xref *r = (const rc_xref *)b; if (l->rcrc) return -1; if (l->rc>r->rc) return 1; return 0; } static rc_xref *xrefs; race_t old_race(const struct race * rc) { static int cache; int l, r; if (rc_changed(&cache)) { int i; if (!xrefs) { xrefs = malloc(sizeof(rc_xref) * MAXRACES); if (!xrefs) abort(); } for (i = 0; i != MAXRACES; ++i) { xrefs[i].rc = get_race(i); xrefs[i].id = (race_t)i; } qsort(xrefs, MAXRACES, sizeof(rc_xref), cmp_xref); } l=0; r=MAXRACES-1; while (l<=r) { int m = (l+r)/2; if (rcxrefs[m].rc) { l = m+1; } else { return (race_t)xrefs[m].id; } } return NORACE; } race_list *get_familiarraces(void) { static int init = 0; static race_list *familiarraces; if (!init) { race *rc = races; for (; rc != NULL; rc = rc->next) { if (rc->flags & RCF_FAMILIAR) { racelist_insert(&familiarraces, rc); } } init = false; } return familiarraces; } void racelist_insert(struct race_list **rl, const struct race *r) { race_list *rl2 = (race_list *)malloc(sizeof(race_list)); if (!rl2) abort(); rl2->data = r; rl2->next = *rl; *rl = rl2; } static int race_mask = 0; void free_races(void) { race_mask = 0; while (races) { int i; race * rc = races->next; rcoption * opt = races->options; if (opt) { for (i=0;i!=MAX_OPTIONS && opt->key[i]!=RCO_NONE;++i) { if (opt->key[i]==RCO_HUNGER) { free(opt->value[i].v); } } free(opt); } for (i = 0; races->attack[i].type!=AT_NONE; ++i) { att *at = races->attack + i; if (at->type == AT_SPELL) { spellref_free(at->data.sp); } else { free(at->data.dice); } } free(xrefs); xrefs = 0; free(races->_name); free(races->def_damage); free(races); races = rc; } num_races = 0; ++rc_changes; } static race *rc_find_i(const char *name) { const char *rname = name; race *rc = races; while (rc && strcmp(rname, rc->_name) != 0) { rc = rc->next; } if (!rc) { const char *rc_depr[] = { "uruk", "orc", "illusion", "template", "juju-zombie", "juju", NULL }; int i; for (i = 0; rc_depr[i]; i += 2) { if (strcmp(name, rc_depr[i]) == 0) { rc = rc_find_i(rc_depr[i + 1]); log_info("a reference was made to the retired race '%s', returning '%s'.", name, rc->_name); break; } } } return rc; } race * rc_find(const char *name) { return rc_find_i(name); } bool rc_changed(int *cache) { assert(cache); if (*cache != rc_changes) { *cache = rc_changes; return true; } return false; } bool rc_can_use(const struct race *rc, const struct item_type *itype) { if (itype->mask_allow) { return (itype->mask_allow & rc->mask_item) != 0; } if (itype->mask_deny) { return (itype->mask_deny & rc->mask_item) == 0; } return true; } race *rc_create(const char *zName) { race *rc; int i; char zText[64]; assert(zName); rc = (race *)calloc(1, sizeof(race)); if (!rc) abort(); rc->mask_item = 1 << race_mask; ++race_mask; rc->magres.sa[1] = 1; rc->hitpoints = 1; rc->flags = RCF_DEFAULT; rc->weight = PERSON_WEIGHT; rc->capacity = 540; rc->income = 20; rc->recruit_multi = 1.0F; rc->regaura = 1.0F; rc->speed = 1.0F; rc->battle_flags = 0; rc->at_default = -2; rc->df_default = -2; if (strchr(zName, ' ') != NULL) { log_error("race '%s' has an invalid name. remove spaces\n", zName); assert(strchr(zName, ' ') == NULL); } rc->_name = str_strdup(zName); rc->attack[0].type = AT_COMBATSPELL; for (i = 1; i < RACE_ATTACKS; ++i) rc->attack[i].type = AT_NONE; rc->index = num_races++; ++rc_changes; rc->next = races; snprintf(zText, sizeof(zText), "age_%s", zName); rc->age_unit = (race_func)get_function(zText); snprintf(zText, sizeof(zText), "name_%s", zName); rc->name_unit = (race_func)get_function(zText); return races = rc; } race *rc_get_or_create(const char *zName) { race *rc; assert(zName); rc = rc_find_i(zName); return rc ? rc : rc_create(zName); } /** dragon movement **/ bool allowed_dragon(const region * src, const region * target) { if (fval(src->terrain, ARCTIC_REGION) && fval(target->terrain, SEA_REGION)) return false; return allowed_fly(src, target); } bool r_insectstalled(const region * r) { return fval(r->terrain, ARCTIC_REGION); } variant rc_magres(const race *rc) { return rc->magres; } double rc_maxaura(const race *rc) { return rc->maxaura / 100.0; } const char * rc_hungerdamage(const race *rc) { variant *v = rc_getoption(rc, RCO_HUNGER); return v ? (const char *)v->v : NULL; } int rc_armor_bonus(const race *rc) { variant *v = rc_getoption(rc, RCO_STAMINA); return v ? v->i : 0; } int rc_scare(const struct race *rc) { variant *v = rc_getoption(rc, RCO_SCARE); return v ? v->i : 0; } int rc_luxury_trade(const struct race *rc) { if (rc) { variant *v = rc_getoption(rc, RCO_TRADELUX); if (v) return v->i; } return 1000; } int rc_herb_trade(const struct race *rc) { if (rc) { variant *v = rc_getoption(rc, RCO_TRADEHERB); if (v) return v->i; } return 500; } void set_study_speed(race *rc, skill_t sk, int modifier) { if (!rc->study_speed) { rc->study_speed = calloc(1, MAXSKILLS); if (!rc->study_speed) abort(); } rc->study_speed[sk] = (char)modifier; } const race *rc_otherrace(const race *rc) { variant *v = rc_getoption(rc, RCO_OTHER); return v ? (const race *)v->v : NULL; } int rc_migrants_formula(const race *rc) { return (rc->flags&RCF_MIGRANTS) ? MIGRANTS_LOG10 : MIGRANTS_NONE; } void rc_set_param(struct race *rc, const char *key, const char *value) { if (strcmp(key, "recruit_multi") == 0) { rc->recruit_multi = atof(value); } else if (strcmp(key, "other_race")==0) { rc_setoption(rc, RCO_OTHER, value); } else if (strcmp(key, "scare")==0) { rc_setoption(rc, RCO_SCARE, value); } else if (strcmp(key, "hunger_damage")==0) { rc_setoption(rc, RCO_HUNGER, value); } else if (strcmp(key, "armor.stamina")==0) { rc_setoption(rc, RCO_STAMINA, value); } else if (strcmp(key, "luxury_trade")==0) { rc_setoption(rc, RCO_TRADELUX, value); } else if (strcmp(key, "herb_trade")==0) { rc_setoption(rc, RCO_TRADEHERB, value); } else { log_error("unknown property for race %s: %s=%s", rc->_name, key, value); } } const char* rc_key(const char *rcname, name_t n, char *name, size_t size) { const char * postfix = 0; switch (n) { case NAME_SINGULAR: postfix = ""; break; case NAME_PLURAL: postfix = "_p"; break; case NAME_DEFINITIVE: postfix = "_d"; break; case NAME_CATEGORY: postfix = "_x"; break; default: assert(!"invalid name_t enum in rc_name_s"); } if (postfix) { snprintf(name, size, "race::%s%s", rcname, postfix); return name; } return NULL; } const char* rc_name(const race * rc, name_t n, char *name, size_t size) { if (!rc) { return NULL; } return rc_key(rc->_name, n, name, size); } const char *rc_name_s(const race * rc, name_t n) { static char name[64]; /* FIXME: static return value */ return rc_name(rc, n, name, sizeof(name)); } const char *raceprefix(const unit * u) { group *g = get_group(u); attrib *attr = g ? g->attribs : u->faction->attribs; return get_prefix(attr); } const char *racename(const struct locale *loc, const unit * u, const race * rc) { const char *str, *prefix = raceprefix(u); if (prefix != NULL) { static char lbuf[80]; /* FIXME: static return value */ sbstring sbs; char ch[2]; sbs_init(&sbs, lbuf, sizeof(lbuf)); sbs_strcat(&sbs, LOC(loc, mkname("prefix", prefix))); str = LOC(loc, rc_name_s(rc, u->number != 1)); assert(~str[0] & 0x80 || !"unicode/not implemented"); ch[0] = (char)tolower(*(unsigned char *)str); ch[1] = 0; sbs_strcat(&sbs, ch); sbs_strcat(&sbs, str + 1); return lbuf; } str = LOC(loc, rc_name_s(rc, (u->number == 1) ? NAME_SINGULAR : NAME_PLURAL)); return str ? str : rc->_name; } void write_race_reference(const race * rc, struct storage *store) { WRITE_TOK(store, rc ? rc->_name : "none"); } struct race * read_race_reference(struct storage *store) { char zName[20]; READ_TOK(store, zName, sizeof(zName)); if (strcmp(zName, "none") == 0) { return NULL; } return rc_find_i(zName); } void register_race_function(race_func func, const char *name) { register_function((pf_generic)func, name); } int rc_mask(const race * rc) { assert(rc->mask_item); return rc->mask_item; } int rc_get_mask(char *list) { int mask = 0; char * tok = strtok(list, " ,"); while (tok) { race * rc = rc_get_or_create(tok); mask |= rc_mask(rc); tok = strtok(NULL, " ,"); } return mask; }