/* 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 #include "eressea.h" #include "race.h" #include #include #include #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 #include /* attrib includes */ #include #include /* libc includes */ #include #include #include #include #include /** 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 = NULL; 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]; } 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; } void give_starting_equipment(struct unit *u) { struct region *r = u->region; switch(old_race(u->race)) { case RC_ELF: set_show_item(u->faction, I_FEENSTIEFEL); break; case RC_GOBLIN: 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_CAT: 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); } break; case RC_CENTAUR: rsethorses(r, 250+rand()%51+rand()%51); break; } u->hp = unit_max_hp(u); } 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); 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); 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); 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=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]); spelllist_add(&fspells->spells, find_spellbyid(SPL_FLEE)); spelllist_add(&fspells->spells, find_spellbyid(SPL_SLEEP)); spelllist_add(&fspells->spells, find_spellbyid(SPL_FRIGHTEN)); fspells = mkspells(new_race[RC_NYMPH]); spelllist_add(&fspells->spells, find_spellbyid(SPL_SEDUCE)); spelllist_add(&fspells->spells, find_spellbyid(SPL_CALM_MONSTER)); spelllist_add(&fspells->spells, find_spellbyid(SPL_SONG_OF_CONFUSION)); spelllist_add(&fspells->spells, find_spellbyid(SPL_DENYATTACK)); fspells = mkspells(new_race[RC_NYMPH]); spelllist_add(&fspells->spells, find_spellbyid(SPL_SEDUCE)); spelllist_add(&fspells->spells, find_spellbyid(SPL_CALM_MONSTER)); spelllist_add(&fspells->spells, find_spellbyid(SPL_SONG_OF_CONFUSION)); spelllist_add(&fspells->spells, find_spellbyid(SPL_DENYATTACK)); fspells = mkspells(new_race[RC_UNICORN]); spelllist_add(&fspells->spells, find_spellbyid(SPL_RESISTMAGICBONUS)); spelllist_add(&fspells->spells, find_spellbyid(SPL_SONG_OF_PEACE)); spelllist_add(&fspells->spells, find_spellbyid(SPL_CALM_MONSTER)); spelllist_add(&fspells->spells, find_spellbyid(SPL_HERO)); spelllist_add(&fspells->spells, find_spellbyid(SPL_HEALINGSONG)); spelllist_add(&fspells->spells, find_spellbyid(SPL_DENYATTACK)); fspells = mkspells(new_race[RC_WRAITH]); spelllist_add(&fspells->spells, find_spellbyid(SPL_STEALAURA)); spelllist_add(&fspells->spells, find_spellbyid(SPL_FRIGHTEN)); spelllist_add(&fspells->spells, find_spellbyid(SPL_SUMMONUNDEAD)); fspells = mkspells(new_race[RC_IMP]); spelllist_add(&fspells->spells, find_spellbyid(SPL_STEALAURA)); fspells = mkspells(new_race[RC_DREAMCAT]); spelllist_add(&fspells->spells, find_spellbyid(SPL_ILL_SHAPESHIFT)); spelllist_add(&fspells->spells, find_spellbyid(SPL_TRANSFERAURA_TRAUM)); fspells = mkspells(new_race[RC_FEY]); spelllist_add(&fspells->spells, find_spellbyid(SPL_DENYATTACK)); spelllist_add(&fspells->spells, find_spellbyid(SPL_CALM_MONSTER)); spelllist_add(&fspells->spells, find_spellbyid(SPL_SEDUCE)); } void init_races(void) { init_familiarspells(); }