/* vi: set ts=2: * * Eressea PB(E)M host Copyright (C) 1998-2000 * 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" /* attributes includes */ #include #include #include #include /* kernel includes */ #include "message.h" #include "curse.h" #include "spell.h" #include "names.h" #include "faction.h" #include "item.h" #include "border.h" #include "plane.h" #include "alchemy.h" #include "unit.h" #include "save.h" #include "magic.h" #include "building.h" #include "battle.h" #include "race.h" #include "pool.h" #include "region.h" #include "unit.h" #include "skill.h" #include "objtypes.h" #include "ship.h" #include "karma.h" #include "group.h" /* util includes */ #include #include #include #include #include #include #include #include /* libc includes */ #include #include #include #include #include #include #include #include #include #include /* exported variables */ region *regions; faction *factions; settings global; char buf[BUFSIZE + 1]; FILE *logfile; FILE *updatelog; const struct race * new_race[MAXRACES]; race_t old_race(const struct race * rc) { race_t i; for (i=0;i!=MAXRACES;++i) { if (new_race[i]==rc) return i; } return NORACE; } const char *directions[MAXDIRECTIONS+2] = { "northwest", "northeast", "east", "southeast", "southwest", "west", "", "pause" }; const char *gr_prefix[3] = { "einem", "einer", "einem" }; const char *parameters[MAXPARAMS] = { "LOCALE", "ALLES", "BAUERN", "BURG", "EINHEIT", "PRIVAT", "HINTEN", "KOMMANDO", "KRÄUTER", "NICHT", "NÄCHSTER", "PARTEI", "ERESSEA", "PERSONEN", "REGION", "SCHIFF", "SILBER", "STRAßEN", "TEMPORÄRE", "FLIEHE", "GEBÄUDE", "GIB", /* Für HELFE */ "KÄMPFE", "BEWACHE", "ZAUBER", "PAUSE", "VORNE", "AGGRESSIV", "DEFENSIV", "STUFE", "HELFE", "FREMDES", "AURA", "UM", "BEISTAND", "GNADE", "HINTER", "VOR", "ANZAHL", "GEGENSTÄNDE", "TRÄNKE", "GRUPPE", "PARTEITARNUNG", "BÄUME", "XEPOTION", "XEBALLOON", "XELAEN" }; const char *keywords[MAXKEYWORDS] = { "//", "BANNER", "ARBEITEN", "ATTACKIEREN", "BIETEN", "BEKLAUEN", "BELAGERE", "BENENNEN", "BENUTZEN", "BESCHREIBEN", "BETRETEN", "BEWACHEN", "BOTSCHAFT", "ENDE", "FAHREN", "NUMMER", "FOLGEN", "FORSCHEN", "GIB", "HELFEN", "KÄMPFEN", "KAMPFZAUBER", "KAUFEN", "KONTAKTIEREN", "LEHREN", "LERNEN", "LIEFERE", "MACHEN", "NACH", "PASSWORT", "REGION", "REKRUTIEREN", "RESERVIEREN", "ROUTE", "SABOTIEREN", "OPTION", "SPIONIEREN", "STIRB", "TARNEN", "TRANSPORTIEREN", "TREIBEN", "UNTERHALTEN", "VERKAUFEN", "VERLASSEN", "VERGESSEN", "ZAUBERE", "ZEIGEN", "ZERSTÖREN", "ZÜCHTEN", "DEFAULT", "REPORT", "URSPRUNG", "EMAIL", "MEINUNG", "MAGIEGEBIET", "PIRATERIE", "NEUSTART", "GRUPPE", "OPFERE", "BETEN", "SORTIEREN", "JIHAD", "GM", "INFO", #ifdef USE_UGROUPS "JOINVERBAND", "LEAVEVERBAND", #endif "PRÄFIX", "SYNONYM", "PFLANZEN", "WERWESEN", "XONTORMIA" }; const char *report_options[MAX_MSG] = { "Kampf", "Ereignisse", "Bewegung", "Einkommen", "Handel", "Produktion", "Orkvermehrung", "Zauber", "", "" }; const char *message_levels[ML_MAX] = { "Wichtig", "Debug", "Fehler", "Warnungen", "Infos" }; const char *options[MAXOPTIONS] = { "AUSWERTUNG", "COMPUTER", "ZUGVORLAGE", "SILBERPOOL", "STATISTIK", "DEBUG", "ZIPPED", "ZEITUNG", /* Option hat Sonderbehandlung! */ "MATERIALPOOL", "ADRESSEN", "BZIP2", "PUNKTE", "SHOWSKCHANGE" }; int max_skill(const faction * f, skill_t sk) { int m = INT_MAX; switch (sk) { case SK_MAGIC: m = MAXMAGICIANS; if (old_race(f->race) == RC_ELF) m += 1; m += fspecial(f, FS_MAGOCRACY) * 2; break; case SK_ALCHEMY: m = MAXALCHEMISTS; break; } return m; } char * g_basedir; char * g_resourcedir; const char * basepath(void) { if (g_basedir) return g_basedir; return "."; } const char * resourcepath(void) { static char zText[MAX_PATH]; if (g_resourcedir) return g_resourcedir; return strcat(strcpy(zText, basepath()), "/res"); } int count_all_money(const region * r) { const unit *u; int m = rmoney(r); for (u = r->units; u; u = u->next) m += get_money(u); return m; } int count_skill(faction * f, skill_t sk) { int n = 0; region *r; unit *u; region *last = lastregion(f); for (r = firstregion(f); r != last; r = r->next) for (u = r->units; u; u = u->next) if (u->faction == f && has_skill(u, sk)) if (!is_familiar(u)) n += u->number; return n; } int shipcapacity (const ship * sh) { int i; /* sonst ist construction:: size nicht ship_type::maxsize */ assert(!sh->type->construction || sh->type->construction->improvement==NULL); if (sh->type->construction && sh->size!=sh->type->construction->maxsize) return 0; #ifdef SHIPDAMAGE i = ((sh->size * DAMAGE_SCALE - sh->damage) / DAMAGE_SCALE) * sh->type->cargo / sh->size; i += ((sh->size * DAMAGE_SCALE - sh->damage) % DAMAGE_SCALE) * sh->type->cargo / (sh->size*DAMAGE_SCALE); #else i = sh->type->cargo; #endif return i; } int max_unique_id; int quiet = 0; FILE *debug; int shipspeed (ship * sh, const unit * u) { int k = sh->type->range; static const curse_type * stormwind_ct, * nodrift_ct; static boolean init; if (!init) { init = true; stormwind_ct = ct_find("stormwind"); nodrift_ct = ct_find("nodrift"); } assert(u->ship==sh); assert(sh->type->construction->improvement==NULL); /* sonst ist construction::size nicht ship_type::maxsize */ if (sh->size!=sh->type->construction->maxsize) return 0; if( curse_active(get_curse(sh->attribs, stormwind_ct))) k *= 2; if( curse_active(get_curse(sh->attribs, nodrift_ct))) k += 1; if (old_race(u->faction->race) == RC_AQUARIAN && old_race(u->race) == RC_AQUARIAN) k += 1; #ifdef SHIPSPEED k *= SHIPSPEED; #endif #ifdef SHIPDAMAGE if (sh->damage) k = (k * (sh->size * DAMAGE_SCALE - sh->damage) + sh->size * DAMAGE_SCALE- 1) / (sh->size*DAMAGE_SCALE); #endif return min (k, MAXSPEED); } /* erwartete Anzahl Einheiten x 2 */ #define UMAXHASH 199999 unit *unithash[UMAXHASH]; void uhash (unit * u) { assert(!u->nexthash || !"unit ist bereits gehasht"); u->nexthash = unithash[u->no % UMAXHASH]; unithash[u->no % UMAXHASH] = u; } void uunhash (unit * u) { unit ** x = &(unithash[u->no % UMAXHASH]); while (*x && *x!=u) x = &(*x)->nexthash; assert(*x || !"unit nicht gefunden"); *x = u->nexthash; u->nexthash=NULL; } unit * ufindhash (int i) { unit * u = unithash[i % UMAXHASH]; while (u && u->no!=i) u = u->nexthash; return u; } #define FMAXHASH 2047 faction * factionhash[FMAXHASH]; void fhash(faction * f) { int index = f->no % FMAXHASH; f->nexthash = factionhash[index]; factionhash[index] = f; } void funhash(faction * f) { int index = f->no % FMAXHASH; faction ** fp = factionhash+index; while (*fp && (*fp)!=f) fp = &(*fp)->nexthash; *fp = f->nexthash; } static faction * ffindhash(int no) { int index = no % FMAXHASH; faction * f = factionhash[index]; while (f && f->no!=no) f = f->nexthash; return f; } /* ----------------------------------------------------------------------- */ void stripfaction (faction * f) { #ifdef OLD_MESSAGES free_messages(f->msgs); #else /* TODO: inhalt auch löschen */ if (f->msgs) free(f->msgs); if (f->battles) free(f->battles); #endif /* TODO: free msgs */ freestrlist(f->mistakes); freelist(f->allies); free(f->email); free(f->banner); free(f->passw); free(f->override); free(f->name); #ifndef FAST_REGION vset_destroy(&f->regions); #endif while (f->attribs) a_remove (&f->attribs, f->attribs); freelist(f->ursprung); funhash(f); } void stripunit(unit * u) { free(u->name); free(u->display); free(u->thisorder); free(u->lastorder); freestrlist(u->orders); freestrlist(u->botschaften); if(u->skills) free(u->skills); while (u->items) { item * it = u->items->next; u->items->next = NULL; i_free(u->items); u->items = it; } while (u->attribs) a_remove (&u->attribs, u->attribs); } void verify_data(void) { #ifndef NDEBUG int lf = -1; faction *f; unit *u; int mage, alchemist; puts(" - Überprüfe Daten auf Korrektheit..."); list_foreach(faction, factions, f) { mage = 0; alchemist = 0; for (u=f->units;u;u=u->nextF) { if (eff_skill(u, SK_MAGIC, u->region)) { mage += u->number; } if (eff_skill(u, SK_ALCHEMY, u->region)) alchemist += u->number; if (u->number > 50000 || u->number < 0) { if (lf != f->no) { lf = f->no; printf("Partei %s:\n", factionid(f)); } log_warning(("Einheit %s hat %d Personen\n", unitid(u), u->number)); } } if (f->no != 0 && ((mage > 3 && old_race(f->race) != RC_ELF) || mage > 4)) log_error(("Partei %s hat %d Magier.\n", factionid(f), mage)); if (alchemist > 3) log_error(("Partei %s hat %d Alchemisten.\n", factionid(f), alchemist)); } list_next(f); #endif } int distribute(int old, int new_value, int n) { int i; int t; assert(new_value <= old); if (old == 0) return 0; t = (n / old) * new_value; for (i = (n % old); i; i--) if (rand() % old < new_value) t++; return t; } int change_hitpoints (unit * u, int value) { int hp = u->hp; hp += value; /* Jede Person benötigt mindestens 1 HP */ if (hp < u->number){ if (hp < 0){ /* Einheit tot */ hp = 0; } scale_number(u, hp); } u->hp = hp; return hp; } int atoip(const char *s) { int n; n = atoi (s); if (n < 0) n = 0; return n; } void scat (const char *s) { strncat (buf, s, BUFSIZE - strlen (buf)); } void icat (int n) { char s[12]; sprintf (s, "%d", n); scat (s); } region * findunitregion (const unit * su) { #ifndef SLOW_REGION return su->region; #else region *r; const unit *u; for (r = regions; r; r = r->next) { for (u = r->units; u; u = u->next) { if (su == u) { return r; } } } /* This should never happen */ assert (!"Die unit wurde nicht gefunden"); return (region *) NULL; #endif } int effskill(const unit * u, skill_t sk) { return eff_skill(u, sk, u->region); } int effstealth(const unit * u) { int e, es; /* Auf dem Ozean keine Tarnung! */ if (u->region->terrain == T_OCEAN) return 0; e = effskill(u, SK_STEALTH); es = u_geteffstealth(u); if (es >=0 && es < e) return es; return e; } int eff_stealth (const unit * u, const region * r) { int e, es; e = eff_skill (u, SK_STEALTH, r); es = u_geteffstealth(u); if (es >=0 && es < e) return es; return e; } void scale_number (unit * u, int n) { skill_t sk; const attrib * a; int remain; if (n == u->number) return; if (n && u->number) { int full; remain = ((u->hp%u->number) * (n % u->number)) % u->number; full = u->hp/u->number; /* wieviel kriegt jede person mindestens */ u->hp = full * n + (u->hp-full*u->number) * n / u->number; assert(u->hp>=0); if ((rand() % u->number) < remain) ++u->hp; /* Nachkommastellen */ } else { remain = 0; u->hp = 0; } for (a = a_find(u->attribs, &at_effect);a;a=a->nexttype) { effect_data * data = (effect_data *)a->data.v; int snew = data->value / u->number * n; if (n) { remain = data->value - snew / n * u->number; snew += remain * n / u->number; remain = (remain * n) % u->number; if ((rand() % u->number) < remain) ++snew; /* Nachkommastellen */ } data->value = snew; } for (sk = 0; sk < MAXSKILLS; sk++) { if (n==0 || u->number == 0) { remove_skill(u, sk); } } set_number(u, n); } boolean unit_has_cursed_item(unit *u) { item * itm = u->items; while (itm) { if (fval(itm->type, ITF_CURSED) && itm->number>0) return true; itm=itm->next; } return false; } #ifdef ALLIANCES int allied(const unit * u, const faction * f2, int mode) { if (u->faction->alliance==f2->alliance) return mode; return 0; } int isallied(const plane * pl, const faction * f, const faction * f2, int mode) { unused(pl); if (f->alliance==f2->alliance) return mode; return 0; } #else /* f hat zu f2 HELFE mode gesetzt */ int isallied(const plane * pl, const faction * f, const faction * f2, int mode) { ally *sf; attrib * a; if (f == f2) return mode; if (f2==NULL) return 0; a = a_find(f->attribs, &at_gm); while (a) { plane * p = (plane*)a->data.v; if (p==pl) return mode; a=a->next; } if (pl && pl->flags & PFL_FRIENDLY) return mode; if (mode != HELP_GIVE && pl && (pl->flags & PFL_NOALLIANCES)) return 0; for (sf = f->allies; sf; sf = sf->next) if (sf->faction == f2) return (sf->status & mode); return 0; } static int alliance(const ally * sf, const faction * f, int mode) { while (sf) { if (sf->faction == f) return sf->status & mode; sf = sf->next; } return 0; } /* Die Gruppe von Einheit u hat helfe zu f2 gesetzt. */ int allied(const unit * u, const faction * f2, int mode) { ally * sf; const attrib * a; plane * pl; if (u->faction == f2) return mode; if (u->faction == NULL || f2==NULL) return 0; sf = u->faction->allies; pl = getplane(u->region); if (pl && pl->flags & PFL_FRIENDLY) return mode; /* if f2 is a gm in this plane, everyone has an auto-help to it */ a = a_find(f2->attribs, &at_gm); while (a) { plane * p = (plane*)a->data.v; if (p==pl) return mode; a=a->next; } if (mode != HELP_GIVE && pl && (pl->flags & PFL_NOALLIANCES)) return 0; a = a_find(u->attribs, &at_group); if (a) sf = ((group*)a->data.v)->allies; return alliance(sf, f2, mode); } #endif boolean seefaction(const faction * f, const region * r, const unit * u, int modifier) { if (((f == u->faction) || !fval(u, FL_PARTEITARNUNG)) && cansee(f, r, u, modifier)) return true; return false; } boolean cansee(const faction * f, const region * r, const unit * u, int modifier) /* r kann != u->region sein, wenn es um durchreisen geht */ /* und es muss niemand aus f in der region sein, wenn sie vom Turm * erblickt wird */ { int n; boolean cansee = false; unit *u2; if (u->faction == f || omniscient(f)) { return true; } else if (old_race(u->race) == RC_SPELL) { return false; } else if (u->number == 0) { attrib *a = a_find(u->attribs, &at_creator); if(a) { /* u is an empty temporary unit. In this special case we look at the creating unit. */ u = (unit *)a->data.v; } else { return false; } } n = eff_stealth(u, r) - modifier; for (u2 = r->units; u2; u2 = u2->next) { if (u2->faction == f) { int o; if (getguard(u) || usiege(u) || u->building || u->ship) { cansee = true; break; } if (invisible(u) >= u->number && !get_item(u2, I_AMULET_OF_TRUE_SEEING)) continue; o = eff_skill(u2, SK_OBSERVATION, r); #if NIGHTEYES if (u2->enchanted == SP_NIGHT_EYES && o < NIGHT_EYE_TALENT) o = NIGHT_EYE_TALENT; #endif if (o >= n) { cansee = true; break; } } } return cansee; } #if 0 boolean cansee(faction * f, region * r, unit * u, int modifier) /* r kann != u->region sein, wenn es um durchreisen geht */ /* und es muss niemand aus f in der region sein, wenn sie vom Turm * erblickt wird */ { boolean cansee = false; unit *u2; #if FAST_CANSEE static region * lastr = NULL; static faction * lastf = NULL; static int maxskill = INT_MIN; #endif int n = 0; boolean ring = false; if (u->faction == f || omniscient(f)) return true; #if FAST_CANSEE /* buggy */ if (lastr==r && lastf==f) { n = eff_stealth(u, r) - modifier; if (n<=maxskill) return true; } else { lastf=f; lastr=r; maxskill=INT_MIN; } #endif if (old_race(u->race) == RC_SPELL || u->number == 0) return false; else { boolean xcheck = false; int o = INT_MIN; ring = (boolean)(ring || (invisible(u) >= u->number)); n = n || eff_stealth(u, r) - modifier; for (u2 = r->units; u2; u2 = u2->next) { if (u2->faction == f) { if (!xcheck && (getguard(u) || usiege(u) || u->building || u->ship)) { cansee = true; break; } else xcheck = true; if (ring && !get_item(u2, I_AMULET_OF_TRUE_SEEING)) continue; o = eff_skill(u2, SK_OBSERVATION, r); #if NIGHTEYES if (u2->enchanted == SP_NIGHT_EYES && o < NIGHT_EYE_TALENT) o = NIGHT_EYE_TALENT; #endif if (o >= n) { cansee = true; break; } } } #if FAST_CANSEE maxskill = o; #endif } return cansee; } #endif boolean cansee_durchgezogen(const faction * f, const region * r, const unit * u, int modifier) /* r kann != u->region sein, wenn es um durchreisen geht */ /* und es muss niemand aus f in der region sein, wenn sie vom Turm * erblickt wird */ { int n; boolean cansee = false; unit *u2; if (old_race(u->race) == RC_SPELL || u->number == 0) return false; else if (u->faction == f) cansee = true; else { n = eff_stealth(u, r) - modifier; for (u2 = r->units; u2; u2 = u2->next){ if (u2->faction == f) { int o; if (getguard(u) || usiege(u) || u->building || u->ship) { cansee = true; break; } if (invisible(u) >= u->number && !get_item(u2, I_AMULET_OF_TRUE_SEEING)) continue; o = eff_skill(u2, SK_OBSERVATION, r); #if NIGHTEYES if (u2->enchanted == SP_NIGHT_EYES && o < NIGHT_EYE_TALENT) o = NIGHT_EYE_TALENT; #endif if (o >= n) { cansee = true; break; } } } if (getguard(u) || usiege(u) || u->building || u->ship) { cansee = true; } } return cansee; } #ifndef NDEBUG const char * strcheck (const char *s, size_t maxlen) { static char buffer[16 * 1024]; if (strlen(s) > maxlen) { assert(maxlen < 16 * 1024); log_warning(("[strcheck] String wurde auf %d Zeichen verkürzt:\n%s\n", (int)maxlen, s)); strnzcpy(buffer, s, maxlen); return buffer; } return s; } #endif attrib_type at_lighthouse = { "lighthouse" /* Rest ist NULL; temporäres, nicht alterndes Attribut */ }; /* update_lighthouse: call this function whenever the size of a lighthouse changes * it adds temporary markers to the surrounding regions. * The existence of markers says nothing about the quality of the observer in * the lighthouse, for this may change more frequently. */ void update_lighthouse(building * lh) { region * r = lh->region; int d = (int)log10(lh->size) + 1; int x, y; static const struct building_type * bt_lighthouse; if (!bt_lighthouse) bt_lighthouse = bt_find("lighthouse"); assert(bt_lighthouse); if (lh->type!=bt_lighthouse) return; for (x=-d;x<=d;++x) { for (y=-d;y<=d;++y) { attrib * a; region * r2 = findregion(x+r->x, y+r->y); if (rterrain(r2)!=T_OCEAN) continue; if (!r2 || distance(r, r2) > d) continue; a = a_find(r2->attribs, &at_lighthouse); while (a) { building * b = (building*)a->data.v; if (b==lh) break; a=a->nexttype; } if (!a) { a = a_add(&r2->attribs, a_new(&at_lighthouse)); a->data.v = (void*)lh; } } } } int count_all(const faction * f) { int n = 0; unit *u; for (u=f->units;u;u=u->nextF) if (playerrace(u->race)) { n += u->number; assert(f==u->faction); } return n; } int count_maxmigrants(const faction * f) { int x = 0; if (old_race(f->race) == RC_HUMAN) { x = (int)(log10(count_all(f) / 50.0) * 20); if (x < 0) x = 0; } return x; } /*------------------------------------------------------------------*/ /* GET STR, I zur Eingabe von Daten liest diese aus dem Buffer, der beim ersten * Aufruf inititialisiert wird? */ char * igetstrtoken (const char *s1) { int i; static const char *s; static char lbuf[DISPLAYSIZE + 1]; if (s1) s = s1; while (*s == ' ') s++; i = 0; while (*s && *s != ' ' && i < DISPLAYSIZE) { lbuf[i] = (*s); /* Hier wird space_replacement wieder in space zurueck * verwandelt, ausser wenn es nach einem escape_char kommt. Im * letzteren Fall wird escape_char durch space_replacement * ersetzt, statt den aktuellen char einfach dran zu haengen. */ if (*s == SPACE_REPLACEMENT) { if (i > 0 && lbuf[i - 1] == ESCAPE_CHAR) lbuf[--i] = SPACE_REPLACEMENT; else lbuf[i] = ' '; } i++; s++; } lbuf[i] = 0; return lbuf; } char * getstrtoken (void) { return igetstrtoken (0); } int geti (void) { return atoip (getstrtoken ()); } /* GET KEYWORD, SKILL, ITEM, SPELL benutzen FINDSTR - welche Item um Item eine * Liste durchsucht. * * FIND wird immer von GET aufgerufen. GET braucht keine Parameter, IGET braucht * einen String aus dem gelesen wird. In FIND stehen dann listen etc drinnen. * FIND kann man auch allein verwenden, wenn der string _nur_ noch das gesuchte * object enthaelt. Steht noch weitere info darin, sollte man GET verwenden, * bzw. GETI wenn die info am Anfang eines neuen Stringes steht. */ int findstr(const char **v, const char *s, unsigned char n) { int i; size_t ss = strlen(s); if (!ss) return -1; for (i = 0; i != n; i++) if (!strncasecmp(s, v[i], ss)) return i; return -1; } enum { UT_NONE, UT_PARAM, UT_ITEM, UT_BUILDING, UT_HERB, UT_POTION, UT_MAX }; static struct lstr { const struct locale * lang; struct tnode tokens[UT_MAX]; struct tnode skillnames; struct tnode keywords; struct tnode races; struct tnode directions; struct tnode options; struct lstr * next; } * lstrs; static struct lstr * get_lnames(const struct locale * lang) { static struct lstr * lnames = NULL; static const struct locale * lastlang = NULL; if (lastlang!=lang || lnames==NULL) { lnames = lstrs; while (lnames && lnames->lang!=lang) lnames = lnames->next; if (lnames==NULL) { lnames = calloc(sizeof(struct lstr), 1); lnames->lang = lang; lnames->next = lstrs; lstrs = lnames; } } return lnames; } const struct race * findrace(const char * s, const struct locale * lang) { struct lstr * lnames = get_lnames(lang); const struct race * rc; if (findtoken(&lnames->races, s, (void **)&rc)==E_TOK_SUCCESS) { return rc; } return NULL; } int findoption(const char *s, const struct locale * lang) { struct lstr * lnames = get_lnames(lang); int dir; if (findtoken(&lnames->options, s, (void**)&dir)==E_TOK_SUCCESS) { return (direction_t)dir; } return NODIRECTION; } skill_t findskill(const char *s, const struct locale * lang) { struct lstr * lnames = get_lnames(lang); int i; if (findtoken(&lnames->skillnames, s, (void**)&i)==E_TOK_NOMATCH) return NOSKILL; return (skill_t)i; } keyword_t findkeyword(const char *s, const struct locale * lang) { struct lstr * lnames = get_lnames(lang); int i; #ifdef AT_PERSISTENT if (*s == '@') s++; #endif if (findtoken(&lnames->keywords, s, (void**)&i)==E_TOK_NOMATCH) return NOKEYWORD; if (global.disabled[i]) return NOKEYWORD; return (keyword_t) i; } keyword_t igetkeyword (const char *s, const struct locale * lang) { return findkeyword (igetstrtoken (s), lang); } keyword_t getkeyword (const struct locale * lang) { return findkeyword (getstrtoken (), lang); } param_t findparam(const char *s, const struct locale * lang) { struct lstr * lnames = get_lnames(lang); const building_type * btype; int i; if (findtoken(&lnames->tokens[UT_PARAM], s, (void**)&i)==E_TOK_NOMATCH) { btype = findbuildingtype(s, lang); if (btype!=NULL) return (param_t) P_BUILDING; return NOPARAM; } return (param_t)i; } param_t igetparam (const char *s, const struct locale *lang) { return findparam (igetstrtoken (s), lang); } param_t getparam (const struct locale * lang) { return findparam (getstrtoken (), lang); } #ifdef FUZZY_BASE36 extern int fuzzy_hits; boolean enable_fuzzy = false; #endif /* FUZZY_BASE36 */ faction * findfaction (int n) { faction * f; f = ffindhash(n); if (f) return f; for (f = factions; f; f = f->next) if (f->no == n) { fhash(f); return f; } #ifdef FUZZY_BASE36 if(enable_fuzzy) { n = atoi(itoa36(n)); if (n) { f = ffindhash(n); if (f) return f; for (f = factions; f; f = f->next) { if (f->no == n) { fhash(f); return (f); } } } } #endif /* FUZZY_BASE36 */ /* Gibt komische Seiteneffekte hier! */ /* if (n==MONSTER_FACTION) return makemonsters(); */ return NULL; } faction * getfaction (void) { return findfaction (getid()); } faction * findfaction_unique_id (int unique_id) { faction *f; for (f = factions; f; f = f->next) if (f->unique_id == unique_id) { return f; } return NULL; } unit * findunitr (const region * r, int n) { unit *u; /* findunit regional! */ for (u = r->units; u; u = u->next) if (u->no == n) return u; return 0; } unit *findunit(int n) { return findunitg(n, NULL); } unit * findunitg (int n, const region * hint) { /* Abfangen von Syntaxfehlern. */ if(n <= 0) return NULL; /* findunit global! */ hint = 0; return ufindhash (n); } unit * getnewunit (const region * r, const faction * f) { int n; n = getid(); return findnewunit (r, f, n); } int read_newunitid (const faction * f, const region * r) { int n; unit *u2; n = getid(); if (n == 0) return -1; u2 = findnewunit(r, f, n); if (u2) return u2->no; return -1; } int read_unitid (const faction * f, const region * r) { char *s; s = getstrtoken (); /* Da s nun nur einen string enthaelt, suchen wir ihn direkt in der * paramliste. machen wir das nicht, dann wird getnewunit in s nach der * nummer suchen, doch dort steht bei temp-units nur "temp" drinnen! */ switch (findparam (s, f->locale)) { case P_TEMP: return read_newunitid(f, r); } if (!s || *s == 0) return -1; return atoi36(s); } /* exported symbol */ boolean getunitpeasants; unit * getunitg(const region * r, const faction * f) { int n; getunitpeasants = 0; n = read_unitid(f, r); if (n == 0) { getunitpeasants = 1; return NULL; } if (n < 0) return 0; return findunit(n); } unit * getunit(const region * r, const faction * f) { int n; unit *u2; getunitpeasants = 0; n = read_unitid(f, r); if (n == 0) { getunitpeasants = 1; return NULL; } if (n < 0) return 0; for (u2 = r->units; u2; u2 = u2->next) { if (u2->no == n && !fval(u2, UFL_ISNEW)) { return u2; } } return 0; } /* - String Listen --------------------------------------------- */ strlist * makestrlist (const char *s) { strlist *S; S = malloc(sizeof(strlist)); S->s = strdup(s); S->next = NULL; return S; } void addstrlist (strlist ** SP, const char *s) { addlist(SP, makestrlist(s)); } void freestrlist (strlist * s) { strlist *q, *p = s; while (p) { q = p->next; free(p->s); free(p); p = q; } } /* - Meldungen und Fehler ------------------------------------------------- */ boolean nomsg = false; /* - Namen der Strukturen -------------------------------------- */ typedef char name[OBJECTIDSIZE + 1]; static name idbuf[8]; static int nextbuf = 0; char * estring(const char *s) { char *buf = idbuf[(++nextbuf) % 8]; char *r; strcpy(buf,s); r = buf; while(*buf) { if(*buf == ' ') { *buf = '~'; } buf++; } return r; } char * cstring(const char *s) { char *buf = idbuf[(++nextbuf) % 8]; char *r; strcpy(buf,s); r = buf; while(*buf) { if(*buf == '~') { *buf = ' '; } buf++; } return r; } const char * regionid(const region * r) { char *buf = idbuf[(++nextbuf) % 8]; if (!r) { strcpy(buf, "(Chaos)"); } else { sprintf(buf, "\\r(%d,%d)", r->x, r->y); } return buf; } const char * buildingname (const building * b) { char *buf = idbuf[(++nextbuf) % 8]; sprintf(buf, "%s (%s)", strcheck(b->name, NAMESIZE), itoa36(b->no)); return buf; } building * largestbuilding (const region * r, boolean img) { static const building_type * btype = NULL; building *b, *best = NULL; if (!btype) btype = bt_find("castle"); /* TODO: parameter der funktion? */ /* durch die verw. von '>' statt '>=' werden die aelteren burgen * bevorzugt. */ for (b = rbuildings(r); b; b = b->next) { if (b->type!=btype) { if (img) { const attrib * a = a_find(b->attribs, &at_icastle); if (!a) continue; if (a->data.v != btype) continue; } else continue; } if (best==NULL || b->size > best->size) best = b; } return best; } const char * unitname(const unit * u) { char *ubuf = idbuf[(++nextbuf) % 8]; sprintf(ubuf, "%s (%s)", strcheck(u->name, NAMESIZE), itoa36(u->no)); return ubuf; } char * xunitid(const unit *u) { char *buf = idbuf[(++nextbuf) % 8]; sprintf(buf, "%s in %s", unitname(u), regionid(u->region)); return buf; } /* -- Erschaffung neuer Einheiten ------------------------------ */ extern faction * dfindhash(int i); const char* forbidden[] = { "t", "te", "tem", "temp", NULL }; int forbiddenid(int id) { static int * forbid = NULL; static size_t len; size_t i; if (id<=0) return 1; if (!forbid) { while (forbidden[len]) ++len; forbid = calloc(len, sizeof(int)); for (i=0;i!=len;++i) { forbid[i] = strtol(forbidden[i], NULL, 36); } } for (i=0;i!=len;++i) if (id==forbid[i]) return 1; return 0; } /* ID's für Einheiten und Zauber */ int newunitid(void) { int random_unit_no; int start_random_no; random_unit_no = 1 + (rand() % MAX_UNIT_NR); start_random_no = random_unit_no; while (ufindhash(random_unit_no) || dfindhash(random_unit_no) || cfindhash(random_unit_no) || forbiddenid(random_unit_no)) { random_unit_no++; if (random_unit_no == MAX_UNIT_NR + 1) { random_unit_no = 1; } if (random_unit_no == start_random_no) { random_unit_no = (int) MAX_UNIT_NR + 1; } } return random_unit_no; } int newcontainerid(void) { int random_no; int start_random_no; random_no = 1 + (rand() % MAX_CONTAINER_NR); start_random_no = random_no; while (findship(random_no) || findbuilding(random_no)) { random_no++; if (random_no == MAX_CONTAINER_NR + 1) { random_no = 1; } if (random_no == start_random_no) { random_no = (int) MAX_CONTAINER_NR + 1; } } return random_no; } void createunitid(unit *u, int id) { if (id<=0 || id > MAX_UNIT_NR || ufindhash(id) || dfindhash(id) || forbiddenid(id)) u->no = newunitid(); else u->no = id; uhash(u); } unit * createunit(region * r, faction * f, int number, const struct race * rc) { return create_unit(r, f, number, rc, 0, NULL, NULL); } attrib_type at_creator = { "creator" /* Rest ist NULL; temporäres, nicht alterndes Attribut */ }; unit * create_unit(region * r, faction * f, int number, const struct race *urace, int id, const char * dname, unit *creator) { unit * u = calloc(1, sizeof(unit)); assert(f->alive); u_setfaction(u, f); set_string(&u->thisorder, ""); set_string(&u->lastorder, LOC(u->faction->locale, "defaultorder")); u_seteffstealth(u, -1); u->race = urace; u->irace = urace; set_number(u, number); /* die nummer der neuen einheit muss vor name_unit generiert werden, * da der default name immer noch 'Nummer u->no' ist */ createunitid(u, id); /* zuerst in die Region setzen, da zb Drachennamen den Regionsnamen * enthalten */ move_unit(u, r, NULL); /* u->race muss bereits gesetzt sein, wird für default-hp gebraucht */ /* u->region auch */ u->hp = unit_max_hp(u) * number; if (!dname) { name_unit(u); } else set_string(&u->name, dname); set_string(&u->display, ""); /* Nicht zu der Einheitenzahl zählen sollten auch alle Monster. Da * aber auf die MAXUNITS nur in MACHE TEMP geprüft wird, ist es egal */ if(!fval(u->race, RCF_UNDEAD)) { f->no_units++; } if (creator) { attrib * a; /* erbt Kampfstatus */ u->status = creator->status; /* erbt Gebäude/Schiff*/ if (creator->region==r) { u->building = creator->building; u->ship = creator->ship; } /* Temps von parteigetarnten Einheiten sind wieder parteigetarnt */ if (fval(creator, FL_PARTEITARNUNG)) fset(u, FL_PARTEITARNUNG); /* Daemonentarnung */ set_racename(&u->attribs, get_racename(creator->attribs)); if (fval(u->race, RCF_SHAPESHIFT) && fval(creator->race, RCF_SHAPESHIFT)) { u->irace = creator->irace; } /* Gruppen */ a = a_find(creator->attribs, &at_group); if (a) { group * g = (group*)a->data.v; a_add(&u->attribs, a_new(&at_group))->data.v = g; } a = a_find(creator->attribs, &at_otherfaction); if (a) { a_add(&u->attribs, make_otherfaction(get_otherfaction(a))); } a = a_add(&u->attribs, a_new(&at_creator)); a->data.v = creator; } /* Monster sind grundsätzlich parteigetarnt */ if(f->no <= 0) fset(u, FL_PARTEITARNUNG); return u; } /* Setzt Default Befehle -------------------------------------- */ boolean idle (faction * f) { return (boolean) (f ? false : true); } int maxworkingpeasants(const struct region * r) { #if GROWING_TREES int i = production(r) * MAXPEASANTS_PER_AREA - ((rtrees(r,2)+rtrees(r,1)/2) * TREESIZE); #else int i = production(r) * MAXPEASANTS_PER_AREA - rtrees(r) * TREESIZE; #endif return max(i, 0); } boolean check_leuchtturm(region * r, faction * f) { attrib * a; if (rterrain(r) != T_OCEAN) return false; for (a = a_find(r->attribs, &at_lighthouse);a;a=a->nexttype) { building *b = (building *)a->data.v; region *r2 = b->region; assert(b->type == bt_find("lighthouse")); if (fval(b, BLD_WORKING) && b->size >= 10) { int c = 0; unit *u; int d = 0; int maxd = (int)log10(b->size) + 1; for (u = r2->units; u; u = u->next) { if (u->building == b) { c += u->number; if (c > buildingcapacity(b)) break; if (f==NULL || u->faction == f) { if (!d) d = distance(r, r2); if (maxd < d) break; if (eff_skill(u, SK_OBSERVATION, r) >= d * 3) return true; } } else if (c) break; /* first unit that's no longer in the house ends the search */ } } } return false; } region * lastregion (faction * f) { region *r = f->last; if (!r && f->units) { for (r = firstregion(f); r; r = r->next) { plane * p = rplane(r); unit *u; attrib *ru; for (u = r->units; u; u = u->next) { if (u->faction == f) { f->last = r->next; break; } } if (f->last == r->next) continue; for (ru = a_find(r->attribs, &at_travelunit); ru; ru = ru->nexttype) { u = (unit*)ru->data.v; if (u->faction == f) { f->last = r->next; break; } } if (f->last == r->next) continue; if (check_leuchtturm(r, f)) f->last = r->next; if (p && is_watcher(p, f)) { f->last = r->next; } } } return f->last; } region * firstregion (faction * f) { region *r; if (f->first || !f->units) return f->first; for (r = regions; r; r = r->next) { attrib *ru; unit *u; plane * p = rplane(r); for (u = r->units; u; u = u->next) { if (u->faction == f) { f->first = r; return r; } } if (f->first == r->next) continue; for (ru = a_find(r->attribs, &at_travelunit); ru; ru = ru->nexttype) { u = (unit*)ru->data.v; if (u->faction == f) { f->first = r; return r; } } if (check_leuchtturm(r, f)) { f->first = r; return r; } if (p && is_watcher(p, f)) { f->first = r; return r; } } f->first = regions; return regions; } void ** blk_list[1024]; int list_index; int blk_index; void gc_done(void) { int i, k; for (i=0;i!=list_index;++i) { for (k=0;k!=1024;++k) free(blk_list[i][k]); free(blk_list[i]); } for (k=0;k!=blk_index;++k) free(blk_list[list_index][k]); free(blk_list[list_index]); } void * gc_add(void * p) { if (blk_index==0) { blk_list[list_index] = (void**)malloc(1024 * sizeof(void*)); } blk_list[list_index][blk_index] = p; blk_index = (blk_index+1) % 1024; if (!blk_index) ++ list_index; return p; } const char * findorder(const unit * u, const char * cmd) { strlist * o; char * c; for (o=u->orders;o;o=o->next) { if (!strcmp(cmd, o->s)) { if (o->s==cmd) return cmd; return o->s; } } c = strdup(cmd); gc_add(c); return c; } void use_birthdayamulet(region * r, unit * magician, strlist * cmdstrings) { region *tr; direction_t d; unused(cmdstrings); unused(magician); for(d=0;ddirections, LOC(lang, dirs[i].name), (void*)dirs[i].direction); } } direction_t finddirection(const char *s, const struct locale * lang) { struct lstr * lnames = get_lnames(lang); int dir; if (findtoken(&lnames->directions, s, (void**)&dir)==E_TOK_SUCCESS) { return (direction_t)dir; } return NODIRECTION; } static void init_tokens(const struct locale * lang) { struct lstr * lnames = get_lnames(lang); int i; const struct race * rc; init_directions(&lnames->directions, lang); for (rc=races;rc;rc=rc->next) { addtoken(&lnames->races, LOC(lang, rc_name(rc, 1)), (void*)rc); addtoken(&lnames->races, LOC(lang, rc_name(rc, 0)), (void*)rc); } for (i=0;i!=MAXPARAMS;++i) addtoken(&lnames->tokens[UT_PARAM], LOC(lang, parameters[i]), (void*)i); for (i=0;i!=MAXSKILLS;++i) addtoken(&lnames->skillnames, skillname((skill_t)i, lang), (void*)i); for (i=0;i!=MAXKEYWORDS;++i) addtoken(&lnames->keywords, LOC(lang, keywords[i]), (void*)i); for (i=0;i!=MAXOPTIONS;++i) addtoken(&lnames->options, LOC(lang, options[i]), (void*)i); #if 0 for (i=0;umlaut[i].txt;++i) addtoken(&lnames->tokens[umlaut[i].typ], umlaut[i].txt, (void*)umlaut[i].id); #endif } void kernel_done(void) { /* calling this function releases memory assigned to static variables, etc. * calling it is optional, e.g. a release server will most likely not do it. */ translation_done(); skill_done(); gc_done(); sql_done(); } static void read_strings(FILE * F) { char rbuf[8192]; while (fgets(rbuf, sizeof(rbuf), F)) { char * b = rbuf; locale * lang; char * key = b; char * language; const char * k; if (rbuf[0]=='#') continue; rbuf[strlen(rbuf)-1] = 0; /* \n weg */ while (*b && *b!=';') ++b; if (!*b) continue; *b++ = 0; language = b; while (*b && *b!=';') ++b; *b++ = 0; lang = find_locale(language); if (!lang) lang = make_locale(language); k = locale_getstring(lang, key); if (k) { log_warning(("Trying to register %s[%s]=\"%s\", already have \"%s\"\n", key, language, k, b)); } else locale_setstring(lang, key, b); } } const char * messages[] = { "%s/%s/strings.xml", "%s/%s/messages.xml", NULL }; const char * strings[] = { "%s/%s/strings.txt", NULL }; const char * locales[] = { "de", "en", NULL }; static int read_xml(const char * filename, struct xml_stack *stack); static int parse_tagbegin(struct xml_stack *stack) { const xml_tag * tag = stack->tag; if (strcmp(tag->name, "include")==0) { const char * filename = xml_value(tag, "file"); if (filename) { return read_xml(filename, stack); } else { log_printf("required tag 'file' missing from include"); return XML_USERERROR; } } else if (strcmp(tag->name, "game")==0) { const char * welcome = xml_value(tag, "welcome"); const char * name = xml_value(tag, "name"); int maxunits = xml_ivalue(tag, "units"); if (welcome!=NULL) { global.welcomepath = strdup(welcome); } if (name!=NULL) { global.gamename = strdup(name); } if (maxunits!=0) { global.maxunits = maxunits; } } else if (strcmp(tag->name, "order")==0) { const char * name = xml_value(tag, "name"); if (xml_bvalue(tag, "disable")) { int k; for (k=0;k!=MAXKEYWORDS;++k) { if (strncmp(keywords[k], name, strlen(name))==0) { global.disabled[k]=1; break; } } } } return XML_OK; } static xml_callbacks xml_eressea = { parse_tagbegin, NULL, NULL }; static int read_xml(const char * filename, xml_stack * stack) { char zText[80]; FILE * F; int i; sprintf(zText, "%s/%s", resourcepath(), filename); F = fopen(zText, "r+"); if (F==NULL) { log_printf("could not open %s: %s\n", zText, strerror(errno)); return XML_USERERROR; } i = xml_read(F, stack); fclose(F); return i; } int init_data(const char * filename) { int l; xml_register(&xml_eressea, "eressea", 0); xml_register(&xml_eressea, "eressea include", XML_CB_IGNORE); l = read_xml(filename, NULL); if (l) return l; /* old stuff, for removal: */ for (l=0;locales[l];++l) { char zText[MAX_PATH]; int i; for (i=0;strings[i];++i) { FILE * F; sprintf(zText, strings[i], resourcepath(), locales[l]); F = fopen(zText, "r+"); if (F) { read_strings(F); fclose(F); } else { sprintf(buf, "fopen(%s): ", zText); perror(buf); return 1; } } } return 0; } void init_locales(void) { int l; for (l=0;locales[l];++l) { const struct locale * lang = find_locale(locales[l]); if (lang) init_tokens(lang); } } /* TODO: soll hier weg */ extern building_type bt_caldera; extern attrib_type at_traveldir_new; attrib_type at_germs = { "germs", DEFAULT_INIT, DEFAULT_FINALIZE, DEFAULT_AGE, DEFAULT_WRITE, DEFAULT_READ, ATF_UNIQUE }; void attrib_init(void) { /* Alle speicherbaren Attribute müssen hier registriert werden */ at_register(&at_unitdissolve); at_register(&at_traveldir_new); at_register(&at_familiar); at_register(&at_familiarmage); at_register(&at_clone); at_register(&at_clonemage); at_register(&at_eventhandler); at_register(&at_stealth); at_register(&at_mage); at_register(&at_bauernblut); at_register(&at_countdown); at_register(&at_showitem); at_register(&at_curse); at_register(&at_cursewall); at_register(&at_seenspell); at_register(&at_reportspell); at_register(&at_deathcloud); /* neue REGION-Attribute */ at_register(&at_direction); at_register(&at_moveblock); #if AT_SALARY at_register(&at_salary); #endif at_register(&at_horseluck); at_register(&at_peasantluck); at_register(&at_deathcount); at_register(&at_chaoscount); at_register(&at_woodcount); at_register(&at_road); /* neue UNIT-Attribute */ at_register(&at_alias); at_register(&at_siege); at_register(&at_target); at_register(&at_potion); at_register(&at_potionuser); at_register(&at_contact); at_register(&at_effect); at_register(&at_private); at_register(&at_icastle); at_register(&at_guard); at_register(&at_lighthouse); at_register(&at_group); at_register(&at_faction_special); at_register(&at_prayer_timeout); at_register(&at_prayer_effect); at_register(&at_wyrm); at_register(&at_building_generic_type); /* border-typen */ register_bordertype(&bt_noway); register_bordertype(&bt_fogwall); register_bordertype(&bt_wall); register_bordertype(&bt_illusionwall); register_bordertype(&bt_firewall); register_bordertype(&bt_wisps); register_bordertype(&bt_road); register_bordertype(&bt_questportal); at_register(&at_jihad); at_register(&at_skillmod); #if GROWING_TREES at_register(&at_germs); #endif at_register(&at_laen); /* required for old datafiles */ at_register(&at_xontormiaexpress); /* required for old datafiles */ } void kernel_init(void) { char zBuffer[MAX_PATH]; skill_init(); attrib_init(); translation_init(); init_messages(); if (!turn) turn = lastturn(); if (turn == 0) srand(time((time_t *) NULL)); else srand(turn); sprintf(zBuffer, "%s/patch-%d.sql", datapath(), turn); sql_init(zBuffer); } /*********************/ /* at_guard */ /*********************/ attrib_type at_guard = { "guard", DEFAULT_INIT, DEFAULT_FINALIZE, DEFAULT_AGE, DEFAULT_WRITE, DEFAULT_READ, ATF_UNIQUE }; void setguard(unit * u, unsigned int flags) { /* setzt die guard-flags der Einheit */ attrib * a = a_find(u->attribs, &at_guard); if(flags == GUARD_NONE) { if(a) a_remove(&u->attribs, a); return; } if (!a) a = a_add(&u->attribs, a_new(&at_guard)); a->data.i = (int)flags; } unsigned int getguard(const unit * u) { attrib *a; if(u->region->terrain == T_OCEAN) return GUARD_NONE; a = a_find(u->attribs, &at_guard); if (a) return (unsigned int)a->data.i; return GUARD_NONE; } #ifndef HAVE_STRDUP char * strdup(const char *s) { return strcpy((char*)malloc(sizeof(char)*(strlen(s)+1)), s); } #endif void remove_empty_factions(boolean writedropouts) { faction **fp, *f3; FILE *dofp = NULL; char zText[MAX_PATH]; sprintf(zText, "%s/dropouts.%d", basepath(), turn); if (writedropouts) dofp = fopen(zText, "w"); for (fp = &factions; *fp;) { faction * f = *fp; /* monster (0) werden nicht entfernt. alive kann beim readgame * () auf 0 gesetzt werden, wenn monsters keine einheiten mehr * haben. */ if (f->alive == 0 && f->no != MONSTER_FACTION) { ursprung * ur = f->ursprung; while (ur && ur->id!=0) ur=ur->next; if (!quiet) printf("\t%s\n", factionname(f)); /* Einfach in eine Datei schreiben und später vermailen */ if (dofp) fprintf(dofp, "%s %s %d %d %d\n", f->email, LOC(default_locale, rc_name(f->race, 0)), f->age, ur?ur->x:0, ur?ur->y:0); if (updatelog) fprintf(updatelog, "dropout %s\n", itoa36(f->no)); for (f3 = factions; f3; f3 = f3->next) { ally * sf; group * g; ally ** sfp = &f3->allies; while (*sfp) { sf = *sfp; if (sf->faction == f || sf->faction == NULL) { *sfp = sf->next; free(sf); } else sfp = &(*sfp)->next; } for (g = f3->groups; g; g=g->next) { sfp = &g->allies; while (*sfp) { sf = *sfp; if (sf->faction == f || sf->faction == NULL) { *sfp = sf->next; free(sf); } else sfp = &(*sfp)->next; } } } fprintf(sqlstream, "UPDATE subscriptions set status='DEAD' where " "faction='%s' and game=%d\n;", itoa36(f->no), GAME_ID); *fp = f->next; /* stripfaction(f); * free(f); * Wir können die nicht löschen, weil sie evtl. noch in attributen * referenziert sind ! */ } else fp = &(*fp)->next; } if (dofp) fclose(dofp); } void remove_empty_units_in_region(region *r) { unit **up = &r->units; while (*up) { unit * u = *up; #ifdef MAXAGE faction * f = u->faction; if (f->age > MAXAGE) set_number(u, 0); #endif if ((u->number <= 0 && u->race != new_race[RC_SPELL]) || (u->age <= 0 && u->race == new_race[RC_SPELL]) || u->number < 0) { destroy_unit(u); } if (*up==u) up=&u->next; } } void remove_empty_units(void) { region *r; for (r = regions; r; r = r->next) { remove_empty_units_in_region(r); } } int *used_faction_ids = NULL; int no_used_faction_ids = 0; static int _cmp_int(const void *i1, const void *i2) { if(i2==NULL) return(*(int*)i1); return (*(int*)i1 - *(int *)i2); } void register_faction_id(int id) { no_used_faction_ids++; used_faction_ids = realloc(used_faction_ids, no_used_faction_ids*sizeof(int)); used_faction_ids[no_used_faction_ids-1] = id; if(no_used_faction_ids > 1) qsort(used_faction_ids, (size_t)no_used_faction_ids, sizeof(int), _cmp_int); } boolean faction_id_is_unused(int id) { #if 0 if(used_faction_ids==NULL) return(true); return (boolean)(bsearch(&id, used_faction_ids, no_used_faction_ids, sizeof(int), _cmp_int) == NULL); #else return true; #endif } int weight(const unit * u) { int w, n = 0, in_bag = 0; int faerie_level; item * itm; for (itm=u->items;itm;itm=itm->next) { w = itm->type->weight * itm->number; n += w; if( !fval(itm->type, ITF_BIG)) in_bag += w; } faerie_level = fspecial(u->faction, FS_FAERIE); if (faerie_level) { n += (u->number * u->race->weight)/(1+faerie_level); } else { n += u->number * u->race->weight; } w = get_item(u, I_BAG_OF_HOLDING) * BAGCAPACITY; if( w > in_bag ) w = in_bag; n -= w; return n; } void init_used_faction_ids(void) { faction *f; no_used_faction_ids = 0; for(f = factions; f; f = f->next) { register_faction_id(f->no); } } unit * make_undead_unit(region * r, faction * f, int n, const struct race * rc) { unit *u; u = createunit(r, f, n, rc); set_string(&u->lastorder, ""); name_unit(u); fset(u, UFL_ISNEW); return u; } void guard(unit * u, unsigned int mask) { int flags = GUARD_CREWS | GUARD_LANDING | GUARD_TRAVELTHRU | GUARD_TAX; #if GUARD_DISABLES_PRODUCTION == 1 flags |= GUARD_PRODUCE; #endif #if GUARD_DISABLES_RECRUIT == 1 flags |= GUARD_RECRUIT; #endif switch (old_race(u->race)) { case RC_ELF: if (u->faction->race != u->race) break; /* else fallthrough */ case RC_TREEMAN: flags |= GUARD_TREES; break; case RC_IRONKEEPER: flags = GUARD_MINING; break; } setguard(u, flags & mask); } int besieged(const unit * u) { /* belagert kann man in schiffen und burgen werden */ return (u && u->building && u->building->besieged && u->building->besieged >= u->building->size * SIEGEFACTOR); } int lifestyle(const unit * u) { static plane * astralspace = NULL; int need = u->number * u->race->maintenance; if (!astralspace) { astralspace = getplanebyname("Astralraum"); } /* Keinen Unterhalt im Astralraum. */ if (getplane(u->region) == astralspace) return 0; if(u->region->planep && fval(u->region->planep, PFL_NOFEED)) return 0; if(fspecial(u->faction, FS_REGENERATION)) need += 1; if(fspecial(u->faction, FS_ADMINISTRATOR)) need += 1; if(fspecial(u->faction, FS_WYRM) && old_race(u->race) == RC_WYRM) need *= 500; return need; } void hunger(unit * u, int need) { region * r = u->region; int dead = 0, hpsub = 0; int hp = u->hp / u->number; int lspp = lifestyle(u)/u->number; if(lspp <= 0) return; while(need > 0) { int dam = old_race(u->race)==RC_HALFLING?15+rand()%14:(13+rand()%12); if(dam >= hp) { ++dead; } else { hpsub += dam; } need -= lspp; } if (dead) { /* Gestorbene aus der Einheit nehmen, * Sie bekommen keine Beerdingung. */ add_message(&u->faction->msgs, new_message(u->faction, "starvation%u:unit%r:region%i:dead%i:live", u, r, dead, u->number-dead)); scale_number(u, u->number - dead); deathcounts(r, dead); } if(hpsub > 0) { /* Jetzt die Schäden der nicht gestorbenen abziehen. */ u->hp -= hpsub; /* Meldung nur, wenn noch keine für Tote generiert. */ if (dead == 0) { /* Durch unzureichende Ernährung wird %s geschwächt */ add_message(&u->faction->msgs, new_message(u->faction, "malnourish%u:unit%r:region", u, r)); } } if(dead || hpsub) { fset(u, FL_HUNGER); } } void plagues(region * r, boolean ismagic) { double prob; int peasants; int i; int gestorben; /* Vermeidung von DivByZero */ double mwp = max(maxworkingpeasants(r), 1); /* Seuchenwahrscheinlichkeit in % */ prob = pow((double) rpeasants(r) / (mwp * (((double)wage(r,NULL,false)) / 10.0) * 1.3), 4.0) * (double) SEUCHE; if (rand() % 100 >= (int)prob && !ismagic) return; peasants = rpeasants(r); for (i = peasants; i != 0; i--) { if (rand() % 100 < SEUCHENOPFER) { if (rand() % 100 < HEILCHANCE && rmoney(r) >= HEILKOSTEN) { rsetmoney(r, rmoney(r) - HEILKOSTEN); } else { peasants--; } } } gestorben = rpeasants(r) - peasants; if (gestorben > 0) { message * msg = add_message(&r->msgs, msg_message("pest", "dead", gestorben)); msg_release(msg); deathcounts(r, gestorben); } rsetpeasants(r, peasants); } /* Lohn bei den einzelnen Burgstufen für Normale Typen, Orks, Bauern, * Modifikation für Städter. */ #if LARGE_CASTLES static const int wagetable[7][4] = { {10, 10, 11, -7}, /* Baustelle */ {10, 10, 11, -5}, /* Handelsposten */ {11, 11, 12, -3}, /* Befestigung */ {12, 11, 13, -1}, /* Turm */ {13, 12, 14, 0}, /* Burg */ {14, 12, 15, 1}, /* Festung */ {15, 13, 16, 2} /* Zitadelle */ }; #else static const int wagetable[7][4] = { {10, 10, 11, -5}, /* Baustelle */ {11, 11, 12, -3}, /* Befestigung */ {12, 11, 13, -1}, /* Turm */ {13, 12, 14, 0}, /* Burg */ {14, 12, 15, 1}, /* Festung */ {15, 13, 16, 2} /* Zitadelle */ }; #endif int wage(const region *r, const unit *u, boolean img) /* Gibt Arbeitslohn für entsprechende Rasse zurück, oder für * die Bauern wenn ra == NORACE. */ { building *b = largestbuilding(r, img); int esize = 0; curse * c; int wage; attrib *a; static const curse_type * drought_ct, * blessedharvest_ct; static boolean init; if (!init) { init = true; drought_ct = ct_find("drought"); blessedharvest_ct = ct_find("blessedharvest"); } if (b) esize = buildingeffsize(b, img); if (u) { wage = wagetable[esize][u->race == new_race[RC_ORC] || u->race == new_race[RC_SNOTLING] || u->race == new_race[RC_URUK]]; if (fspecial(u->faction, FS_URBAN)) { wage += wagetable[esize][3]; } } else { if (rterrain(r) == T_OCEAN) { wage = 11; } else if (fval(r, RF_ORCIFIED)) { wage = wagetable[esize][1]; } else { wage = wagetable[esize][2]; } wage += curse_geteffect(get_curse(r->attribs, blessedharvest_ct)); } /* Godcurse: Income -10 */ if (curse_active(get_curse(r->attribs, ct_find("godcursezone")))) { wage = max(0,wage-10); } /* Bei einer Dürre verdient man nur noch ein Viertel */ if (drought_ct) { c = get_curse(r->attribs, drought_ct); if (curse_active(c)) wage /= curse_geteffect(c); } a = a_find(r->attribs, &at_reduceproduction); if (a) wage = (wage * a->data.sa[0])/100; return wage; } int fwage(const region *r, const faction *f, boolean img) { building *b = largestbuilding(r, img); int esize = 0; int wage; attrib *a; curse * c; if (b) esize = buildingeffsize(b, img); if (f) { wage = wagetable[esize][old_race(f->race) == RC_ORC]; if (fspecial(f, FS_URBAN)) { wage += wagetable[esize][3]; } } else { if (rterrain(r) == T_OCEAN) { wage = 11; } else if (fval(r, RF_ORCIFIED)) { wage = wagetable[esize][1]; } else { wage = wagetable[esize][2]; } wage += curse_geteffect(get_curse(r->attribs, ct_find("blessedharvest"))); } /* Godcurse: Income -10 */ if (curse_active(get_curse(r->attribs, ct_find("godcursezone")))) { wage = max(0,wage-10); } /* Bei einer Dürre verdient man nur noch ein Viertel */ c = get_curse(r->attribs, ct_find("drought")); if (curse_active(c)) wage /= curse_geteffect(c); a = a_find(r->attribs, &at_reduceproduction); if (a) wage = (wage * a->data.sa[0])/100; return wage; } region * findspecialdirection(const region *r, char *token) { attrib *a; spec_direction *d; for (a = a_find(r->attribs, &at_direction);a;a=a->nexttype) { d = (spec_direction *)(a->data.v); if(strncasecmp(d->keyword, token, strlen(token)) == 0) { return findregion(d->x, d->y); } } return NULL; } region * movewhere(region * r, const unit *u) { direction_t d; char *token; region * r2; token = getstrtoken(); d = finddirection(token, u->faction->locale); if (d == D_PAUSE) return r; if (d == NODIRECTION) return findspecialdirection(r, token); #if 0 /* NOT here! make a temporary attribute for this and move it into travel() */ if (is_cursed(r->attribs, C_REGCONF, 0)) { if (rand()%100 < get_curseeffect(r->attribs, C_REGCONF, 0)) { if(u->wants < 0) u->wants--; else if(u->wants > 0) u->wants++; else u->wants = rand()%2==0?1:-1; } } if (u->ship && is_cursed(u->ship->attribs, C_DISORIENTATION, 0)) { if (rand()%100 < get_curseeffect(r->attribs, C_DISORIENTATION, 0)) { if(u->wants < 0) u->wants--; else if(u->wants > 0) u->wants++; else u->wants = rand()%20?1:-1; } } d = (direction_t)((d + u->wants + MAXDIRECTIONS) % MAXDIRECTIONS); #endif if (!rconnect(r, d)) { #if USE_CREATION makeblock(r->x + delta_x[d], r->y + delta_y[d], 1); printf("* Fehler! Region (%d,%d) hatte seine Nachbarn " "(%d,%d) noch nicht generiert!\n", r->x, r->y, r->x + delta_x[d], r->y + delta_y[d]); #else add_message(&u->faction->msgs, msg_message("moveblocked", "unit direction", u, d)); return NULL; #endif } r2 = rconnect(r, d); if (!r2) { printf("* Fehler! Region (%d,%d) hatte seine Nachbarn " "(%d,%d) nicht gefunden!", r->x, r->y, r->x + delta_x[d], r->y + delta_y[d]); return 0; } if (move_blocked(u, r, d) == true) { add_message(&u->faction->msgs, msg_message("moveblocked", "unit direction", u, d)); return NULL; } /* r2 enthält nun die existierende Zielregion - ihre Nachbarn sollen * auch schon alle existieren. Dies erleichtert das Umherschauen bei * den Reports! */ #if USE_CREATION for (d = 0; d != MAXDIRECTIONS; d++) if (!rconnect(r2, d)) makeblock(r2->x + delta_x[d], r2->y + delta_y[d], 1); #endif return r2; } boolean move_blocked(const unit * u, const region *r, direction_t dir) { region * r2 = NULL; border * b; if (dirtype->block && b->type->block(b, u, r)) return true; b = b->next; } return false; } void add_income(unit * u, int type, int want, int qty) { if (want==INT_MAX) want = qty; add_message(&u->faction->msgs, new_message(u->faction, "income%u:unit%r:region%i:mode%i:wanted%i:amount", u, u->region, type, want, qty)); } int weeks_per_month; int months_per_year; int month(int offset) { int t = turn - FIRST_TURN + offset; int year, r, month; if (t<0) t = turn; year = t/(months_per_year * weeks_per_month) + 1; r = t - (year-1) * months_per_year * weeks_per_month; month = r/weeks_per_month; return month; } void reorder_owners(region * r) { unit ** up=&r->units, ** useek; building * b=NULL; ship * sh=NULL; #ifndef NDEBUG size_t len = listlen(r->units); #endif for (b = r->buildings;b;b=b->next) { unit ** ubegin = up; unit ** uend = up; useek = up; while (*useek) { unit * u = *useek; if (u->building==b) { unit ** insert; if (fval(u, FL_OWNER)) { unit * nu = *ubegin; insert=ubegin; if (nu!=u && nu->building==u->building && fval(nu, FL_OWNER)) { log_error(("[reorder_owners] %s hat mehrere Besitzer mit FL_OWNER.\n", buildingname(nu->building))); freset(nu, FL_OWNER); } } else insert = uend; if (insert!=useek) { *useek = u->next; /* raus aus der liste */ u->next = *insert; *insert = u; } if (insert==uend) uend=&u->next; } if (*useek==u) useek = &u->next; } up = uend; } useek=up; while (*useek) { unit * u = *useek; assert(!u->building); if (u->ship==NULL) { if (fval(u, FL_OWNER)) { log_warning(("[reorder_owners] Einheit %s war Besitzer von nichts.\n", unitname(u))); freset(u, FL_OWNER); } if (useek!=up) { *useek = u->next; /* raus aus der liste */ u->next = *up; *up = u; } up = &u->next; } if (*useek==u) useek = &u->next; } for (sh = r->ships;sh;sh=sh->next) { unit ** ubegin = up; unit ** uend = up; useek = up; while (*useek) { unit * u = *useek; if (u->ship==sh) { unit ** insert; if (fval(u, FL_OWNER)) { unit * nu = *ubegin; insert = ubegin; if (nu!=u && nu->ship==u->ship && fval(nu, FL_OWNER)) { log_error(("[reorder_owners] %s hat mehrere Besitzer mit FL_OWNER.\n", shipname(nu->ship))); freset(nu, FL_OWNER); } } else insert = uend; if (insert!=useek) { *useek = u->next; /* raus aus der liste */ u->next = *insert; *insert = u; } if (insert==uend) uend=&u->next; } if (*useek==u) useek = &u->next; } up = uend; } #ifndef NDEBUG assert(len==listlen(r->units)); #endif } int produceexp(struct unit * u, skill_t sk, int n) { if (n==0 || !playerrace(u->race)) return 0; learn_skill(u, sk, PRODUCEEXP/30.0); return 0; } boolean teure_talente (const struct unit * u) { if (has_skill(u, SK_MAGIC) || has_skill(u, SK_ALCHEMY) || has_skill(u, SK_TACTICS) || has_skill(u, SK_HERBALISM) || has_skill(u, SK_SPY)) { return true; } else { return false; } }