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>
|
2014-08-24 22:54:40 +02:00
|
|
|
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"
|
2017-01-28 19:42:20 +01:00
|
|
|
#include "spell.h"
|
2010-08-08 10:06:34 +02:00
|
|
|
#include "terrain.h"
|
|
|
|
#include "unit.h"
|
|
|
|
|
|
|
|
/* util includes */
|
|
|
|
#include <util/attrib.h>
|
|
|
|
#include <util/bsdstring.h>
|
|
|
|
#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>
|
2013-12-31 10:06:28 +01:00
|
|
|
|
|
|
|
#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 <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
|
|
|
|
2014-06-30 03:10:02 +02:00
|
|
|
static const char *racenames[MAXRACES] = {
|
|
|
|
"dwarf", "elf", NULL, "goblin", "human", "troll", "demon", "insect",
|
|
|
|
"halfling", "cat", "aquarian", "orc", "snotling", "undead", "illusion",
|
|
|
|
"youngdragon", "dragon", "wyrm", "ent", "catdragon", "dracoid",
|
2016-09-22 18:28:37 +02:00
|
|
|
NULL, "spell", "irongolem", "stonegolem", "shadowdemon",
|
2014-06-30 03:10:02 +02:00
|
|
|
"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",
|
2016-09-22 18:28:37 +02:00
|
|
|
"shadowknight", NULL, "skeleton", "skeletonlord", "zombie",
|
|
|
|
"juju-zombie", "ghoul", "ghast", NULL, NULL, "template",
|
2014-06-30 03:10:02 +02:00
|
|
|
"clone"
|
|
|
|
};
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-09-20 20:27:41 +02:00
|
|
|
const struct race *get_race(race_t rt) {
|
2014-06-30 04:15:03 +02:00
|
|
|
const char * name;
|
2014-08-24 21:49:55 +02:00
|
|
|
|
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;
|
2014-06-30 03:10:02 +02:00
|
|
|
}
|
2016-09-20 20:27:41 +02:00
|
|
|
return rc_find(name);
|
2014-06-30 03:10:02 +02:00
|
|
|
}
|
|
|
|
|
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 i, l, r;
|
|
|
|
|
|
|
|
if (rc_changed(&cache)) {
|
|
|
|
if (!xrefs) {
|
|
|
|
xrefs = malloc(sizeof(rc_xref) * MAXRACES);
|
|
|
|
}
|
|
|
|
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
|
|
|
{
|
2014-08-24 22:54:40 +02:00
|
|
|
static int init = 0;
|
|
|
|
static race_list *familiarraces;
|
|
|
|
|
|
|
|
if (!init) {
|
|
|
|
race *rc = races;
|
|
|
|
for (; rc != NULL; rc = rc->next) {
|
|
|
|
if (rc->init_familiar != NULL) {
|
|
|
|
racelist_insert(&familiarraces, rc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
init = false;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
2014-08-24 22:54:40 +02:00
|
|
|
return familiarraces;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
void racelist_clear(struct race_list **rl)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-24 22:54:40 +02:00
|
|
|
while (*rl) {
|
|
|
|
race_list *rl2 = (*rl)->next;
|
|
|
|
free(*rl);
|
|
|
|
*rl = rl2;
|
|
|
|
}
|
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
|
|
|
{
|
2014-08-24 22:54:40 +02:00
|
|
|
race_list *rl2 = (race_list *)malloc(sizeof(race_list));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-08-24 22:54:40 +02:00
|
|
|
rl2->data = r;
|
|
|
|
rl2->next = *rl;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-08-24 22:54:40 +02:00
|
|
|
*rl = rl2;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2014-06-08 07:17:48 +02:00
|
|
|
void free_races(void) {
|
|
|
|
while (races) {
|
|
|
|
race * rc = races->next;
|
2017-01-28 19:42:20 +01:00
|
|
|
spellref_free(races->precombatspell);
|
2016-03-11 11:31:05 +01:00
|
|
|
free_params(&races->parameters);
|
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);
|
2014-06-08 07:17:48 +02:00
|
|
|
free(races);
|
2014-08-24 22:54:40 +02:00
|
|
|
races = rc;
|
2014-06-08 07:17:48 +02:00
|
|
|
}
|
2015-11-07 18:14:38 +01:00
|
|
|
num_races = 0;
|
2016-09-19 06:55:32 +02:00
|
|
|
++rc_changes;
|
2014-06-08 07:17:48 +02:00
|
|
|
}
|
|
|
|
|
2014-06-13 07:14:07 +02:00
|
|
|
static race *rc_find_i(const char *name)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-24 22:54:40 +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) {
|
2014-08-24 22:54:40 +02:00
|
|
|
rc = rc->next;
|
|
|
|
}
|
2014-12-12 11:28:37 +01:00
|
|
|
if (!rc && strcmp(name, "uruk") == 0) {
|
|
|
|
rc = rc_find_i("orc");
|
2015-07-02 09:49:51 +02:00
|
|
|
log_warning("a reference was made to the retired race '%s', returning '%s'.", name, rc->_name);
|
2014-12-12 11:28:37 +01:00
|
|
|
}
|
2014-08-24 22:54:40 +02:00
|
|
|
return rc;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2014-06-13 07:14:07 +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;
|
|
|
|
}
|
|
|
|
|
2016-09-20 20:27:41 +02:00
|
|
|
race *rc_create(const char *zName)
|
2014-06-13 07:14:07 +02:00
|
|
|
{
|
2014-06-30 04:15:03 +02:00
|
|
|
race *rc;
|
2015-01-19 15:13:03 +01:00
|
|
|
int i;
|
2014-06-30 04:15:03 +02:00
|
|
|
|
|
|
|
assert(zName);
|
2016-09-20 20:27:41 +02:00
|
|
|
rc = (race *)calloc(sizeof(race), 1);
|
|
|
|
rc->hitpoints = 1;
|
|
|
|
rc->weight = PERSON_WEIGHT;
|
|
|
|
rc->capacity = 540;
|
2016-10-04 09:14:49 +02:00
|
|
|
rc->income = 20;
|
2016-09-20 20:27:41 +02:00
|
|
|
rc->recruit_multi = 1.0F;
|
|
|
|
rc->regaura = 1.0F;
|
|
|
|
rc->speed = 1.0F;
|
|
|
|
rc->battle_flags = 0;
|
|
|
|
if (strchr(zName, ' ') != NULL) {
|
|
|
|
log_error("race '%s' has an invalid name. remove spaces\n", zName);
|
|
|
|
assert(strchr(zName, ' ') == NULL);
|
2014-06-13 07:14:07 +02:00
|
|
|
}
|
2017-01-10 16:31:05 +01:00
|
|
|
rc->_name = strdup(zName);
|
2016-09-20 20:27:41 +02:00
|
|
|
rc->precombatspell = NULL;
|
|
|
|
|
|
|
|
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;
|
|
|
|
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);
|
2014-06-13 07:14:07 +02:00
|
|
|
}
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
/** dragon movement **/
|
2012-06-24 07:41:07 +02:00
|
|
|
bool allowed_dragon(const region * src, const region * target)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-24 22:54:40 +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
|
|
|
}
|
|
|
|
|
2012-06-24 07:41:07 +02:00
|
|
|
bool r_insectstalled(const region * r)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-24 22:54:40 +02:00
|
|
|
return fval(r->terrain, ARCTIC_REGION);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2014-12-09 07:20:36 +01:00
|
|
|
const char* rc_name(const race * rc, name_t n, char *name, size_t size) {
|
2014-08-24 21:49:55 +02:00
|
|
|
const char * postfix = 0;
|
|
|
|
if (!rc) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
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;
|
2014-12-09 07:20:36 +01:00
|
|
|
default: assert(!"invalid name_t enum in rc_name_s");
|
2014-08-24 21:49:55 +02:00
|
|
|
}
|
|
|
|
if (postfix) {
|
2017-01-10 16:31:05 +01:00
|
|
|
snprintf(name, size, "race::%s%s", rc->_name, postfix);
|
2014-08-24 21:49:55 +02:00
|
|
|
return name;
|
|
|
|
}
|
|
|
|
return NULL;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2014-12-09 07:20:36 +01:00
|
|
|
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
|
|
|
{
|
2016-02-01 17:31:03 +01:00
|
|
|
attrib *asource = u->faction->attribs;
|
2014-08-24 22:54:40 +02:00
|
|
|
|
|
|
|
if (fval(u, UFL_GROUP)) {
|
2016-02-01 17:31:03 +01:00
|
|
|
attrib *agroup = a_find(u->attribs, &at_group);
|
2014-08-24 22:54:40 +02:00
|
|
|
if (agroup != NULL)
|
|
|
|
asource = ((const group *)(agroup->data.v))->attribs;
|
|
|
|
}
|
|
|
|
return get_prefix(asource);
|
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
|
|
|
{
|
2014-08-24 22:54:40 +02:00
|
|
|
const char *str, *prefix = raceprefix(u);
|
|
|
|
|
|
|
|
if (prefix != NULL) {
|
|
|
|
static char lbuf[80]; // FIXME: static return value
|
|
|
|
char *bufp = lbuf;
|
|
|
|
size_t size = sizeof(lbuf) - 1;
|
|
|
|
int ch, bytes;
|
|
|
|
|
|
|
|
bytes = (int)strlcpy(bufp, LOC(loc, mkname("prefix", prefix)), size);
|
|
|
|
if (wrptr(&bufp, &size, bytes) != 0)
|
|
|
|
WARN_STATIC_BUFFER();
|
|
|
|
|
2014-12-09 07:20:36 +01:00
|
|
|
bytes = (int)strlcpy(bufp, LOC(loc, rc_name_s(rc, u->number != 1)), size);
|
2014-08-24 22:54:40 +02:00
|
|
|
assert(~bufp[0] & 0x80 || !"unicode/not implemented");
|
|
|
|
ch = tolower(*(unsigned char *)bufp);
|
|
|
|
bufp[0] = (char)ch;
|
|
|
|
if (wrptr(&bufp, &size, bytes) != 0)
|
|
|
|
WARN_STATIC_BUFFER();
|
|
|
|
*bufp = 0;
|
|
|
|
|
|
|
|
return lbuf;
|
|
|
|
}
|
2014-12-09 07:20:36 +01:00
|
|
|
str = LOC(loc, rc_name_s(rc, (u->number == 1) ? NAME_SINGULAR : NAME_PLURAL));
|
2014-08-24 22:54:40 +02:00
|
|
|
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
|
|
|
{
|
2014-08-24 22:54:40 +02:00
|
|
|
WRITE_TOK(store, rc ? rc->_name : "none");
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
variant read_race_reference(struct storage *store)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-24 22:54:40 +02:00
|
|
|
variant result;
|
|
|
|
char zName[20];
|
|
|
|
READ_TOK(store, zName, sizeof(zName));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-08-24 22:54:40 +02:00
|
|
|
if (strcmp(zName, "none") == 0) {
|
|
|
|
result.v = NULL;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
result.v = rc_find_i(zName);
|
|
|
|
}
|
|
|
|
assert(result.v != NULL);
|
2010-08-08 10:06:34 +02:00
|
|
|
return result;
|
|
|
|
}
|
2015-11-24 19:53:27 +01:00
|
|
|
|
2016-08-28 21:44:09 +02:00
|
|
|
void register_race_description_function(race_desc_func func, const char *name) {
|
|
|
|
register_function((pf_generic)func, name);
|
|
|
|
}
|
|
|
|
|
2016-08-28 21:02:45 +02:00
|
|
|
void register_race_name_function(race_name_func func, const char *name) {
|
|
|
|
register_function((pf_generic)func, name);
|
2015-11-24 19:53:27 +01:00
|
|
|
}
|