server/src/kernel/race.c

596 lines
14 KiB
C
Raw Normal View History

2010-08-08 10:06:34 +02:00
/*
2015-01-30 22:10:29 +01:00
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
Katja Zedel <katze@felidae.kn-bremen.de
Christian Schlittchen <corwin@amber.kn-bremen.de>
2010-08-08 10:06:34 +02:00
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**/
#include <platform.h>
#include <kernel/config.h>
#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"
2010-08-08 10:06:34 +02:00
#include "terrain.h"
#include "unit.h"
/* util includes */
#include <kernel/attrib.h>
2010-08-08 10:06:34 +02:00
#include <util/functions.h>
2016-11-22 12:32:28 +01:00
#include <util/umlaut.h>
2010-08-08 10:06:34 +02:00
#include <util/language.h>
#include <util/log.h>
#include <util/rng.h>
#include <util/strings.h>
#include <util/variant.h>
#include <storage.h>
2010-08-08 10:06:34 +02:00
/* attrib includes */
#include <attributes/raceprefix.h>
/* libc includes */
2012-06-04 03:41:07 +02:00
#include <assert.h>
#include <ctype.h>
#include <limits.h>
2012-06-04 03:41:07 +02:00
#include <math.h>
2010-08-08 10:06:34 +02:00
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/** external variables **/
2011-03-07 08:02:35 +01:00
race *races;
2010-08-08 10:06:34 +02:00
int num_races = 0;
2016-09-19 06:55:32 +02:00
static int rc_changes = 1;
2010-08-08 10:06:34 +02:00
const char *racenames[MAXRACES] = {
"dwarf", "elf", NULL, "goblin", "human", "troll", "demon", "insect",
2017-02-20 20:42:31 +01:00
"halfling", "cat", "aquarian", "orc", "snotling", "undead", NULL,
"youngdragon", "dragon", "wyrm", "ent", "catdragon", "dracoid",
2017-05-24 08:52:19 +02:00
NULL, NULL, "irongolem", "stonegolem", "shadowdemon",
"shadowmaster", "mountainguard", "alp", "toad", "braineater", "peasant",
"wolf", NULL, NULL, NULL, NULL, "songdragon", NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, "seaserpent",
"shadowknight", NULL, "skeleton", "skeletonlord", "zombie",
"juju", "ghoul", "ghast", NULL, NULL, "template",
"clone"
};
2018-10-26 16:08:46 +02:00
#define MAX_OPTIONS 4
typedef struct rcoption {
2018-10-26 16:08:46 +02:00
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;
2018-10-26 16:08:46 +02:00
for (i=0;!v && i < MAX_OPTIONS;++i) {
if (rc->options->key[i]==key) {
v = rc->options->value+i;
2017-11-20 08:24:56 +01:00
break;
}
2017-11-20 08:24:56 +01:00
if (rc->options->key[i]==RCO_NONE) {
v = rc->options->value+i;
rc->options->key[i] = key;
2018-10-26 16:08:46 +02:00
if (i+1 < MAX_OPTIONS) {
2017-11-20 08:24:56 +01:00
rc->options->key[i+1]=RCO_NONE;
}
break;
2017-02-06 11:52:07 +01:00
}
}
}
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;
2018-10-26 16:08:46 +02:00
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;
}
2016-11-22 12:32:28 +01:00
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) {
2014-06-30 04:15:03 +02:00
const char * name;
assert(rt >= 0);
2014-06-30 04:15:03 +02:00
assert(rt < MAXRACES);
name = racenames[rt];
if (!name) {
2016-09-19 06:43:56 +02:00
return NULL;
}
return rc_find(name);
}
2016-10-04 10:34:18 +02:00
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->rc<r->rc) return -1;
if (l->rc>r->rc) return 1;
return 0;
}
2016-10-04 10:51:37 +02:00
static rc_xref *xrefs;
2016-10-04 10:34:18 +02:00
race_t old_race(const struct race * rc)
{
static int cache;
int l, r;
2016-10-04 10:34:18 +02:00
if (rc_changed(&cache)) {
int i;
2016-10-04 10:34:18 +02:00
if (!xrefs) {
xrefs = malloc(sizeof(rc_xref) * MAXRACES);
if (!xrefs) abort();
2016-10-04 10:34:18 +02:00
}
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 (rc<xrefs[m].rc) {
r = m-1;
} else if (rc>xrefs[m].rc) {
l = m+1;
} else {
return (race_t)xrefs[m].id;
}
}
return NORACE;
}
2011-03-07 08:02:35 +01:00
race_list *get_familiarraces(void)
2010-08-08 10:06:34 +02:00
{
static int init = 0;
static race_list *familiarraces;
if (!init) {
race *rc = races;
for (; rc != NULL; rc = rc->next) {
2017-02-20 09:32:03 +01:00
if (rc->flags & RCF_FAMILIAR) {
racelist_insert(&familiarraces, rc);
}
}
init = false;
2010-08-08 10:06:34 +02:00
}
return familiarraces;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void racelist_insert(struct race_list **rl, const struct race *r)
2010-08-08 10:06:34 +02:00
{
race_list *rl2 = (race_list *)malloc(sizeof(race_list));
2010-08-08 10:06:34 +02:00
if (!rl2) abort();
rl2->data = r;
rl2->next = *rl;
2010-08-08 10:06:34 +02:00
*rl = rl2;
2010-08-08 10:06:34 +02:00
}
static int race_mask = 0;
void free_races(void) {
race_mask = 0;
while (races) {
2017-01-28 19:52:28 +01:00
int i;
race * rc = races->next;
rcoption * opt = races->options;
2017-01-28 19:52:28 +01:00
if (opt) {
2018-10-26 16:08:46 +02:00
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);
}
2017-01-28 19:52:28 +01:00
for (i = 0; races->attack[i].type!=AT_NONE; ++i) {
2017-02-07 21:37:38 +01:00
att *at = races->attack + i;
if (at->type == AT_SPELL) {
spellref_free(at->data.sp);
}
else {
free(at->data.dice);
}
2017-01-28 19:52:28 +01:00
}
2016-10-04 10:51:37 +02:00
free(xrefs);
xrefs = 0;
2014-12-30 01:44:28 +01:00
free(races->_name);
free(races->def_damage);
free(races);
races = rc;
}
num_races = 0;
2016-09-19 06:55:32 +02:00
++rc_changes;
}
static race *rc_find_i(const char *name)
2010-08-08 10:06:34 +02:00
{
const char *rname = name;
race *rc = races;
2010-08-08 10:06:34 +02:00
2016-08-31 09:26:48 +02:00
while (rc && strcmp(rname, rc->_name) != 0) {
rc = rc->next;
}
2017-02-20 20:42:31 +01:00
if (!rc) {
const char *rc_depr[] = { "uruk", "orc", "illusion", "template", "juju-zombie", "juju", NULL };
2017-02-20 20:42:31 +01:00
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]);
2018-05-10 21:05:51 +02:00
log_info("a reference was made to the retired race '%s', returning '%s'.", name, rc->_name);
2017-02-20 20:42:31 +01:00
break;
}
}
}
return rc;
2010-08-08 10:06:34 +02:00
}
const race * rc_find(const char *name) {
return rc_find_i(name);
}
2016-09-19 06:55:32 +02:00
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)
{
2014-06-30 04:15:03 +02:00
race *rc;
int i;
char zText[64];
2014-06-30 04:15:03 +02:00
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);
}
2010-08-08 10:06:34 +02:00
/** dragon movement **/
bool allowed_dragon(const region * src, const region * target)
2010-08-08 10:06:34 +02:00
{
if (fval(src->terrain, ARCTIC_REGION) && fval(target->terrain, SEA_REGION))
return false;
return allowed_fly(src, target);
2010-08-08 10:06:34 +02:00
}
bool r_insectstalled(const region * r)
2010-08-08 10:06:34 +02:00
{
return fval(r->terrain, ARCTIC_REGION);
2010-08-08 10:06:34 +02:00
}
variant rc_magres(const race *rc) {
return rc->magres;
}
2017-02-03 19:50:48 +01:00
double rc_maxaura(const race *rc) {
return rc->maxaura / 100.0;
2017-02-03 19:50:48 +01:00
}
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;
2010-08-08 10:06:34 +02:00
}
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));
}
2011-03-07 08:02:35 +01:00
const char *raceprefix(const unit * u)
2010-08-08 10:06:34 +02:00
{
2018-10-26 22:12:43 +02:00
group *g = get_group(u);
attrib *attr = g ? g->attribs : u->faction->attribs;
return get_prefix(attr);
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
const char *racename(const struct locale *loc, const unit * u, const race * rc)
2010-08-08 10:06:34 +02:00
{
const char *str, *prefix = raceprefix(u);
if (prefix != NULL) {
static char lbuf[80]; /* FIXME: static return value */
2018-01-26 17:39:15 +01:00
sbstring sbs;
char ch[2];
sbs_init(&sbs, lbuf, sizeof(lbuf));
2018-12-01 20:25:06 +01:00
sbs_strcat(&sbs, LOC(loc, mkname("prefix", prefix)));
2018-01-26 17:39:15 +01:00
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;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void write_race_reference(const race * rc, struct storage *store)
2010-08-08 10:06:34 +02:00
{
WRITE_TOK(store, rc ? rc->_name : "none");
2010-08-08 10:06:34 +02:00
}
struct race * read_race_reference(struct storage *store)
2010-08-08 10:06:34 +02:00
{
char zName[20];
READ_TOK(store, zName, sizeof(zName));
2010-08-08 10:06:34 +02:00
if (strcmp(zName, "none") == 0) {
return NULL;
}
return rc_find_i(zName);
2010-08-08 10:06:34 +02:00
}
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;
}