/* 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.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 /* kernel includes */ #include "alliance.h" #include "alchemy.h" #include "battle.h" #include "border.h" #include "building.h" #include "calendar.h" #include "curse.h" #include "faction.h" #include "group.h" #include "item.h" #include "karma.h" #include "magic.h" #include "message.h" #include "move.h" #include "names.h" #include "objtypes.h" #include "order.h" #include "plane.h" #include "pool.h" #include "race.h" #include "region.h" #include "save.h" #include "ship.h" #include "skill.h" #include "terrain.h" #include "unit.h" /* util includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* libxml includes */ #include #include /* libc includes */ #include #include #include #include #include #include #include #include #include /* exported variables */ region *regions; faction *factions; settings global; FILE *logfile; FILE *updatelog; const struct race * new_race[MAXRACES]; boolean sqlpatch = false; int turn; #ifdef XECMD_MODULE attrib_type at_xontormiaexpress = { "xontormiaexpress", DEFAULT_INIT, DEFAULT_FINALIZE, DEFAULT_AGE, a_writeint, a_readint, ATF_UNIQUE }; #endif int NewbieImmunity(void) { static int value = -1; if (value<0) { const char * str = get_param(global.parameters, "NewbieImmunity"); value = str?atoi(str):0; } return value; } static int MaxAge(void) { static int value = -1; if (value<0) { const char * str = get_param(global.parameters, "MaxAge"); value = str?atoi(str):0; } return value; } static int ally_flag(const char * s) { if (strcmp(s, "money")==0) return HELP_MONEY; if (strcmp(s, "fight")==0) return HELP_FIGHT; if (strcmp(s, "give")==0) return HELP_GIVE; if (strcmp(s, "guard")==0) return HELP_GUARD; if (strcmp(s, "stealth")==0) return HELP_FSTEALTH; if (strcmp(s, "travel")==0) return HELP_TRAVEL; return 0; } boolean ExpensiveMigrants(void) { int value = -1; if (value<0) { const char * str = get_param(global.parameters, "study.expensivemigrants"); value = str?atoi(str):0; } return value; } int AllianceAuto(void) { static int value = -1; if (value<0) { const char * str = get_param(global.parameters, "alliance.auto"); value = 0; if (str!=NULL) { char * sstr = strdup(str); char * tok = strtok(sstr, " "); while (tok) { value |= ally_flag(tok); tok = strtok(NULL, " "); } free(sstr); } } return value; } int AllianceRestricted(void) { static int value = -1; if (value<0) { const char * str = get_param(global.parameters, "alliance.restricted"); value = 0; if (str!=NULL) { char * sstr = strdup(str); char * tok = strtok(sstr, " "); while (tok) { value |= ally_flag(tok); tok = strtok(NULL, " "); } free(sstr); } } return value; } int LongHunger(const struct unit * u) { static int value = -1; if (u!=NULL) { if (!fval(u, UFL_HUNGER)) return false; #ifdef NEW_DAEMONHUNGER_RULE if (u->race==new_race[RC_DAEMON]) return false; #endif } if (value<0) { const char * str = get_param(global.parameters, "hunger.long"); value = str?atoi(str):0; } return value; } int SkillCap(skill_t sk) { static int value = -1; if (sk==SK_MAGIC) return 0; /* no caps on magic */ if (value<0) { const char * str = get_param(global.parameters, "skill.maxlevel"); value = str?atoi(str):0; } return value; } boolean TradeDisabled(void) { static int value = -1; if (value<0) { const char * str = get_param(global.parameters, "trade.disabled"); value = str?atoi(str):0; } return value; } int NMRTimeout(void) { static int value = -1; if (value<0) { const char * str = get_param(global.parameters, "nmr.timeout"); value = str?atoi(str):0; } return value; } 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" }; /** Returns the English name of the race, which is what the database uses. */ const char * dbrace(const struct race * rc) { static char zText[32]; char * zPtr = zText; /* the english names are all in ASCII, so we don't need to worry about UTF8 */ strcpy(zText, (const char*)LOC(find_locale("en"), rc_name(rc, 0))); while (*zPtr) { *zPtr = (char)(toupper(*zPtr)); ++zPtr; } return zText; } const char *parameters[MAXPARAMS] = { "LOCALE", "ALLES", "JEDEM", "BAUERN", "BURG", "EINHEIT", "PRIVAT", "HINTEN", "KOMMANDO", "KRAEUTER", "NICHT", "NAECHSTER", "PARTEI", "ERESSEA", "PERSONEN", "REGION", "SCHIFF", "SILBER", "STRASSEN", "TEMPORAERE", "FLIEHE", "GEBAEUDE", "GIB", /* Für HELFE */ "KAEMPFE", "DURCHREISE", "BEWACHE", "ZAUBER", "PAUSE", "VORNE", "AGGRESSIV", "DEFENSIV", "STUFE", "HELFE", "FREMDES", "AURA", "UM", "BEISTAND", "GNADE", "HINTER", "VOR", "ANZAHL", "GEGENSTAENDE", "TRAENKE", "GRUPPE", "PARTEITARNUNG", "BAEUME", "XEPOTION", "XEBALLOON", "XELAEN" }; const char *keywords[MAXKEYWORDS] = { "//", "BANNER", "ARBEITEN", "ATTACKIEREN", "BEKLAUEN", "BELAGERE", "BENENNEN", "BENUTZEN", "BESCHREIBEN", "BETRETEN", "BEWACHEN", "BOTSCHAFT", "ENDE", "FAHREN", "NUMMER", "KRIEG", "FRIEDEN", "FOLGEN", "FORSCHEN", "GIB", "HELFEN", "KAEMPFEN", "KAMPFZAUBER", "KAUFEN", "KONTAKTIEREN", "LEHREN", "LERNEN", "LIEFERE", "MACHEN", "NACH", "PASSWORT", "REKRUTIEREN", "RESERVIEREN", "ROUTE", "SABOTIEREN", "OPTION", "SPIONIEREN", "STIRB", "TARNEN", "TRANSPORTIEREN", "TREIBEN", "UNTERHALTEN", "VERKAUFEN", "VERLASSEN", "VERGESSEN", "ZAUBERE", "ZEIGEN", "ZERSTOEREN", "ZUECHTEN", "DEFAULT", "URSPRUNG", "EMAIL", "MEINUNG", "MAGIEGEBIET", "PIRATERIE", "NEUSTART", "GRUPPE", "OPFERE", "BETEN", "SORTIEREN", "JIHAD", "GM", "INFO", "PRAEFIX", "PFLANZEN", "WERWESEN", "XONTORMIA", "ALLIANZ", "BEANSPRUCHEN", #ifdef HEROES "PROMOTION", #endif }; 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", "XML" }; static int allied_skillcount(const faction * f, skill_t sk) { int num = 0; alliance * a = f->alliance; faction_list * members = a->members; while (members!=NULL) { num += count_skill(members->data, sk); members=members->next; } return num; } static int allied_skilllimit(const faction * f, skill_t sk) { static int value = -1; if (value<0) { const char * str = get_param(global.parameters, "alliance.skilllimit"); value = str?atoi(str):0; } return value; } static void init_maxmagicians(struct attrib *a) { a->data.i = MAXMAGICIANS; } static attrib_type at_maxmagicians = { "maxmagicians", init_maxmagicians, NULL, NULL, a_writeint, a_readint, ATF_UNIQUE }; static void init_npcfaction(struct attrib *a) { a->data.i = 1; } static attrib_type at_npcfaction = { "npcfaction", init_npcfaction, NULL, NULL, a_writeint, a_readint, ATF_UNIQUE }; int max_magicians(const faction * f) { int m = MAXMAGICIANS; attrib * a; if ((a = a_find(f->attribs, &at_maxmagicians)) != NULL) { m = a->data.i; } if (f->race == new_race[RC_ELF]) ++m; #ifdef KARMA_MODULE m += fspecial(f, FS_MAGOCRACY) * 2; #endif /* KARMA_MODULE */ return m; } int max_skill(faction * f, skill_t sk) { int m = INT_MAX; int al = allied_skilllimit(f, sk); if (al>0) { if (sk!=SK_ALCHEMY && sk!=SK_MAGIC) return INT_MAX; if (f->alliance!=NULL) { int ac = listlen(f->alliance->members); /* number of factions */ int fl = (al+ac-1)/ac; /* faction limit, rounded up */ /* the faction limit may not be achievable because it would break the alliance-limit */ int sc = al - allied_skillcount(f, sk); if (sc<=0) return 0; return fl; } } switch (sk) { case SK_MAGIC: m = max_magicians(f); 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_skill(faction * f, skill_t sk) { int n = 0; unit *u; for (u = f->units; u; u = u->nextF) { if (has_skill(u, sk)) { if (!is_familiar(u)) n += u->number; } } return n; } int quiet = 0; FILE *debug; int shipspeed (const ship * sh, const unit * u) { int k = sh->type->range; static const curse_type * stormwind_ct, * nodrift_ct; static boolean init; attrib *a; curse *c; 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 (u->faction->race == new_race[RC_AQUARIAN] && u->race == new_race[RC_AQUARIAN]) { k += 1; } a = a_find(sh->attribs, &at_speedup); while (a != NULL && a->type==&at_speedup) { k += a->data.sa[0]; a = a->next; } c = get_curse(sh->attribs, ct_find("shipspeedup")); while(c) { k += curse_geteffect(c); c = c->nexthash; } #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 k; } /* 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 2039 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) { /* TODO: inhalt auch löschen */ if (f->msgs) free_messagelist(f->msgs); while (f->battles) { struct bmsg * bm = f->battles; f->battles = bm->next; if (bm->msgs) free_messagelist(bm->msgs); free(bm); } freelist(f->allies); free(f->email); free(f->banner); free(f->passw); free(f->override); free(f->name); while (f->attribs) a_remove (&f->attribs, f->attribs); i_freeall(&f->items); freelist(f->ursprung); funhash(f); } 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 > UNIT_MAXSIZE) { if (lf != f->no) { lf = f->no; log_stdio(stdout, "Partei %s:\n", factionid(f)); } log_warning(("Einheit %s hat %d Personen\n", unitid(u), u->number)); } } if (f->no != 0 && ((mage > 3 && f->race != new_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 (rng_int() % 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; } unsigned int atoip(const char *s) { int n; n = atoi (s); if (n < 0) n = 0; return n; } 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 eff_stealth(const unit * u, const region * r) { int e; /* Auf Schiffen keine Tarnung! */ if (u->ship) return 0; e = eff_skill (u, SK_STEALTH, r); if (fval(u, UFL_STEALTH)) { int 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>0) { 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 ((rng_int() % u->number) < remain) ++u->hp; /* Nachkommastellen */ } else { remain = 0; u->hp = 0; } if (u->number>0) { for (a = a_find(u->attribs, &at_effect);a && a->type==&at_effect;a=a->next) { 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 ((rng_int() % u->number) < remain) ++snew; /* Nachkommastellen */ } data->value = snew; } } if (u->number==0 || n==0) { for (sk = 0; sk < MAXSKILLS; sk++) { 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; } static void init_gms(void) { faction * f; for (f=factions;f;f=f->next) { const attrib * a = a_findc(f->attribs, &at_gm); if (a!=NULL) fset(f, FFL_GM); } } static int autoalliance(const plane * pl, const faction * sf, const faction * f2) { static boolean init = false; if (!init) { init_gms(); init = true; } if (pl && (pl->flags & PFL_FRIENDLY)) return HELP_ALL; /* if f2 is a gm in this plane, everyone has an auto-help to it */ if (fval(f2, FFL_GM)) { attrib * a = a_find(f2->attribs, &at_gm); while (a) { const plane * p = (const plane*)a->data.v; if (p==pl) return HELP_ALL; a=a->next; } } if (sf->alliance!=NULL && AllianceAuto()) { if (sf->alliance==f2->alliance) return AllianceAuto(); } return 0; } static int ally_mode(const ally * sf, int mode) { if (sf==NULL) return 0; return sf->status & mode; } int alliedgroup(const struct plane * pl, const struct faction * f, const struct faction * f2, const struct ally * sf, int mode) { while (sf && sf->faction!=f2) sf=sf->next; if (sf==NULL) { mode = mode & autoalliance(pl, f, f2); } mode = ally_mode(sf, mode) | (mode & autoalliance(pl, f, f2)); if (AllianceRestricted()) { if (a_findc(f->attribs, &at_npcfaction)) { return mode; } if (a_findc(f2->attribs, &at_npcfaction)) { return mode; } if (f->alliance!=f2->alliance) { mode &= ~AllianceRestricted(); } } return mode; } int alliedfaction(const struct plane * pl, const struct faction * f, const struct faction * f2, int mode) { return alliedgroup(pl, f, f2, f->allies, mode); } /* Die Gruppe von Einheit u hat helfe zu f2 gesetzt. */ int alliedunit(const unit * u, const faction * f2, int mode) { ally * sf; const plane * pl = u->region->planep; int automode; if (u->faction == f2) return mode; if (u->faction == NULL || f2==NULL) return 0; automode = mode & autoalliance(pl, u->faction, f2); if (pl!=NULL && (pl->flags & PFL_NOALLIANCES)) mode = (mode & automode) | (mode & HELP_GIVE); sf = u->faction->allies; if (fval(u, UFL_GROUP)) { const attrib * a = a_findc(u->attribs, &at_group); if (a!=NULL) sf = ((group*)a->data.v)->allies; } return alliedgroup(pl, u->faction, f2, sf, mode); } boolean seefaction(const faction * f, const region * r, const unit * u, int modifier) { if (((f == u->faction) || !fval(u, UFL_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 stealth, rings; unit *u2 = r->units; static const item_type * itype_grail; static boolean init; if (!init) { init = true; itype_grail = it_find("grail"); } if (u->faction == f || omniscient(f)) { return true; } else if (u->race == new_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; } } if (leftship(u)) return true; if (itype_grail!=NULL && i_get(u->items, itype_grail)) return true; while (u2 && u2->faction != f) u2 = u2->next; if (u2==NULL) return false; /* simple visibility, just gotta have a unit in the region to see 'em */ if (getguard(u) || usiege(u) || u->building || u->ship) { return true; } stealth = eff_stealth(u, r) - modifier; rings = invisible(u, NULL); while (u2) { if (ringsnumber || invisible(u, u2) < u->number) { int observation = eff_skill(u2, SK_OBSERVATION, r); #ifdef NIGHTEYES if (u2->enchanted == SP_NIGHT_EYES && o < NIGHT_EYE_TALENT) observation = NIGHT_EYE_TALENT; #endif if (observation >= stealth) { return true; } } /* find next unit in our faction */ do { u2=u2->next; } while (u2 && u2->faction != f); } return false; } boolean cansee_unit(const unit * u, const unit * target, int modifier) /* target->region kann != u->region sein, wenn es um durchreisen geht */ { if (target->race == new_race[RC_SPELL] || target->number == 0) return false; else if (target->faction == u->faction) return true; else { int n, rings, o; if (getguard(target) || usiege(target) || target->building || target->ship) { return true; } n = eff_stealth(target, target->region) - modifier; rings = invisible(target, NULL); if (rings==0 && n<=0) { return true; } if (rings && invisible(target, u) >= target->number) { return false; } o = eff_skill(u, SK_OBSERVATION, target->region); #ifdef NIGHTEYES if (u->enchanted == SP_NIGHT_EYES && o < NIGHT_EYE_TALENT) o = NIGHT_EYE_TALENT; #endif if (o >= n) { return true; } } return false; } 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; unit *u2; if (u->race == new_race[RC_SPELL] || u->number == 0) return false; else if (u->faction == f) return true; else { int rings; if (getguard(u) || usiege(u) || u->building || u->ship) { return true; } n = eff_stealth(u, r) - modifier; rings = invisible(u, NULL); if (rings==0 && n<=0) { return true; } for (u2 = r->units; u2; u2 = u2->next){ if (u2->faction == f) { int o; if (rings && invisible(u, u2) >= u->number) continue; o = eff_skill(u2, SK_OBSERVATION, r); #ifdef NIGHTEYES if (u2->enchanted == SP_NIGHT_EYES && o < NIGHT_EYE_TALENT) o = NIGHT_EYE_TALENT; #endif if (o >= n) { return true; } } } } return false; } #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)); strlcpy(buffer, s, maxlen); return buffer; } return s; } #endif static 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) { static boolean init_lighthouse = false; static const struct building_type * bt_lighthouse = 0; if (!init_lighthouse) { bt_lighthouse = bt_find("lighthouse"); if (bt_lighthouse==NULL) return; init_lighthouse = true; } if (lh->type==bt_lighthouse) { region * r = lh->region; short d = (short)log10(lh->size) + 1; short x; if (lh->size>0) { r->flags |= RF_LIGHTHOUSE; } for (x=-d;x<=d;++x) { short y; for (y=-d;y<=d;++y) { attrib * a; region * r2 = findregion(x+r->x, y+r->y); if (r2==NULL) continue; if (!fval(r2->terrain, SEA_REGION)) continue; if (distance(r, r2) > d) continue; a = a_find(r2->attribs, &at_lighthouse); while (a && a->type==&at_lighthouse) { building * b = (building*)a->data.v; if (b==lh) break; a = a->next; } if (!a) { a = a_add(&r2->attribs, a_new(&at_lighthouse)); a->data.v = (void*)lh; } } } } } int count_all(const faction * f) { #ifndef NDEBUG int n = 0; unit *u; for (u=f->units;u;u=u->nextF) { if (playerrace(u->race)) { n += u->number; assert(f==u->faction); } } if (f->num_people != n) { log_error(("Anzahl Personen für (%s) ist != num_people: %d statt %d.\n", factionid(f), f->num_people, n)); return n; } #endif return f->num_people; } int count_migrants (const faction * f) { unit *u = f->units; int n = 0; while (u) { assert(u->faction == f); if (u->race != f->race && u->race != new_race[RC_ILLUSION] && u->race != new_race[RC_SPELL] && !!playerrace(u->race) && !(is_cursed(u->attribs, C_SLAVE, 0))) { n += u->number; } u = u->nextF; } return n; } int count_maxmigrants(const faction * f) { int x = 0; if (f->race == new_race[RC_HUMAN]) { int nsize = count_all(f); if (nsize>0) { x = (int)(log10(nsize / 50.0) * 20); if (x < 0) x = 0; } } return x; } void init_tokens(const struct order * ord) { char * cmd = getcommand(ord); init_tokens_str(cmd, cmd); } void parse(keyword_t kword, int (*dofun)(unit *, struct order *), boolean thisorder) { region *r; for (r = regions; r; r = r->next) { unit **up = &r->units; while (*up) { unit * u = *up; order ** ordp = &u->orders; if (thisorder) ordp = &u->thisorder; while (*ordp) { order * ord = *ordp; if (get_keyword(ord) == kword) { if (dofun(u, ord)!=0) break; if (u->orders==NULL) break; } if (thisorder) break; if (*ordp==ord) ordp=&ord->next; } if (*up==u) up=&u->next; } } } const char * igetstrtoken(const char * initstr) { if (initstr!=NULL) { init_tokens_str(initstr, NULL); } return getstrtoken(); } unsigned int getuint (void) { return atoip((const char *)getstrtoken()); } int getint (void) { return atoi((const char *)getstrtoken()); } const struct race * findrace(const char * s, const struct locale * lang) { struct tnode * tokens = get_translations(lang, UT_RACES); variant token; if (findtoken(tokens, s, &token)==E_TOK_SUCCESS) { return (const struct race *)token.v; } return NULL; } int findoption(const char *s, const struct locale * lang) { struct tnode * tokens = get_translations(lang, UT_OPTIONS); variant token; if (findtoken(tokens, s, &token)==E_TOK_SUCCESS) { return (direction_t)token.i; } return NODIRECTION; } skill_t findskill(const char *s, const struct locale * lang) { struct tnode * tokens = get_translations(lang, UT_SKILLS); variant token; if (findtoken(tokens, s, &token)==E_TOK_NOMATCH) return NOSKILL; return (skill_t)token.i; } keyword_t findkeyword(const char *s, const struct locale * lang) { struct tnode * tokens = get_translations(lang, UT_KEYWORDS); variant token; if (*s == '@') s++; if (findtoken(tokens, s, &token)==E_TOK_NOMATCH) return NOKEYWORD; if (global.disabled[token.i]) return NOKEYWORD; return (keyword_t) token.i; } param_t findparam(const char *s, const struct locale * lang) { struct tnode * tokens = get_translations(lang, UT_PARAMS); variant token; if (findtoken(tokens, s, &token)==E_TOK_NOMATCH) { const building_type * btype = findbuildingtype(s, lang); if (btype!=NULL) return (param_t) P_GEBAEUDE; return NOPARAM; } if (token.i==P_BUILDING) return P_GEBAEUDE; return (param_t)token.i; } param_t getparam (const struct locale * lang) { return findparam (getstrtoken (), lang); } faction * findfaction (int n) { faction * f = ffindhash(n); return f; } faction * getfaction (void) { return findfaction (getid()); } 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); } static 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) { const char * 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((const char *)s); } /* exported symbol */ boolean getunitpeasants; unit * getunitg(const region * r, const faction * f) { int n = read_unitid(f, r); if (n == 0) { getunitpeasants = 1; return NULL; } getunitpeasants = 0; if (n < 0) return 0; return findunit(n); } unit * getunit(const region * r, const faction * f) { int n = read_unitid(f, r); unit *u2; if (n == 0) { getunitpeasants = 1; return NULL; } getunitpeasants = 0; if (n < 0) return 0; u2 = findunit(n); if (u2!=NULL && u2->region==r) { /* there used to be a 'u2->flags & UFL_ISNEW || u2->number>0' condition * here, but it got removed because of a bug that made units disappear: * http://eressea.upb.de/mantis/bug_view_page.php?bug_id=0000172 */ return u2; } return NULL; } /* - String Listen --------------------------------------------- */ void addstrlist (strlist ** SP, const char *s) { strlist * slist = malloc(sizeof(strlist)); slist->next = NULL; slist->s = strdup(s); addlist(SP, slist); } void freestrlist (strlist * s) { strlist *q, *p = s; while (p) { q = p->next; free(p->s); free(p); p = q; } } /* - Meldungen und Fehler ------------------------------------------------- */ boolean lomem = false; /* - Namen der Strukturen -------------------------------------- */ typedef char name[OBJECTIDSIZE+1]; static name idbuf[8]; static int nextbuf = 0; char * estring_i(char *ibuf) { char *p = ibuf; while (*p) { if (isspace(*(unsigned*)p) == ' ') { *p = '~'; } ++p; } return ibuf; } char * estring(const char *s) { char *ibuf = idbuf[(++nextbuf) % 8]; strlcpy(ibuf, s, sizeof(name)); return estring_i(ibuf); } char * cstring_i(char *ibuf) { char *p = ibuf; while (*p) { if (*p == '~') { *p = ' '; } ++p; } return ibuf; } char * cstring(const char *s) { char *ibuf = idbuf[(++nextbuf) % 8]; strlcpy(ibuf, s, sizeof(name)); return cstring_i(ibuf); } 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; } char * write_unitname(const unit * u, char * buffer, size_t size) { snprintf((char*)buffer, size, "%s (%s)", (const char*)u->name, itoa36(u->no)); buffer[size-1] = 0; return buffer; } const char * unitname(const unit * u) { char *ubuf = idbuf[(++nextbuf) % 8]; return write_unitname(u, ubuf, sizeof(name)); } /* -- Erschaffung neuer Einheiten ------------------------------ */ extern faction * dfindhash(int i); static 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 + (rng_int() % 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 + (rng_int() % 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; } unit * createunit(region * r, faction * f, int number, const struct race * rc) { assert(rc); return create_unit(r, f, number, rc, 0, NULL, NULL); } boolean idle (faction * f) { return (boolean) (f ? false : true); } int maxworkingpeasants(const struct region * r) { int i = production(r) * MAXPEASANTS_PER_AREA - ((rtrees(r,2)+rtrees(r,1)/2) * TREESIZE); return max(i, 0); } unit_list * get_lighthouses(const region * r) { attrib * a; unit_list * ulist = NULL; if (!fval(r->terrain, SEA_REGION)) return NULL; for (a = a_find(r->attribs, &at_lighthouse);a && a->type==&at_lighthouse;a=a->next) { building *b = (building *)a->data.v; region *r2 = b->region; if (fval(b, BLD_WORKING) && b->size >= 10) { boolean c = false; unit *u; int d = distance(r, r2); int maxd = (int)log10(b->size) + 1; if (maxd < d) break; for (u = r2->units; u; u = u->next) { if (u->building == b) { c = true; if (c > buildingcapacity(b)) break; if (eff_skill(u, SK_OBSERVATION, r) >= d * 3) { unitlist_insert(&ulist, u); } } else if (c) break; /* first unit that's no longer in the house ends the search */ } } } return ulist; } int lighthouse_range(const building * b, const faction * f) { int d = 0; if (fval(b, BLD_WORKING) && b->size >= 10) { region * r = b->region; unit *u; int maxd = (int)log10(b->size) + 1; int c = 0; for (u = r->units; u; u = u->next) { if (u->building == b) { c += u->number; if (c > buildingcapacity(b)) break; if (f==NULL || u->faction == f) { int sk = eff_skill(u, SK_OBSERVATION, r) / 3; d = max(d, sk); d = min(maxd, d); if (d==maxd) break; } } else if (c) break; /* first unit that's no longer in the house ends the search */ } } return d; } boolean check_leuchtturm(region * r, faction * f) { attrib * a; if (!fval(r->terrain, SEA_REGION)) return false; for (a = a_find(r->attribs, &at_lighthouse);a && a->type==&at_lighthouse;a=a->next) { 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) { #ifdef SMART_INTERVALS unit * u = f->units; region *r = f->last; if (u==NULL) return NULL; if (r!=NULL) return r->next; /* it is safe to start in the region of the first unit. */ f->last = u->region; /* if regions have indices, we can skip ahead: */ for (u=u->nextF; u!=NULL; u=u->nextF) { r = u->region; if (r->index > f->last->index) f->last = r; } /* we continue from the best region and look for travelthru etc. */ for (r = f->last->next; r; r = r->next) { plane * p = rplane(r); /* search the region for travelthru-attributes: */ if (fval(r, RF_TRAVELUNIT)) { attrib * ru = a_find(r->attribs, &at_travelunit); while (ru && ru->type==&at_travelunit) { u = (unit*)ru->data.v; if (u->faction == f) { f->last = r; break; } ru = ru->next; } } if (f->last == r) continue; if (check_leuchtturm(r, f)) f->last = r; if (p && is_watcher(p, f)) { f->last = r; } } return f->last->next; #else return NULL; #endif } region * firstregion (faction * f) { #ifdef SMART_INTERVALS region *r = f->first; if (f->units==NULL) return NULL; if (r!=NULL) return r; return f->first = regions; #else return regions; #endif } void ** blk_list[1024]; int list_index; int blk_index; static 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; } static void init_directions(tnode * root, const struct locale * lang) { /* mit dieser routine kann man mehrere namen für eine direction geben, * das ist für die hexes ideal. */ const struct { const char* name; int direction; } dirs [] = { { "dir_ne", D_NORTHEAST}, { "dir_nw", D_NORTHWEST}, { "dir_se", D_SOUTHEAST}, { "dir_sw", D_SOUTHWEST}, { "dir_east", D_EAST}, { "dir_west", D_WEST}, { "northeast", D_NORTHEAST}, { "northwest", D_NORTHWEST}, { "southeast", D_SOUTHEAST}, { "southwest", D_SOUTHWEST}, { "east", D_EAST }, { "west",D_WEST }, { "PAUSE", D_PAUSE }, { NULL, NODIRECTION} }; int i; struct tnode * tokens = get_translations(lang, UT_DIRECTIONS); for (i=0; dirs[i].direction!=NODIRECTION;++i) { variant token; token.i = dirs[i].direction; addtoken(tokens, LOC(lang, dirs[i].name), token); } } direction_t finddirection(const char *s, const struct locale * lang) { struct tnode * tokens = get_translations(lang, UT_DIRECTIONS); variant token; if (findtoken(tokens, s, &token)==E_TOK_SUCCESS) { return (direction_t)token.i; } return NODIRECTION; } static void init_locale(const struct locale * lang) { variant var; int i; const struct race * rc; struct tnode * tokens; const terrain_type * terrain; tokens = get_translations(lang, UT_MAGIC); for (i=0;i!=MAXMAGIETYP;++i) { var.i = i; addtoken(tokens, LOC(lang, mkname("school", magietypen[i])), var); } tokens = get_translations(lang, UT_DIRECTIONS); init_directions(tokens, lang); tokens = get_translations(lang, UT_RACES); for (rc=races;rc;rc=rc->next) { var.v = (void*)rc; addtoken(tokens, LOC(lang, rc_name(rc, 1)), var); addtoken(tokens, LOC(lang, rc_name(rc, 0)), var); } tokens = get_translations(lang, UT_PARAMS); for (i=0;i!=MAXPARAMS;++i) { var.i = i; addtoken(tokens, LOC(lang, parameters[i]), var); } tokens = get_translations(lang, UT_SKILLS); for (i=0;i!=MAXSKILLS;++i) { if (i!=SK_TRADE || !TradeDisabled()) { const char * skname = skillname((skill_t)i, lang); if (skname!=NULL) { var.i = i; addtoken(tokens, skname, var); } } } tokens = get_translations(lang, UT_KEYWORDS); for (i=0;i!=MAXKEYWORDS;++i) { var.i = i; addtoken(tokens, LOC(lang, keywords[i]), var); } tokens = get_translations(lang, UT_OPTIONS); for (i=0;i!=MAXOPTIONS;++i) { var.i = i; addtoken(tokens, LOC(lang, options[i]), var); } tokens = get_translations(lang, UT_TERRAINS); for (terrain=terrains();terrain!=NULL;terrain=terrain->next) { var.v = (void*)terrain; addtoken(tokens, LOC(lang, terrain->_name), var); } } typedef struct param { struct param * next; char * name; char * data; } param; const char * get_param(const struct param * p, const char * key) { while (p!=NULL) { if (strcmp(p->name, key)==0) return p->data; p = p->next; } return NULL; } void set_param(struct param ** p, const char * key, const char * data) { while (*p!=NULL) { if (strcmp((*p)->name, key)==0) { free((*p)->data); (*p)->data = strdup(data); return; } p=&(*p)->next; } *p = malloc(sizeof(param)); (*p)->name = strdup(key); (*p)->data = strdup(data); (*p)->next = NULL; } 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(); } const char * localenames[] = { "de", "en", NULL }; int init_data(const char * filename) { int l; char zText[80]; sprintf(zText, "%s/%s", resourcepath(), filename); l = read_xml(zText); if (l) return l; if (turn=ST_AGGRO && status<=ST_FLEE); if (u->status!=status) { u->status = (status_t)status; #ifdef SIMPLE_ESCAPE if (u->status==ST_FLEE) { setguard(u, GUARD_NONE); } #endif } } void setguard(unit * u, unsigned int flags) { /* setzt die guard-flags der Einheit */ attrib * a = NULL; assert(flags==0 || !fval(u, UFL_MOVED)); #ifdef SIMPLE_ESCAPE assert(flags==0 || u->statusattribs, &at_guard); } if (flags == GUARD_NONE) { freset(u, UFL_GUARD); if (a) a_remove(&u->attribs, a); return; } fset(u, UFL_GUARD); fset(u->region, RF_GUARDED); if (!a) a = a_add(&u->attribs, a_new(&at_guard)); a->data.i = (int)flags; } unsigned int getguard(const unit * u) { if (!fval(u->region->terrain, LAND_REGION)) return GUARD_NONE; if (fval(u, UFL_GUARD)) { attrib * 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"); if (dofp) { const unsigned char utf8_bom[4] = { 0xef, 0xbb, 0xbf }; fwrite(utf8_bom, 1, 3, dofp); } 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->units==NULL || f->alive == 0) && f->no != MONSTER_FACTION) { ursprung * ur = f->ursprung; while (ur && ur->id!=0) ur=ur->next; if (!quiet) log_stdio(stdout, "\t%s\n", factionname(f)); /* Einfach in eine Datei schreiben und später vermailen */ if (dofp) { fprintf(dofp, "%3d %s %s %s\n", f->age, LOC(default_locale, rc_name(f->race, 0)), f->email, factionname(f)); } 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; } } } if (f->subscription) { sql_print(("UPDATE subscriptions set status='DEAD' where id=%u;\n", f->subscription)); } *fp = f->next; } 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; if (u->number) { faction * f = u->faction; if (f==NULL || !f->alive) { set_number(u, 0); } if (MaxAge()>0) { if ((!fval(f, FFL_NOTIMEOUT) && f->age > MaxAge())) { set_number(u, 0); } } } if ((u->number == 0 && u->race != new_race[RC_SPELL]) || (u->age <= 0 && u->race == new_race[RC_SPELL])) { if (u->faction!=NULL) destroy_unit(u); if (u->number==0) remove_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); } } boolean faction_id_is_unused(int id) { return findfaction(id)==NULL && id!=MONSTER_FACTION; } int weight(const unit * u) { int w, n = 0, in_bag = 0; int faerie_level = 0; 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; } #ifdef KARMA_MODULE faerie_level = fspecial(u->faction, FS_FAERIE); #endif /* KARMA_MODULE */ 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 make_undead_unit(unit * u) { #ifdef LASTORDER set_order(&u->lastorder, NULL); #endif free_orders(&u->orders); name_unit(u); fset(u, UFL_ISNEW); } 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; if (u->faction->no == MONSTER_FACTION) return 0; need = maintenance_cost(u); if (!astralspace) { astralspace = getplanebyname("Astralraum"); } if(u->region->planep && fval(u->region->planep, PFL_NOFEED)) return 0; #ifdef KARMA_MODULE if(fspecial(u->faction, FS_REGENERATION)) need += 1; if(fspecial(u->faction, FS_ADMINISTRATOR)) need += 1; if(fspecial(u->faction, FS_WYRM) && u->race == new_race[RC_WYRM]) need *= 500; #endif /* KARMA_MODULE */ return need; } boolean hunger(int number, unit * u) { region * r = u->region; int dead = 0, hpsub = 0; int hp = u->hp / u->number; while (number--) { int dam = u->race==new_race[RC_HALFLING]?15+rng_int()%14:(13+rng_int()%12); if (dam >= hp) { ++dead; } else { hpsub += dam; } } if (dead) { /* Gestorbene aus der Einheit nehmen, * Sie bekommen keine Beerdingung. */ ADDMSG(&u->faction->msgs, msg_message("starvation", "unit region dead 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 */ ADDMSG(&u->faction->msgs, msg_message("malnourish", "unit region", u, r)); } } return (dead || hpsub); } void plagues(region * r, boolean ismagic) { int peasants; int i; int dead = 0; /* Seuchenwahrscheinlichkeit in % */ if (!ismagic) { double mwp = max(maxworkingpeasants(r), 1); double prob = pow(rpeasants(r) / (mwp * wage(r, NULL, NULL) * 0.13), 4.0) * PLAGUE_CHANCE; if (rng_double() >= prob) return; } peasants = rpeasants(r); dead = (int)(0.5F + PLAGUE_VICTIMS * peasants); for (i = dead; i != 0; i--) { if (rng_double() < PLAGUE_HEALCHANCE && rmoney(r) >= PLAGUE_HEALCOST) { rsetmoney(r, rmoney(r) - PLAGUE_HEALCOST); } else { --dead; } } if (dead > 0) { message * msg = add_message(&r->msgs, msg_message("pest", "dead", dead)); msg_release(msg); deathcounts(r, dead); rsetpeasants(r, peasants - dead); } } /* Lohn bei den einzelnen Burgstufen für Normale Typen, Orks, Bauern, * Modifikation für Städter. */ 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 */ }; static int default_wage(const region *r, const faction * f, const race * rc) { building *b = largestbuilding(r, false); int esize = 0; curse * c; int wage; attrib *a; const building_type *artsculpture_type = bt_find("artsculpture"); 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!=NULL) { /* TODO: this reveals imaginary castles */ esize = buildingeffsize(b, false); } if (f!=NULL) { int index = 0; if (rc==new_race[RC_ORC] || rc==new_race[RC_URUK] || rc==new_race[RC_SNOTLING]) { index = 1; } wage = wagetable[esize][index]; #ifdef KARMA_MODULE if (fspecial(f, FS_URBAN)) { wage += wagetable[esize][3]; } #endif /* KARMA_MODULE */ } else { if (fval(r->terrain, SEA_REGION)) { 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)); } /* Artsculpture: Income +5 */ for(b=r->buildings; b; b=b->next) { if(b->type == artsculpture_type) { wage += 5; } } /* 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; } /* Gibt Arbeitslohn für entsprechende Rasse zurück, oder für * die Bauern wenn f == NULL. */ int wage(const region *r, const faction * f, const race * rc) { if (global.functions.wage) { return global.functions.wage(r, f, rc); } return default_wage(r, f, rc); } #define MAINTENANCE 10 int maintenance_cost(const struct unit * u) { if (u==NULL) return MAINTENANCE; if (global.functions.maintenance) { int retval = global.functions.maintenance(u); if (retval>=0) return retval; } return u->race->maintenance * u->number; } message * movement_error(unit * u, const char * token, order * ord, int error_code) { direction_t d; switch (error_code) { case E_MOVE_BLOCKED: d = finddirection(token, u->faction->locale); return msg_message("moveblocked", "unit direction", u, d); case E_MOVE_NOREGION: return msg_feedback(u, ord, "unknowndirection", "dirname", token); } return NULL; } int movewhere(const unit *u, const char * token, region * r, region** resultp) { region * r2; direction_t d; if (*token == '\0') { *resultp = NULL; return E_MOVE_OK; } d = finddirection(token, u->faction->locale); switch (d) { case D_PAUSE: *resultp = r; break; case NODIRECTION: r2 = find_special_direction(r, token, u->faction->locale); if (r2==NULL) { return E_MOVE_NOREGION; } *resultp = r2; break; default: r2 = rconnect(r, d); if (r2==NULL || move_blocked(u, r, r2)) { return E_MOVE_BLOCKED; } *resultp = r2; } return E_MOVE_OK; } boolean move_blocked(const unit * u, const region *r, const region *r2) { border * b; curse * c; static const curse_type * fogtrap_ct = NULL; if (r2==NULL) return true; b = get_borders(r, r2); while (b) { if (b->type->block && b->type->block(b, u, r)) return true; b = b->next; } if (fogtrap_ct==NULL) fogtrap_ct = ct_find("fogtrap"); c = get_curse(r->attribs, fogtrap_ct); if (curse_active(c)) return true; return false; } void add_income(unit * u, int type, int want, int qty) { if (want==INT_MAX) want = qty; ADDMSG(&u->faction->msgs, msg_message("income", "unit region mode wanted amount", u, u->region, type, want, qty)); } 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, UFL_OWNER)) { unit * nu = *ubegin; insert = ubegin; if (nu!=u && nu->building==u->building && fval(nu, UFL_OWNER)) { log_error(("[reorder_owners] %s hat mehrere Besitzer mit UFL_OWNER.\n", buildingname(nu->building))); freset(nu, UFL_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, UFL_OWNER)) { log_warning(("[reorder_owners] Einheit %s war Besitzer von nichts.\n", unitname(u))); freset(u, UFL_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, UFL_OWNER)) { unit * nu = *ubegin; insert = ubegin; if (nu!=u && nu->ship==u->ship && fval(nu, UFL_OWNER)) { log_error(("[reorder_owners] %s hat mehrere Besitzer mit UFL_OWNER.\n", shipname(nu->ship))); freset(nu, UFL_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 (global.producexpchance>0.0F) { if (n==0 || !playerrace(u->race)) return 0; learn_skill(u, sk, global.producexpchance); } return 0; } int lovar(double xpct_x2) { int n = (int)(xpct_x2 * 500)+1; if (n==0) return 0; return (rng_int() % n + rng_int() % n)/1000; } 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; } } void attrib_init(void) { /* Alle speicherbaren Attribute müssen hier registriert werden */ at_register(&at_shiptrail); 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_countdown); at_register(&at_curse); at_register(&at_seenspell); /* neue REGION-Attribute */ at_register(&at_direction); at_register(&at_moveblock); at_register(&at_deathcount); at_register(&at_chaoscount); at_register(&at_woodcount); /* neue UNIT-Attribute */ at_register(&at_siege); at_register(&at_effect); at_register(&at_private); at_register(&at_icastle); at_register(&at_guard); at_register(&at_group); #ifdef KARMA_MODULE at_register(&at_faction_special); at_register(&at_prayer_timeout); at_register(&at_wyrm); at_register(&at_jihad); #endif /* KARMA_MODULE */ at_register(&at_building_generic_type); at_register(&at_maxmagicians); at_register(&at_npcfaction); /* border-typen */ register_bordertype(&bt_noway); register_bordertype(&bt_fogwall); register_bordertype(&bt_wall); register_bordertype(&bt_illusionwall); register_bordertype(&bt_road); register_bordertype(&bt_questportal); at_register(&at_germs); #ifdef XECMD_MODULE at_register(&at_xontormiaexpress); /* required for old datafiles */ #endif at_register(&at_speedup); at_register(&at_building_action); } void kernel_init(void) { char zBuffer[MAX_PATH]; skill_init(); attrib_init(); translation_init(); if (!turn) turn = lastturn(); if (turn == 0) rng_init((int)time(0)); else rng_init(turn); if (sqlpatch) { sprintf(zBuffer, "%s/patch-%d.sql", datapath(), turn); sql_init(zBuffer); } } order * default_order(const struct locale * lang) { return parse_order(locale_string(lang, "defaultorder"), lang); } int entertainmoney(const region *r) { int n; if (is_cursed(r->attribs, C_DEPRESSION, 0)) { return 0; } n = rmoney(r) / ENTERTAINFRACTION; if (is_cursed(r->attribs, C_GENEROUS, 0)) { n *= get_curseeffect(r->attribs, C_GENEROUS, 0); } return n; }