server/src/common/kernel/race.c
Enno Rehling b252b00163 Der server mag es nicht, wenn Rassen ein space im internal name haben, weil
trigger den rassennamen ohne "" schreiben. also operiere ich die mal raus,
spaces soll man da auch nicht haben.
2005-08-21 09:24:14 +00:00

732 lines
19 KiB
C

/* vi: set ts=2:
*
* Eressea PB(E)M host Copyright (C) 1998-2003
* Christian Schlittchen (corwin@amber.kn-bremen.de)
* Katja Zedel (katze@felidae.kn-bremen.de)
* Henning Peters (faroul@beyond.kn-bremen.de)
* Enno Rehling (enno@eressea-pbem.de)
* Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
*
* based on:
*
* Atlantis v1.0 13 September 1993 Copyright 1993 by Russell Wallace
* Atlantis v1.7 Copyright 1996 by Alex Schröder
*
* This program may not be used, modified or distributed without
* prior permission by the authors of Eressea.
* This program may not be sold or used commercially without prior written
* permission from the authors.
*/
#include <config.h>
#include "eressea.h"
#include "race.h"
#include <races/zombies.h>
#include <races/dragons.h>
#include <races/illusion.h>
#include "alchemy.h"
#include "build.h"
#include "building.h"
#include "faction.h"
#include "item.h"
#include "magic.h"
#include "region.h"
#include "spell.h"
#include "unit.h"
#include "names.h"
#include "pathfinder.h"
#include "ship.h"
#include "skill.h"
#include "karma.h"
#include "group.h"
/* util includes */
#include <attrib.h>
#include <functions.h>
/* attrib includes */
#include <attributes/raceprefix.h>
#include <attributes/synonym.h>
/* libc includes */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
/** external variables **/
race * races;
void
racelist_clear(struct race_list **rl)
{
while (*rl) {
race_list * rl2 = (*rl)->next;
free(*rl);
*rl = rl2;
}
}
void
racelist_insert(struct race_list **rl, const struct race *r)
{
race_list *rl2 = (race_list*)malloc(sizeof(race_list));
rl2->data = r;
rl2->next = *rl;
*rl = rl2;
}
race *
rc_new(const char * zName)
{
char zBuffer[80];
race * rc = calloc(sizeof(race), 1);
if (strchr(zName, ' ')!=NULL) {
log_error(("race '%s' has an invalid name. remove spaces\n", zName));
assert(strchr(zName, ' ')==NULL);
}
strcpy(zBuffer, zName);
rc->_name[0] = strdup(zBuffer);
sprintf(zBuffer, "%s_p", zName);
rc->_name[1] = strdup(zBuffer);
sprintf(zBuffer, "%s_d", zName);
rc->_name[2] = strdup(zBuffer);
sprintf(zBuffer, "%s_x", zName);
rc->_name[3] = strdup(zBuffer);
rc->precombatspell = SPL_NOSPELL;
return rc;
}
race *
rc_add(race * rc)
{
rc->next = races;
return races = rc;
}
static const char * racealias[2][2] = {
{ "skeletton lord", "skeleton lord" },
{ NULL, NULL }
};
race *
rc_find(const char * name)
{
const char * rname = name;
race * rc = races;
int i;
for (i=0;racealias[i][0];++i) {
if (strcmp(racealias[i][0], name)==0) {
rname = racealias[i][1];
break;
}
}
while (rc && !strcmp(rname, rc->_name[0])==0) rc = rc->next;
return rc;
}
/** dragon movement **/
boolean
allowed_dragon(const region * src, const region * target)
{
if (src->terrain==T_GLACIER && target->terrain == T_OCEAN) return false;
return allowed_fly(src, target);
}
char ** race_prefixes = NULL;
extern void
add_raceprefix(const char * prefix)
{
static size_t size = 4;
static unsigned int next = 0;
if (race_prefixes==NULL) race_prefixes = malloc(size * sizeof(char*));
if (next+1==size) {
size *= 2;
race_prefixes = realloc(race_prefixes, size * sizeof(char*));
}
race_prefixes[next++] = strdup(prefix);
race_prefixes[next] = NULL;
}
/* Die Bezeichnungen dürfen wegen der Art des Speicherns keine
* Leerzeichen enthalten! */
const struct race_syn race_synonyms[] = {
{1, {"Fee", "Feen", "Feen", "Feen"}},
{2, {"Gnoll", "Gnolle", "Gnollen", "Gnoll"}},
{-1, {NULL, NULL, NULL, NULL}}
};
/* "den Zwergen", "Halblingsparteien" */
void
set_show_item(faction *f, item_t i)
{
attrib *a = a_add(&f->attribs, a_new(&at_showitem));
a->data.v = (void*)olditemtype[i];
}
static item * equipment;
void add_equipment(const item_type * itype, int number)
{
if (itype!=NULL) i_change(&equipment, itype, number);
}
void
give_starting_equipment(struct region *r, struct unit *u)
{
item * itm = equipment;
while (itm!=NULL) {
i_add(&u->items, i_new(itm->type, itm->number));
itm=itm->next;
}
switch(old_race(u->race)) {
case RC_DWARF:
set_level(u, SK_SWORD, 1);
set_item(u, I_AXE, 1);
set_item(u, I_CHAIN_MAIL, 1);
break;
case RC_ELF:
set_item(u, I_FEENSTIEFEL, 1);
set_show_item(u->faction, I_FEENSTIEFEL);
break;
case RC_ORC:
case RC_URUK:
set_level(u, SK_SPEAR, 4);
set_level(u, SK_SWORD, 4);
set_level(u, SK_CROSSBOW, 4);
set_level(u, SK_LONGBOW, 4);
set_level(u, SK_CATAPULT, 4);
break;
case RC_GOBLIN:
set_item(u, I_RING_OF_INVISIBILITY, 1);
set_show_item(u->faction, I_RING_OF_INVISIBILITY);
scale_number(u, 10);
break;
case RC_HUMAN:
{
building *b = new_building(bt_find("castle"), r, u->faction->locale);
b->size = 10;
u->building = b;
fset(u, UFL_OWNER);
}
break;
case RC_TROLL:
set_level(u, SK_BUILDING, 1);
set_level(u, SK_OBSERVATION, 3);
set_item(u, I_STONE, 50);
break;
case RC_DAEMON:
set_level(u, SK_AUSDAUER, 15);
u->hp = unit_max_hp(u);
break;
case RC_INSECT:
/* TODO: Potion-Beschreibung ausgeben */
i_change(&u->items, oldpotiontype[P_WARMTH]->itype, 9);
break;
case RC_HALFLING:
set_level(u, SK_TRADE, 1);
set_level(u, SK_RIDING, 2);
set_item(u, I_HORSE, 2);
set_item(u, I_WAGON, 1);
set_item(u, I_BALM, 5);
set_item(u, I_SPICES, 5);
set_item(u, I_JEWELERY, 5);
set_item(u, I_MYRRH, 5);
set_item(u, I_OIL, 5);
set_item(u, I_SILK, 5);
set_item(u, I_INCENSE, 5);
break;
case RC_CAT:
set_item(u, I_RING_OF_INVISIBILITY, 1);
set_show_item(u->faction, I_RING_OF_INVISIBILITY);
break;
case RC_AQUARIAN:
{
ship *sh = new_ship(st_find("boat"), u->faction->locale, r);
sh->size = sh->type->construction->maxsize;
u->ship = sh;
fset(u, UFL_OWNER);
}
set_level(u, SK_SAILING, 1);
break;
case RC_CENTAUR:
rsethorses(r, 250+rand()%51+rand()%51);
break;
}
}
int
unit_max_hp(const unit * u)
{
int h;
double p;
static const curse_type * heal_ct = NULL;
h = u->race->hitpoints;
if (heal_ct==NULL) heal_ct = ct_find("healingzone");
p = pow(effskill(u, SK_AUSDAUER) / 2.0, 1.5) * 0.2;
h += (int) (h * p + 0.5);
if(fspecial(u->faction, FS_UNDEAD)) {
h *= 2;
}
/* der healing curse verändert die maximalen hp */
if (heal_ct) {
curse *c = get_curse(u->region->attribs, heal_ct);
if (c) {
h = (int) (h * (1.0+(curse_geteffect(c)/100)));
}
}
return h;
}
boolean
r_insectstalled(const region * r)
{
switch (rterrain(r)) {
case T_GLACIER:
case T_ICEBERG_SLEEP:
case T_ICEBERG:
return true;
default:
break;
}
return false;
}
const char *
rc_name(const race * rc, int n)
{
return mkname("race", rc->_name[n]);
}
const char *
raceprefix(const unit *u)
{
const attrib * asource = u->faction->attribs;
if (fval(u, UFL_GROUP)) {
const attrib * agroup = agroup = a_findc(u->attribs, &at_group);
if (agroup!=NULL) asource = ((const group *)(agroup->data.v))->attribs;
}
return get_prefix(asource);
}
const char *
racename(const struct locale *loc, const unit *u, const race * rc)
{
const char * prefix = raceprefix(u);
attrib * asyn = a_find(u->faction->attribs, &at_synonym);
if (prefix!=NULL) {
static char lbuf[80];
char * s = lbuf;
strcpy(lbuf, locale_string(loc, mkname("prefix", prefix)));
s += strlen(lbuf);
if (asyn!=NULL) {
strcpy(s, locale_string(loc,
((frace_synonyms *)(asyn->data.v))->synonyms[u->number != 1]));
} else {
strcpy(s, LOC(loc, rc_name(rc, u->number != 1)));
}
s[0] = (char)tolower(s[0]);
return lbuf;
} else if (asyn!=NULL) {
return(locale_string(loc,
((frace_synonyms *)(asyn->data.v))->synonyms[u->number != 1]));
}
return LOC(loc, rc_name(rc, u->number != 1));
}
static void
oldfamiliars(unit * familiar)
{
sc_mage * m = NULL;
race_t frt = old_race(familiar->race);
switch(frt) {
case RC_HOUSECAT:
/* Kräu+1, Mag, Pfer+1, Spi+3, Tar+3, Wahr+4, Aus */
set_level(familiar, SK_MAGIC, 1);
set_level(familiar, SK_SPY, 1);
set_level(familiar, SK_STEALTH, 1);
set_level(familiar, SK_OBSERVATION, 1);
m = create_mage(familiar, M_GRAU);
break;
case RC_TUNNELWORM:
/* Ber+50,Hol+50,Sbau+50,Aus+2*/
set_level(familiar, SK_MAGIC, 1);
set_level(familiar, SK_MINING, 1);
set_level(familiar, SK_LUMBERJACK, 1);
set_level(familiar, SK_AUSDAUER, 1);
m = create_mage(familiar, M_GRAU);
break;
case RC_EAGLE:
/* Spi, Wahr+2, Aus */
set_level(familiar, SK_MAGIC, 1);
set_level(familiar, SK_OBSERVATION, 1);
m = create_mage(familiar, M_GRAU);
break;
case RC_RAT:
/* Spionage+5, Tarnung+4, Wahrnehmung+2, Ausdauer */
set_level(familiar, SK_MAGIC, 1);
set_level(familiar, SK_SPY, 1);
set_level(familiar, SK_STEALTH, 1);
set_level(familiar, SK_OBSERVATION, 1);
set_level(familiar, SK_AUSDAUER, 1+rand()%8);
/* set_number(familiar, 50+rand()%500+rand()%500); */
m = create_mage(familiar, M_GRAU);
break;
case RC_PSEUDODRAGON:
/* Magie+1, Spionage, Tarnung, Wahrnehmung, Ausdauer */
m = create_mage(familiar, M_GRAU);
set_level(familiar, SK_MAGIC, 1);
m->combatspell[0] = SPL_FLEE;
m->combatspell[1] = SPL_SLEEP;
break;
case RC_NYMPH:
/* Alc, Arm, Bog+2, Han-2, Kräu+4, Mag+1, Pfer+5, Rei+5,
* Rüs-2, Sbau, Seg-2, Sta, Spi+2, Tak-2, Tar+3, Unt+10,
* Waf-2, Wag-2, Wahr+2, Steu-2, Aus-1 */
set_level(familiar, SK_MAGIC, 1);
set_level(familiar, SK_LONGBOW, 1);
set_level(familiar, SK_HERBALISM, 1);
set_level(familiar, SK_HORSE_TRAINING, 1);
set_level(familiar, SK_RIDING, 1);
set_level(familiar, SK_SPY, 1);
set_level(familiar, SK_STEALTH, 1);
set_level(familiar, SK_ENTERTAINMENT, 1);
set_level(familiar, SK_OBSERVATION, 1);
m = create_mage(familiar, M_GRAU);
m->combatspell[0] = SPL_SONG_OF_CONFUSION;
break;
case RC_UNICORN:
/* Mag+2, Spi, Tak, Tar+4, Wahr+4, Aus */
set_level(familiar, SK_MAGIC, 1);
set_level(familiar, SK_STEALTH, 1);
set_level(familiar, SK_OBSERVATION, 1);
m = create_mage(familiar, M_GRAU);
break;
case RC_WARG:
/* Spi, Tak, Tar, Wahri+2, Aus */
set_level(familiar, SK_MAGIC, 1);
set_level(familiar, SK_OBSERVATION, 1);
m = create_mage(familiar, M_GRAU);
break;
case RC_WRAITH:
/* Mag+1, Rei-2, Hie, Sta, Spi, Tar, Wahr, Aus */
set_level(familiar, SK_MAGIC, 1);
m = create_mage(familiar, M_GRAU);
break;
case RC_IMP:
/* Mag+1,Rei-1,Hie,Sta,Spi+1,Tar+1,Wahr+1,Steu+1,Aus*/
set_level(familiar, SK_MAGIC, 1);
set_level(familiar, SK_SPY, 1);
set_level(familiar, SK_STEALTH, 1);
set_level(familiar, SK_OBSERVATION, 1);
set_level(familiar, SK_TAXING, 1);
m = create_mage(familiar, M_GRAU);
break;
case RC_DREAMCAT:
/* Mag+1,Hie,Sta,Spi+1,Tar+1,Wahr+1,Steu+1,Aus*/
set_level(familiar, SK_MAGIC, 1);
set_level(familiar, SK_SPY, 1);
set_level(familiar, SK_STEALTH, 1);
set_level(familiar, SK_OBSERVATION, 1);
set_level(familiar, SK_TAXING, 1);
m = create_mage(familiar, M_GRAU);
break;
case RC_FEY:
/* Mag+1,Rei-1,Hie-1,Sta-1,Spi+2,Tar+5,Wahr+2,Aus */
set_level(familiar, SK_MAGIC, 1);
m = create_mage(familiar,M_GRAU);
break;
case RC_OWL:
/* Spi+1,Tar+1,Wahr+5,Aus */
set_level(familiar, SK_MAGIC, 1);
set_level(familiar, SK_SPY, 1);
set_level(familiar, SK_STEALTH, 1);
set_level(familiar, SK_OBSERVATION, 1);
m = create_mage(familiar,M_GRAU);
break;
case RC_HELLCAT:
/* Spi, Tak, Tar, Wahr+1, Aus */
set_level(familiar, SK_MAGIC, 1);
set_level(familiar, SK_OBSERVATION, 1);
m = create_mage(familiar,M_GRAU);
break;
case RC_TIGER:
/* Spi, Tak, Tar, Wahr+1, Aus */
set_level(familiar, SK_MAGIC, 1);
set_level(familiar, SK_OBSERVATION, 1);
m = create_mage(familiar,M_GRAU);
break;
}
if (m!=NULL) {
spell_list * fspells = familiarspells(familiar->race);
while (fspells!=NULL) {
add_spell(m, fspells->data->id);
fspells=fspells->next;
}
}
}
static item *
dragon_drops(const struct race * rc, int size)
{
item * itm = NULL;
switch (old_race(rc)) {
case RC_FIREDRAGON:
i_add(&itm, i_new(olditemtype[I_DRACHENBLUT], size));
break;
case RC_DRAGON:
i_add(&itm, i_new(olditemtype[I_DRACHENBLUT], size * 4));
i_add(&itm, i_new(olditemtype[I_DRAGONHEAD], size));
break;
case RC_WYRM:
i_add(&itm, i_new(olditemtype[I_DRACHENBLUT], size * 10));
i_add(&itm, i_new(olditemtype[I_DRAGONHEAD], size));
break;
case RC_SEASERPENT:
i_add(&itm, i_new(olditemtype[I_DRACHENBLUT], size * 6));
i_add(&itm, i_new(olditemtype[I_SEASERPENTHEAD], size));
break;
}
return itm;
}
static item *
phoenix_drops(const struct race *rc, int size)
{
const item_type * it_phoenixfeather = it_find("phoenixfeather");
item *itm = NULL;
if (it_phoenixfeather!=NULL) i_add(&itm, i_new(it_phoenixfeather, size));
return itm;
}
static item *
default_spoil(const struct race * rc, int size)
{
item * itm = NULL;
if (rand()%100 < RACESPOILCHANCE) {
char spoilname[32];
const item_type * itype;
sprintf(spoilname, "%sspoil", rc->_name[0]);
itype = it_find(spoilname);
if (itype!=NULL) {
i_add(&itm, i_new(itype, size));
}
}
return itm;
}
int
rc_specialdamage(const race * ar, const race * dr, const struct weapon_type * wtype)
{
race_t art = old_race(ar);
int m, modifier = 0;
if (wtype!=NULL && wtype->modifiers!=NULL) for (m=0;wtype->modifiers[m].value;++m) {
/* weapon damage for this weapon, possibly by race */
if (wtype->modifiers[m].flags & WMF_DAMAGE) {
race_list * rlist = wtype->modifiers[m].races;
if (rlist!=NULL) {
while (rlist) {
if (rlist->data == ar) break;
rlist = rlist->next;
}
if (rlist==NULL) continue;
}
modifier += wtype->modifiers[m].value;
}
}
switch (art) {
case RC_HALFLING:
if (wtype!=NULL && dragonrace(dr)) {
modifier += 5;
}
break;
default:
break;
}
return modifier;
}
void
write_race_reference(const race * rc, FILE * F)
{
fprintf(F, "%s ", rc?rc->_name[0]:"none");
}
int
read_race_reference(const struct race ** rp, FILE * F)
{
char zName[20];
if (global.data_version<NEWRACE_VERSION) {
int i;
fscanf(F, "%d", &i);
if (i>=0) {
*rp = new_race[i];
} else {
*rp = NULL;
return AT_READ_FAIL;
}
} else {
fscanf(F, "%s", zName);
if (strcmp(zName, "none")==0) {
*rp = NULL;
return AT_READ_OK;
}
*rp = rc_find(zName);
assert(*rp!=NULL);
}
return AT_READ_OK;
}
/* Die Funktionen werden über den hier registrierten Namen in races.xml
* in die jeweilige Rassendefiniton eingebunden */
void
register_races(void)
{
char zBuffer[MAX_PATH];
/* function initfamiliar */
register_function((pf_generic)oldfamiliars, "oldfamiliars");
/* function move
* into which regiontyp moving is allowed for this race
* race->move_allowed() */
register_function((pf_generic)allowed_swim, "moveswimming");
register_function((pf_generic)allowed_walk, "movewalking");
register_function((pf_generic)allowed_fly, "moveflying");
register_function((pf_generic)allowed_dragon, "movedragon");
/* function name
* generate a name for a nonplayerunit
* race->generate_name() */
register_function((pf_generic)untoten_name, "nameundead");
register_function((pf_generic)skeleton_name, "nameskeleton");
register_function((pf_generic)zombie_name, "namezombie");
register_function((pf_generic)ghoul_name, "nameghoul");
register_function((pf_generic)drachen_name, "namedragon");
register_function((pf_generic)dracoid_name, "namedracoid");
register_function((pf_generic)shadow_name, "nameshadow");
/* function age
* race->age() */
register_function((pf_generic)age_undead, "ageundead");
register_function((pf_generic)age_illusion, "ageillusion");
register_function((pf_generic)age_skeleton, "ageskeleton");
register_function((pf_generic)age_zombie, "agezombie");
register_function((pf_generic)age_ghoul, "ageghoul");
register_function((pf_generic)age_dragon, "agedragon");
register_function((pf_generic)age_firedragon, "agefiredragon");
/* function itemdrop
* to generate battle spoils
* race->itemdrop() */
register_function((pf_generic)dragon_drops, "dragondrops");
register_function((pf_generic)phoenix_drops, "phoenixdrops");
register_function((pf_generic)default_spoil, "defaultdrops");
sprintf(zBuffer, "%s/races.xml", resourcepath());
}
/** familiars **/
typedef struct familiar_spells {
struct familiar_spells * next;
spell_list * spells;
const race * familiar_race;
} familiar_spells;
static familiar_spells * racespells;
spell_list *
familiarspells(const race * rc)
{
familiar_spells * fspells = racespells;
while (fspells && rc!=fspells->familiar_race) {
fspells = fspells->next;
}
if (fspells!=NULL) return fspells->spells;
return NULL;
}
familiar_spells *
mkspells(const race * rc)
{
familiar_spells * fspells;
fspells = malloc(sizeof(familiar_spells));
fspells->next = racespells;
racespells = fspells;
fspells->familiar_race = rc;
fspells->spells = NULL;
return fspells;
}
void
init_familiarspells(void)
{
familiar_spells * fspells;
fspells = mkspells(new_race[RC_PSEUDODRAGON]);
add_spelllist(&fspells->spells, find_spellbyid(SPL_FLEE));
add_spelllist(&fspells->spells, find_spellbyid(SPL_SLEEP));
add_spelllist(&fspells->spells, find_spellbyid(SPL_FRIGHTEN));
fspells = mkspells(new_race[RC_NYMPH]);
add_spelllist(&fspells->spells, find_spellbyid(SPL_SEDUCE));
add_spelllist(&fspells->spells, find_spellbyid(SPL_CALM_MONSTER));
add_spelllist(&fspells->spells, find_spellbyid(SPL_SONG_OF_CONFUSION));
add_spelllist(&fspells->spells, find_spellbyid(SPL_DENYATTACK));
fspells = mkspells(new_race[RC_NYMPH]);
add_spelllist(&fspells->spells, find_spellbyid(SPL_SEDUCE));
add_spelllist(&fspells->spells, find_spellbyid(SPL_CALM_MONSTER));
add_spelllist(&fspells->spells, find_spellbyid(SPL_SONG_OF_CONFUSION));
add_spelllist(&fspells->spells, find_spellbyid(SPL_DENYATTACK));
fspells = mkspells(new_race[RC_UNICORN]);
add_spelllist(&fspells->spells, find_spellbyid(SPL_RESISTMAGICBONUS));
add_spelllist(&fspells->spells, find_spellbyid(SPL_SONG_OF_PEACE));
add_spelllist(&fspells->spells, find_spellbyid(SPL_CALM_MONSTER));
add_spelllist(&fspells->spells, find_spellbyid(SPL_HERO));
add_spelllist(&fspells->spells, find_spellbyid(SPL_HEALINGSONG));
add_spelllist(&fspells->spells, find_spellbyid(SPL_DENYATTACK));
fspells = mkspells(new_race[RC_WRAITH]);
add_spelllist(&fspells->spells, find_spellbyid(SPL_STEALAURA));
add_spelllist(&fspells->spells, find_spellbyid(SPL_FRIGHTEN));
add_spelllist(&fspells->spells, find_spellbyid(SPL_SUMMONUNDEAD));
fspells = mkspells(new_race[RC_IMP]);
add_spelllist(&fspells->spells, find_spellbyid(SPL_STEALAURA));
fspells = mkspells(new_race[RC_DREAMCAT]);
add_spelllist(&fspells->spells, find_spellbyid(SPL_ILL_SHAPESHIFT));
add_spelllist(&fspells->spells, find_spellbyid(SPL_TRANSFERAURA_TRAUM));
fspells = mkspells(new_race[RC_FEY]);
add_spelllist(&fspells->spells, find_spellbyid(SPL_DENYATTACK));
add_spelllist(&fspells->spells, find_spellbyid(SPL_CALM_MONSTER));
add_spelllist(&fspells->spells, find_spellbyid(SPL_SEDUCE));
}
void
init_races(void)
{
init_familiarspells();
}