/* vi: set ts=2: * * $Id: report.c,v 1.11 2001/02/05 16:11:57 enno Exp $ * 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. */ #define FAST_SEEN 0 #define INDENT 0 #include #include "eressea.h" #include "reports.h" #include "item.h" #include "alchemy.h" #include "faction.h" #include "region.h" #include "unit.h" #include "skill.h" #include "movement.h" #include "message.h" #include "ship.h" #include "building.h" #include "economy.h" #include "build.h" #include "creation.h" #include "save.h" #include "build.h" #include "magic.h" #include "race.h" #include "monster.h" #include "plane.h" #include "pool.h" #include "teleport.h" #include "border.h" #include "objtypes.h" #include "goodies.h" #include "karma.h" #include "render.h" #ifdef GROUPS #include "group.h" #endif #include /* util includes */ #include /* libc includes */ #include #include #include #include #include #include #include extern int quiet; boolean nocr = false; boolean nonr = false; boolean nomer = false; /* * ------------------------------------------------------------- */ extern int *storms; char **seasonnames; char **weeknames; char **weeknames2; char **monthnames; int *season; char *agename; int seasons; extern int weeks_per_month; extern int months_per_year; int read_datenames(const char *filename) { FILE *namesFP; char line[256]; int i, l; if( (namesFP=fopen(filename,"r")) == NULL) { fprintf(stderr, "Kann Datei '%s' nicht öffnen, Abbruch\n",filename); return -1; } fgets(line,255,namesFP); l = strlen(line)-1; if(line[l] == '\n') line[l] = 0; agename = strdup(line); fgets(line,255,namesFP); seasons = strtol(line, NULL, 10); seasonnames = malloc(sizeof(char *) * seasons); for (i=0;i=FIRST_TURN) { sprintf(buf, "Wir schreiben %s des Monats %s im Jahre %d %s.", weeknames[week], monthnames[month], year, agename); } else { sprintf(buf, "Wir schreiben %s des Monats %s im Jahre %d %s.", weeknames[week], monthnames[month], year, "der alten Zeitrechnung"); } return buf; } char * gamedate2(void) { int year,month,week,r; static char buf[256]; int t = turn - FIRST_TURN; 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; /* 0 - months_per_year-1 */ week = r%weeks_per_month; /* 0 - weeks_per_month-1 */ if (turn>=FIRST_TURN) { sprintf(buf, "in %s des Monats %s im Jahre %d %s.", weeknames2[week], monthnames[month], year, agename); } else { sprintf(buf, "in %s des Monats %s im Jahre %d %s.", weeknames2[week], monthnames[month], year, "der alten Zeitrechnung"); } return buf; } char * gamedate_short(void) { int year,month,week,r; static char buf[256]; int t = turn - FIRST_TURN; 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; /* 0 - months_per_year-1 */ week = r%weeks_per_month; /* 0 - weeks_per_month-1 */ sprintf(buf, "%d/%s/%d", week+1, monthnames[month], year); return buf; } /* ------------------------------------------------------------- */ static void rpsnr(FILE * F, const char * s, int offset) { char inset[REPORTWIDTH]; const char *l, *x = s; char ui=0; size_t indent = 0, len; /* Geht nicht: Privatbeschreibungen der Monster enthalten * \\r-Koordinaten ! */ /* assert(!strstr(s, "\\r(") || !"error: regionid not translated"); */ len = strlen(s); while (*x++ == ' '); indent = x - s - 1; if (*(x - 1) && indent && *x == ' ') indent += 2; if (!indent) indent = offset; x = s; memset(inset, 32, indent * sizeof(char)); inset[indent] = 0; while (s <= x+len) { size_t line = min(len-(s-x), REPORTWIDTH - indent*ui); char * br = strchr(s, '\n'); l = s + line; if(br && br < l) { l = br; } else { if (*l) { while (*l!=' ' && l!=s) --l; /* wenn nicht gefunden, harte breaks: */ if (s == l) l = s + REPORTWIDTH - indent; } } if (*s) { #if INDENT putc(' ', F); #endif if (s!=x) { fputs(inset, F); } fwrite(s, sizeof(char), l-s, F); putc('\n', F); } s = l+1; ui=1; } } static faction *current_faction = NULL; int outi; char outbuf[4096]; void rpc(FILE * F, char c, size_t num) { while(num > 0) { putc(c, F); num--; } } void rnl(FILE * F) { int i; int rc, vc; i = outi; assert(i < 4096); while (i && isspace((int)outbuf[i - 1])) i--; outbuf[i] = 0; i = 0; rc = 0; vc = 0; while (outbuf[i]) { switch (outbuf[i]) { case ' ': vc++; break; case '\t': vc = (vc & ~7) + 8; break; default: /* ER: Tabs in Reports sind Mist. Die versauen die * * Anzeige von Einheiten in Burgen und Schiffen. while * (rc / 8 != vc / 8) { if ((rc & 7) == 7) putc(' ', * F); else putc('\t', F); rc = (rc & ~7) + 8; } */ while (rc != vc) { putc(' ', F); rc++; } putc(outbuf[i], F); rc++; vc++; } i++; assert(i < 4096); } putc('\n', F); outi = 0; } static void rps(FILE * F, const char * src) { char * s; if (strstr(src, "\\r(")) s = replace_global_coords(src, current_faction); else s = strcpy(buf, src); rpsnr(F, s, 0); } static void centre(FILE * F, const char *s, boolean breaking) { /* Bei Namen die genau 80 Zeichen lang sind, kann es hier Probleme * geben. Seltsamerweise wird i dann auf MAXINT oder aehnlich * initialisiert. Deswegen keine Strings die laenger als REPORTWIDTH * sind! */ if (breaking && REPORTWIDTH < strlen(s)) { strlist *T, *SP = 0; sparagraph(&SP, s, 0, 0); T = SP; while (SP) { centre(F, SP->s, false); SP = SP->next; } freestrlist(T); } else { rpc(F, ' ', (REPORTWIDTH - strlen(s)+1) / 2); fputs(s, F); putc('\n', F); } } static void rparagraph(FILE *F, const char *s, int indent, char mark) { static char mbuf[BUFSIZE+1]; /* static size_t bsize = 0; */ size_t size; char inset[REPORTWIDTH]; if (indent) { memset(inset, ' ', indent); inset[indent]=0; if (mark) inset[indent - 2] = mark; } else { assert(mark == 0); inset[0] = 0; } inset[indent]=0; size = strlen(s)+indent+1; if (size==1) return; strcpy(mbuf, inset); strncpy(mbuf+indent, s, BUFSIZE-indent); *(mbuf+size-1)=0; rps(F, mbuf); } static void report_spell(FILE * F, spellid_t id) { int k, itemanz, res, costtyp; int dh = 0; spell *sp = find_spellbyid(id); rnl(F); centre(F, sp->name, true); rnl(F); strcpy(buf,"Beschreibung:"); rps(F, buf); rparagraph(F, sp->beschreibung, 0, 0); rnl(F); strcpy(buf, "Art: "); if (sp->sptyp & PRECOMBATSPELL) { scat("Präkampfzauber"); } else if (sp->sptyp & COMBATSPELL) { scat("Kampfzauber"); } else if (sp->sptyp & POSTCOMBATSPELL) { scat("Postkampfzauber"); } else { scat("Normaler Zauber"); } rps(F, buf); strcpy(buf, "Komponenten:"); rps(F, buf); for (k = 0; k < MAXINGREDIENT; k++) { res = sp->komponenten[k][0]; itemanz = sp->komponenten[k][1]; costtyp = sp->komponenten[k][2]; if(itemanz > 0){ sprintf(buf, " %d %s", itemanz, resname(res, itemanz!=1)); if (costtyp == SPC_LEVEL || costtyp == SPC_LINEAR ) scat(" * Stufe"); rps(F, buf); } } strcpy(buf, "Modifikationen: "); if (sp->sptyp & FARCASTING) { scat("Fernzauber"); dh = 1; } if (sp->sptyp & OCEANCASTABLE) { if (dh == 1){ scat(", "); } scat("Seezauber"); dh = 1; } if (sp->sptyp & ONSHIPCAST) { if (dh == 1){ scat(", "); } scat("Schiffszauber"); dh = 1; } if (sp->sptyp & NOTFAMILIARCAST) { if (dh == 1){ scat(", k"); } else { scat("K"); } scat("ann nicht vom Vertrauten gezaubert werden"); dh = 1; } if(dh == 0) scat("Keine"); rps(F, buf); sprintf(buf, "Stufe: %d", sp->level); rps(F, buf); sprintf(buf, "Rang: %d", sp->rank); rps(F, buf); rnl(F); strcpy(buf, "Syntax: "); rps(F, buf); if(!sp->syntax){ if(sp->sptyp & ISCOMBATSPELL){ strcpy(buf, "KAMPFZAUBER "); }else{ strcpy(buf, "ZAUBERE "); } /* Reihenfolge beachten: Erst REGION, dann STUFE! */ if (sp->sptyp & FARCASTING) { scat("[REGION x y] "); } if (sp->sptyp & SPELLLEVEL) { scat("[STUFE n] "); } scat("\""); scat(sp->name); scat("\" "); if (sp->sptyp & ONETARGET){ if (sp->sptyp & UNITSPELL) { scat(""); } else if (sp->sptyp & SHIPSPELL) { scat(""); } else if (sp->sptyp & BUILDINGSPELL) { scat(""); } }else { if (sp->sptyp & UNITSPELL) { scat(" [ ...]"); } else if (sp->sptyp & SHIPSPELL) { scat(" [ ...]"); } else if (sp->sptyp & BUILDINGSPELL) { scat(" [ ...]"); } } }else{ strcpy(buf, sp->syntax); } rps(F, buf); rnl(F); } /* ------------------------------------------------------------- */ void nmr_warnings(void) { faction *f,*fa; for(f=factions;f;f=f->next) { if(f->no != MONSTER_FACTION && (turn-f->lastorders) >= 2) { for(fa=factions;fa;fa=fa->next) { if(isallied(NULL, f, fa, HELP_GUARD | HELP_MONEY) && isallied(NULL, fa,f,HELP_GUARD | HELP_MONEY)) { sprintf(buf, "Achtung: %s hat einige Zeit keine " "Züge eingeschickt und könnte dadurch in Kürze aus dem " "Spiel ausscheiden.", factionname(f)); addmessage(0, fa, buf, MSG_EVENT, ML_WARN); } } } } } /* ------------------------------------------------------------- */ void sparagraph(strlist ** SP, const char *s, int indent, char mark) { /* Die Liste SP wird mit dem String s aufgefuellt, mit indent und einer * mark, falls angegeben. SP wurde also auf 0 gesetzt vor dem Aufruf. * Vgl. spunit (). */ int i, j, width; int firstline; static char buf[REPORTWIDTH + 1]; width = REPORTWIDTH - indent; firstline = 1; for (;;) { i = 0; do { j = i; while (s[j] && s[j] != ' ') j++; if (j > width) { /* j zeigt auf das ende der aktuellen zeile, i zeigt auf den anfang der * nächsten zeile. existiert ein wort am anfang der zeile, welches * länger als eine zeile ist, muß dieses hier abgetrennt werden. */ if (i == 0) i = width - 1; break; } i = j + 1; } while (s[j]); for (j = 0; j != indent; j++) buf[j] = ' '; if (firstline && mark) buf[indent - 2] = mark; for (j = 0; j != i - 1; j++) buf[indent + j] = s[j]; buf[indent + j] = 0; addstrlist(SP, buf); if (s[i - 1] == 0) break; s += i; firstline = 0; } } int hat_in_region(item_t it, region * r, faction * f) { unit *u; for (u = r->units; u; u = u->next) { if (u->faction == f && get_item(u, it) > 0) { return 1; } } return 0; } static void print_curses(FILE *F, const void * obj, typ_t typ, const attrib *a, int self, int indent) { for(;a;a=a->next) { int dh = 0; if (fval(a->type, ATF_CURSE)) { curse *c = (curse *)a->data.v; if (c->type->curseinfo) dh = c->type->curseinfo(obj, typ, c, self); if (dh == 1) { rnl(F); rparagraph(F, buf, indent, 0); } } else if (a->type==&at_effect && self) { effect_data * data = (effect_data *)a->data.v; sprintf(buf, "Auf der Einheit lieg%s %d Wirkung%s %s.", (data->value==1 ? "t" : "en"), data->value, (data->value==1 ? "" : "en"), locale_string(NULL, resourcename(data->type->itype->rtype, 0))); rnl(F); rparagraph(F, buf, indent, 0); } } } /* ------------------------------------------------------------- */ char * replace_global_coords(const char *s, faction * f) { return translate_regions(s, f); } static void rps_nowrap(FILE * F, const char *s) { const char *x = s; int indent = 0; x = s = replace_global_coords(s, current_faction); while (*x++ == ' '); indent = x - s - 1; if (*(x - 1) && indent && *x == ' ') indent += 2; x = s; while (*s) { if (s == x) { x = strchr(x + 1, ' '); if (!x) x = s + strlen(s); } rpc(F, *s++, 1); } } int rpu = 0; static void rpunit(FILE * F, const faction * f, const unit * u, int indent, int mode) { strlist *S; int dh; boolean isbattle = (boolean)(mode == see_battle); ++rpu; if(u->race == RC_SPELL) return; rnl(F); dh = bufunit(f, u, indent, mode); rparagraph(F, buf, indent, (char) ((u->faction == f) ? '*' : (dh ? '+' : '-'))); if(!isbattle){ print_curses(F, u, TYP_UNIT, u->attribs, (u->faction == f)? 1 : 0, indent); } if (mode==see_unit && u->faction == f && u->botschaften) { for (S = u->botschaften; S; S = S->next) { rnl(F); rparagraph(F, S->s, indent, 0); } } } static void rp_messages(FILE * F, message * msgs, faction * viewer, int indent, boolean centered, boolean categorized) { messageclass * category; if (!msgs) return; for (category=msgclasses; category; category=category->next) { int k = 0; message * m; for (m=msgs; m; m=m->next) { boolean debug = viewer->options & want(O_DEBUG); if (m->type->section!=category) continue; #ifdef MSG_LEVELS if (!debug && get_msglevel(viewer->warnings, viewer->msglevels, m->type) < m->level) continue; #endif /* messagetype * mt = m->type; */ if (m->receiver==NULL || !viewer || viewer==m->receiver) { const char * s = render(m, viewer->locale); if (!k && categorized) { const char * name; char cat_identifier[24]; sprintf(cat_identifier, "section_%s", category->name); name = locale_string(viewer->locale, cat_identifier); if (debug) { if (name!=buf) strcpy(buf, name); sprintf(buf+strlen(name), " [%s]", cat_identifier); name = buf; } k = 1; rnl(F); if (centered) centre(F, name, true); else { if (indent>0) strcpy(buf, " "); strcpy(buf+indent, name); rpsnr(F, buf, 2); } rnl(F); } if (indent>0) { strcpy(buf, " "); strcpy(buf+indent, s); s = buf; } if (debug) { int mylevel = get_msglevel(viewer->warnings, viewer->msglevels, m->type); int level = msg_level(m); if (s!=buf) strcpy(buf, s); sprintf(buf+strlen(s), " [%d:%d(%d)]", m->type->hashkey, level, mylevel); s = buf; } rpsnr(F, s, 2); } } } } /* ------------------------------------------------------------- */ char * f_regionid(const region * r, const faction * f) { static int i = 0; static char bufs[4][NAMESIZE + 20]; char * buf = bufs[(++i)%4]; plane *pl = NULL; if (!r) strcpy(buf, "(Chaos)"); else { pl = getplane(r); if(pl && fval(pl,PFL_NOCOORDS)) { if (rterrain(r) == T_OCEAN) sprintf(buf, "Ozean"); else sprintf(buf, "%s", rname(r, f->locale)); } else { if (rterrain(r) == T_OCEAN) sprintf(buf, "Ozean (%d,%d%s%s)", region_x(r,f), region_y(r,f), pl?",":"", pl?pl->name:""); else sprintf(buf, "%s (%d,%d%s%s)", rname(r, f->locale), region_x(r,f), region_y(r,f), pl?",":"", pl?pl->name:""); } } return buf; } static void prices(FILE * F, region * r, faction * f) { const luxury_type *sale=NULL; struct demand * dmd; int n = 0; if (r->land==NULL || r->land->demands==NULL) return; for (dmd=r->land->demands;dmd;dmd=dmd->next) { if (dmd->value==0) sale = dmd->type; else if (dmd->value > 0) n++; } assert(sale!=NULL); sprintf(buf, "Auf dem Markt wird für %s %d Silber verlangt.", locale_string(f->locale, resourcename(sale->itype->rtype, GR_PLURAL)), sale->price); if(n > 0) scat(" Geboten wird für "); for (dmd=r->land->demands;dmd;dmd=dmd->next) if(dmd->value > 0) { char sbuf[80]; sprintf(sbuf, "%s %d Silber", locale_string(f->locale, resourcename(dmd->type->itype->rtype, GR_PLURAL)), dmd->value * dmd->type->price); scat(sbuf); n--; if (n == 0) scat("."); else if (n == 1) scat(" und für "); else scat(", für "); } /* Schreibe Paragraphen */ rparagraph(F, buf, 0, 0); } /* ------------------------------------------------------------- */ extern const direction_t back[MAXDIRECTIONS]; /* ------------------------------------------------------------- */ boolean see_border(border * b, faction * f, region * r) { boolean cs = b->type->fvisible(b, f, r); if (!cs) { cs = b->type->rvisible(b, r); if (!cs) { unit * us = r->units; while (us && !cs) { if (us->faction==f) { cs = b->type->uvisible(b, us); if (cs) break; } us=us->next; } } } return cs; } extern attrib_type at_roads_override; static void describe(FILE * F, region * r, int partial, faction * f) { char dbuf[512]; int n; boolean dh; direction_t d; int maxmining = 0, trees; unit *u; attrib *a; const char *tname; struct edge { struct edge * next; char * name; boolean transparent; boolean block; boolean exist[MAXDIRECTIONS]; direction_t lastd; } * edges = NULL, * e; boolean see[MAXDIRECTIONS]; for (d = 0; d != MAXDIRECTIONS; d++) { /* Nachbarregionen, die gesehen werden, ermitteln */ region *r2 = rconnect(r, d); border *b; see[d] = true; if (!r2) continue; for (b=get_borders(r, r2);b;) { struct edge * e = edges; boolean transparent = b->type->transparent(b, f); const char * name = b->type->name(b, r, f, GF_DETAILED|GF_ARTICLE); if (!transparent) see[d] = false; if (!see_border(b, f, r)) { b = b->next; continue; } while (e && (e->transparent != transparent || strcmp(name,e->name))) e = e->next; if (!e) { e = calloc(sizeof(struct edge), 1); e->name = strdup(name); e->transparent = transparent; e->next = edges; edges = e; } e->lastd=d; e->exist[d] = true; b = b->next; } } strcpy(buf, f_regionid(r, f)); if (partial == 1) { scat(" (durchgereist)"); } #ifdef SEE_FAR else if (partial == 3) { scat(" (benachbart)"); } #endif else if (partial == 2) { scat(" (vom Turm erblickt)"); } /* Terrain */ scat(", "); if(is_cursed(r->attribs,C_MAELSTROM, 0)) tname = "maelstrom"; else { if (r_isforest(r)) tname = "forest"; else tname = terrain[rterrain(r)].name; } scat(locale_string(f->locale, tname)); /* Bäume */ trees = rtrees(r); if (production(r) && trees > 0) { scat(", "); icat(trees); scat(" "); if (fval(r, RF_MALLORN)) { if (rtrees(r) == 1) scat("Mallornbaum"); else scat("Mallornbäume"); } else if (rtrees(r) == 1) scat("Baum"); else scat("Bäume"); } /* Eisen */ if (partial == 0 && f != (faction *) NULL && (rterrain(r) == T_MOUNTAIN || rterrain(r) == T_GLACIER)) { for (u = r->units; u; u = u->next) { if (u->faction == f) { int s = eff_skill(u, SK_MINING, r); maxmining = max(maxmining, s); } } if (maxmining >= 4) { scat(", "); icat(riron(r)); scat(" Eisen"); } #ifdef NEW_ITEMS if (rlaen(r)>=0 && maxmining >= 7) #else if (rlaen(r)>=0 && maxmining >= itemdata[I_EOG].minskill) #endif { scat(", "); icat(rlaen(r)); scat(" Laen"); } } /* Bauern & Geld */ if (rpeasants(r)) { scat(", "); icat(rpeasants(r)); if(fval(r, RF_ORCIFIED)) { scat(rpeasants(r)==1?" Ork":" Orks"); } else { scat(rpeasants(r)==1?" Bauer":" Bauern"); } if (rmoney(r) && partial == 0) { scat(", "); icat(rmoney(r)); scat(" Silber"); } } /* Pferde */ if (rhorses(r)) { scat(", "); icat(rhorses(r)); scat(" "); #ifdef NEW_ITEMS scat(locale_string(f->locale, resourcename(oldresourcetype[R_HORSE], (rhorses(r)>1)?GR_PLURAL:0))); #else scat(itemdata[I_HORSE].name[rhorses(r) > 1]); #endif } scat("."); if (r->display[0]) { scat(" "); scat(r->display); n = r->display[strlen(r->display) - 1]; if (n != '!' && n != '?' && n != '.') scat("."); } if (!is_cursed(r->attribs, C_REGCONF, 0)) { attrib *a_do = a_find(r->attribs, &at_roads_override); if(a_do) { scat(" "); scat((char *)a_do->data.v); } else { int nrd = 0; /* Nachbarregionen, die gesehen werden, ermitteln */ for (d = 0; d != MAXDIRECTIONS; d++) if (see[d] && rconnect(r, d)) nrd++; /* Richtungen aufzählen */ dh = false; for (d = 0; d != MAXDIRECTIONS; d++) if (see[d]) { region * r2 = rconnect(r, d); if(!r2) continue; nrd--; if (dh) { if (nrd == 0) scat(" und im "); else scat(", im "); } else scat(" Im "); scat(directions[d]); scat(" "); if (!dh) scat("der Region liegt "); sprintf(dbuf, trailinto(r2, f->locale), f_regionid(r2, f)); scat(dbuf); dh = true; } /* Spezielle Richtungen */ for (a = a_find(r->attribs, &at_direction);a;a = a->nexttype) { spec_direction * d = (spec_direction *)(a->data.v); scat(" "); scat(d->desc); scat(" (\""); scat(d->keyword); scat("\")"); scat("."); dh = 1; } if (dh) scat("."); } rnl(F); rparagraph(F, buf, 0, 0); } else { scat(" Große Verwirrung befällt alle Reisenden in dieser Region."); rnl(F); rparagraph(F, buf, 0, 0); } if (r->planep && r->planep->id == 1 && !is_cursed(r->attribs, C_ASTRALBLOCK, 0)) { /* Sonderbehandlung Teleport-Ebene */ regionlist *rl = allinhab_in_range(r_astral_to_standard(r), TP_RADIUS); regionlist *rl2; if (rl) { strcpy(buf, "Schemen der Regionen "); rl2 = rl; while(rl2) { scat(f_regionid(rl2->region, f)); rl2 = rl2->next; if(rl2) scat(", "); } scat(" sind erkennbar."); free_regionlist(rl); /* Schreibe Paragraphen */ rnl(F); rparagraph(F, buf, 0, 0); } } n = 0; /* Wirkungen permanenter Sprüche */ print_curses(F, r, TYP_REGION, r->attribs, 0, 0); /* Produktionsreduktion */ a = a_find(r->attribs, &at_reduceproduction); if(a) { sprintf(buf, "Die Region ist verwüstet, der Boden karg."); rparagraph(F, buf, 0, 0); } if (edges) rnl(F); for (e=edges;e;e=e->next) { boolean first = true; for (d=0;d!=MAXDIRECTIONS;++d) { if (!e->exist[d]) continue; if (first) strcpy(buf, "Im "); else { if (e->lastd==d) strcat(buf, " und im "); else strcat(buf, ", im "); } strcat(buf, directions[d]); first = false; } if (!e->transparent) strcat(buf, " versperrt "); else strcat(buf, " befindet sich "); strcat(buf, e->name); if (!e->transparent) strcat(buf, " die Sicht."); else strcat(buf, "."); rparagraph(F, buf, 0, 0); } if (edges) { while (edges) { e = edges->next; free(edges->name); free(edges); edges = e; } } } extern const int wagetable[6][3]; void statistics(FILE * F, region * r, faction * f) { unit *u; int number, p; item *itm, *items = NULL; p = rpeasants(r); number = 0; /* zählen */ for (u = r->units; u; u = u->next) if (u->faction == f && u->race != RC_SPELL) { for (itm=u->items;itm;itm=itm->next) { i_change(&items, itm->type, itm->number); } number += u->number; } /* Ausgabe */ rnl(F); sprintf(buf, "Statistik für %s:", f_regionid(r, f)); rps(F, buf); rnl(F); /* Region */ if (landregion(rterrain(r)) && rmoney(r)) { sprintf(buf, "Unterhaltung: max. %d Silber", entertainmoney(r)); rps(F, buf); } if (production(r) && (!rterrain(r) == T_OCEAN || f->race == RC_AQUARIAN)) { sprintf(buf, "Lohn für Arbeit: %d Silber", fwage(r, f, true)); rps(F, buf); } if (p) { sprintf(buf, "Rekrutieren: max. %d Bauern", p / RECRUITFRACTION); rps(F, buf); if (gebaeude_vorhanden(r, &bt_caravan)) { sprintf(buf, "Luxusgüter zum angegebenen Preis: %d", (p * 2) / TRADE_FRACTION); } else { sprintf(buf, "Luxusgüter zum angegebenen Preis: %d", p / TRADE_FRACTION); } rps(F, buf); } /* Info über Einheiten */ sprintf(buf, "Personen: %d", number); rps(F, buf); for (itm = items; itm; itm=itm->next) { sprintf(buf, "%s: %d", locale_string(f->locale, resourcename(itm->type->rtype, GR_PLURAL)), itm->number); rps(F, buf); } while (items) i_free(i_remove(&items, items)); } int in_region(region * r, unit * u) { unit *ru; for (ru = r->units; ru; ru = ru->next) { if (u == ru) { return (1); } } return (0); } static void durchreisende(FILE * F, region * r, faction * f) { attrib *ru; int wieviele; int counter; wieviele = counter = 0; /* Wieviele sind aufzulisten? Für die Grammatik. */ for (ru = a_find(r->attribs, &at_travelunit); ru; ru = ru->nexttype) { unit * u = (unit*)ru->data.v; if (cansee_durchgezogen(f, r, u, 0) > 0 && !in_region(r, u)) { if (u->ship && !fval(u, FL_OWNER)) continue; wieviele++; } } if (!wieviele) return; /* Auflisten. */ buf[0] = 0; rnl(F); for (ru = a_find(r->attribs, &at_travelunit); ru; ru = ru->nexttype) { unit * u = (unit*)ru->data.v; if (cansee_durchgezogen(f, r, u, 0) > 0 && !in_region(r, u)) { if (u->ship && !fval(u, FL_OWNER)) continue; counter++; if (u->ship != (ship *) NULL) { if (counter == 1) { scat("Die "); } else { scat("die "); } scat(shipname(u->ship)); } else { scat(unitname(u)); } if (counter + 1 < wieviele) { scat(", "); } else if (counter + 1 == wieviele) { scat(" und "); } } } if (wieviele == 1) { scat(" hat die Region durchquert."); rparagraph(F, buf, 0, 0); } else { scat(" haben die Region durchquert."); rparagraph(F, buf, 0, 0); } } static void order_template(FILE * F, faction * f) { strlist *S2; region *r; plane *pl; unit *u; int dh; region *last = lastregion(f); rps_nowrap(F, ""); rnl(F); rps_nowrap(F, "Vorlage für den nächsten Zug:"); rnl(F); rps_nowrap(F, ""); rnl(F); sprintf(buf, "%s %s \"hier_passwort_eintragen\"", parameters[P_FACTION], factionid(f)); rps_nowrap(F, buf); rnl(F); dh = (f->options & Pow(O_SILBERPOOL)); rps_nowrap(F, ""); rnl(F); sprintf(buf, "; ECHECK %s-w4 -r%d -v%s", dh ? "-l " : "", race[f->race].rekrutieren, ECHECK_VERSION); /* -v3.4: ECheck Version 3.4.x */ rps_nowrap(F, buf); rnl(F); for (r = firstregion(f); r != last; r = r->next) { dh = 0; for (u = r->units; u; u = u->next) if (u->faction == f && u->race != RC_SPELL) { if (!dh) { rps_nowrap(F, ""); rnl(F); pl = getplane(r); if (pl && fval(pl, PFL_NOCOORDS)) { sprintf(buf, "REGION; %s", rname(r, f->locale)); } else { sprintf(buf, "REGION %d,%d ; %s", region_x(r,f), region_y(r,f), rname(r, f->locale)); } rps_nowrap(F, buf); rnl(F); sprintf(buf,"; ECheck Lohn %d", fwage(r,f,true)); rps_nowrap(F, buf); rnl(F); rps_nowrap(F, ""); rnl(F); } dh = 1; sprintf(buf, "%s %s; %s [%d,%d$", parameters[P_UNIT], unitid(u), u->name, u->number, get_money(u)); if (u->building != NULL && fval(u, FL_OWNER)) { building * b = u->building; int cost = buildingmaintenance(b, R_SILVER); if (cost > 0) { scat(",U"); icat(cost); } #if TODO if (buildingdaten[u->building->typ].spezial != 0) { scat("+"); } #endif } else if (u->ship) { if (fval(u, FL_OWNER)) scat(",S"); else scat(",s"); scat(shipid(u->ship)); } if (lifestyle(u) == 0) scat(",I"); scat("]"); rps_nowrap(F, buf); rnl(F); for (S2 = u->orders; S2; S2 = S2->next) { if(is_persistent(S2->s)) { sprintf(buf, " %s", S2->s); rps_nowrap(F, buf); rnl(F); } } if(u->lastorder[0]) { sprintf(buf, " %s", u->lastorder); rps_nowrap(F, buf); rnl(F); } } } rps_nowrap(F, ""); rnl(F); sprintf(buf, parameters[P_NEXT]); rps_nowrap(F, buf); rnl(F); } static void alliances(ally * sf) { int allierte = 0; int i=0, h, hh = 0; int dh = 0; ally * sff; for (sff = sf; sff; sff = sff->next) { if (sff->status > 0 && sff->status <= HELP_ALL) { allierte++; } } while (sf) { if (sf->status > 0) { i++; if (dh) { if (i == allierte) scat(" und "); else scat(", "); } dh = 1; hh = 0; scat(factionname(sf->faction)); scat(" ("); if (sf->status == HELP_ALL) { scat("Alles"); } else for (h = 1; h < HELP_ALL; h *= 2) { if ((sf->status & h) == h) switch (h) { case HELP_MONEY: scat("Silber"); hh = 1; break; case HELP_FIGHT: if (hh) scat(", "); scat("Kämpfe"); hh = 1; break; case HELP_OBSERVE: if (hh) scat(", "); scat("Wahrnehmung"); hh = 1; break; case HELP_GIVE: if (hh) scat(", "); scat("Gib"); hh = 1; break; case HELP_GUARD: if (hh) scat(", "); scat("Bewache"); hh = 1; } } scat(")"); } sf = sf->next; } } static void allies(FILE * F, faction * f) { #ifdef GROUPS group * g = f->groups; #endif if (f->allies) { if (!f->allies->next) { strcpy(buf, "Wir helfen der Partei "); } else { strcpy(buf, "Wir helfen den Parteien "); } alliances(f->allies); scat("."); rparagraph(F, buf, 0, 0); rnl(F); } #ifdef GROUPS while (g) { if (g->allies) { if (!g->allies->next) { sprintf(buf, "%s hilft der Partei ", g->name); } else { sprintf(buf, "%s hilft den Parteien ", g->name); } alliances(g->allies); scat("."); rparagraph(F, buf, 0, 0); rnl(F); } g = g->next; } #endif } static void guards(FILE * F, region * r, faction * see) { /* die Partei see sieht dies; wegen * "unbekannte Partei", wenn man es selbst ist... */ faction* guardians[512]; int nextguard = 0; unit *u; int i; boolean tarned = false; /* Bewachung */ for (u = r->units; u; u = u->next) if (getguard(u)) { faction *f = u->faction; if (f!=see && fval(u, FL_PARTEITARNUNG)) { tarned=true; } else { for (i=0;i!=nextguard;++i) if (guardians[i]==f) break; if (i==nextguard) { guardians[nextguard++] = f; } } } if (!nextguard && !tarned) return; strcpy(buf, "Die Region wird von "); for (i = 0; i!=nextguard+(tarned?1:0); ++i) { if (i!=0) { if (i == nextguard-(tarned?0:1)) scat(" und "); else scat(", "); } if (ino>f2->no) return 1; else if (f1->nono) return -1; return 0; } static void list_address(FILE * F, faction * uf) { faction *f; void **fp; cvector *fcts = malloc(sizeof(cvector)); cv_init(fcts); for (f = factions; f; f = f->next) { if (f->no != MONSTER_FACTION && kann_finden(uf, f) != 0) { cv_pushback(fcts, f); } } v_sort(fcts->begin, fcts->end, fcompare); centre(F, "Liste aller Adressen", false); rnl(F); for (fp = fcts->begin; fp != fcts->end; ++fp) { f = *fp; sprintf(buf, "%s: %s; %s", factionname(f), f->email, f->banner); rparagraph(F, buf, 4, '*'); } rnl(F); rpline(F); free(cv_kill(fcts)); } void report_building(FILE *F, const region * r, const building * b, const faction * f, int mode) { int i; unit *u; attrib * a = a_find(b->attribs, &at_icastle); const locale * lang = NULL; const building_type * type = b->type; if (f) lang = f->locale; if (a!=NULL) { type = ((icastle_data*)a->data.v)->type; } else { type = b->type; } sprintf(buf, "%s, Größe %d, %s", buildingname(b), b->size, buildingtype(b, b->size, lang)); if (b->size < type->maxsize) { scat(" (im Bau)"); } if (b->besieged > 0 && mode>=see_lighthouse) { scat(", belagert von "); icat(b->besieged); scat(" Personen "); if (b->besieged >= b->size * SIEGEFACTOR) { scat("(abgeschnitten)"); } else { scat("(unvollständig belagert)"); } } i = 0; if (b->display[0]) { scat("; "); scat(b->display); i = b->display[strlen(b->display) - 1]; } if (i != '!' && i != '?' && i != '.') scat("."); rparagraph(F, buf, 2, 0); if (modeattribs, (buildingowner(r,b)->faction == f)? 1 : 0, 2); } else { print_curses(F, b, TYP_BUILDING, b->attribs, 0, 2); } for (u = r->units; u; u = u->next) if (u->building == b && fval(u, FL_OWNER)) { rpunit(F, f, u, 6, mode); break; } for (u = r->units; u; u = u->next) if (u->building == b && !fval(u, FL_OWNER)) rpunit(F, f, u, 6, mode); } static void report(FILE *F, faction * f) { #ifndef NEW_ITEMS potion_t potion; #endif int flag = 0; char ch; int dh; int anyunits; region *r; building *b; ship *sh; unit *u; attrib *a; int wants_stats; int wants_zugvorlage; int wants_addresses; seen_region * sd; int ix; unsigned char op; char buf2[80]; ix = Pow(O_STATISTICS); wants_stats = (f->options & ix); ix = Pow(O_ZUGVORLAGE); wants_zugvorlage = (f->options & ix); ix = Pow(O_ADRESSEN); wants_addresses = (f->options & ix); if (quiet) { printf(" NR"); fflush(stdout); } else printf(" - Schreibe Report\n"); centre(F, gamedate(), true); rnl(F); sprintf(buf, "%s, %s/%s (%s)", factionname(f), race[f->race].name[1], neue_gebiete[f->magiegebiet], f->email); centre(F, buf, true); buf[0] = 0; dh = 0; for(a=a_find(f->attribs, &at_faction_special); a; a=a->nexttype) { dh++; if(fspecials[a->data.sa[0]].levels) { sprintf(buf2, "%s (%d)", fspecials[a->data.sa[0]].name, a->data.sa[1]); } else { sprintf(buf2, "%s", fspecials[a->data.sa[0]].name); } if(dh > 1) strcat(buf, ", "); strcat(buf, buf2); } if(dh > 0) centre(F, buf, true); dh = 0; if(f->karma > 0) { sprintf(buf, "Deine Partei hat %d Karma.", f->karma); centre(F, buf, true); } if (f->age <= 2) { rnl(F); sprintf(buf, "Dein Passwort lautet \"%s\".", f->passw); centre(F, buf, true); sprintf(buf, "Bitte denke daran, deine Befehle mit dem Betreff" " ERESSEA BEFEHLE an eressea@eressea.amber.kn-bremen.de zu senden." " Am besten, du verwendest die Befehlsvorlage am Ende des Reports."); centre(F, buf, true); rnl(F); sprintf(buf, "Die ersten beiden Züge mußt du abgeben, sonst wird deine" " Partei sofort wieder gelöscht, um Karteileichen zu vermeiden."); centre(F, buf, true); rnl(F); sprintf(buf, "Mit der ersten Auswertung bekommst du einen Computerreport, " "den du mit vielen der Tools auf http://eressea-pbem.de/download.html " "benutzen kannst. Wenn du ihn weiterhin bekommen willst, gib einer " "deiner Einheiten den Befehl OPTION COMPUTER"); centre(F, buf, true); } rnl(F); if (f->options & want(O_SCORE) && f->age > DISPLAYSCORE) { sprintf(buf, "Deine Partei hat diese Woche %d Punkte.", f->score); centre(F, buf, true); sprintf(buf, "(Durchschnitt für Parteien ähnlichen Alters ist %d Punkte)", average_score_of_age(f->age, f->age / 24 + 1)); centre(F, buf, true); } if (f->race == RC_HUMAN) { sprintf(buf, "Deine Partei besteht aus %d Personen in %d Einheiten, davon sind %d Personen Migranten.", count_all(f), f->no_units, count_migrants(f)); centre(F, buf, true); sprintf(buf, "Maximal kann Deine Partei %d Migranten aufnehmen.", count_maxmigrants(f)); centre(F, buf, true); } else { sprintf(buf, "Deine Partei besteht aus %d Personen in %d Einheiten.", count_all(f),f->no_units); centre(F, buf, true); } if (f->age > 1 && f->lastorders != turn) { rnl(F); if (turn - f->lastorders == 1) { centre(F, "Deine Partei hat letzte Runde keinen Zug abgegeben!", true); } else { sprintf(buf, "Deine Partei hat seit %d Runden keinen Zug abgegeben! Wenn du" " drei Runden nacheinander keinen Zug abgibst, wird sie" " automatisch gelöscht.", turn - f->lastorders); centre(F, buf, true); } rnl(F); } /* Insekten-Winter-Warnung */ if(f->race == RC_INSECT) { if(season[month(1)] == 0) { strcpy(buf, "Es ist Winter, und Insekten können nur in Wüsten oder mit " "Hilfe des Nestwärme-Tranks Personen rekrutieren."); centre(F, buf, true); rnl(F); } else if(season[month(2)] == 0) { strcpy(buf, "Es ist Spätherbst, und diese Woche ist die letzte vor dem " "Winter, in der Insekten rekrutieren können."); centre(F, buf, true); rnl(F); } } sprintf(buf, "Optionen:"); for (op = 0; op != MAXOPTIONS; op++) { if (f->options & (int) pow(2, op)) { scat(" "); scat(options[op]); flag++; } } if (flag > 0) { rnl(F); centre(F, buf, true); } /* Der Report soll später einmal durch einen Makroprozessor laufen. * (enno) was? wer hat das geschrieben? * Momentan ist das wegen der der Mailverschickmimik schwierig. */ rp_messages(F, f->msgs, f, 0, true, true); if(f->battles) { struct bmsg * bm; rnl(F); centre(F, "Kämpfe", false); rnl(F); for (bm=f->battles;bm;bm=bm->next) { #ifdef HAVE_SNPRINTF snprintf(buf, 80, "In %s findet ein Kampf statt:", translate_regions(regionid(bm->r), f)); #else sprintf(buf, "In %s findet ein Kampf statt:", translate_regions(regionid(bm->r), f)); buf[80]=0; #endif rnl(F); centre(F, buf, true); rnl(F); rp_messages(F, bm->msgs, f, 0, true, false); } } a = a_find(f->attribs, &at_reportspell); if (a) { rnl(F); centre(F, "Neue Zauber", true); while (a) { report_spell(F, (spellid_t)a->data.i); a = a->nexttype; } } #ifdef NEW_ITEMS ch = 0; for (a=a_find(f->attribs, &at_showitem);a;a=a->nexttype) { const potion_type * ptype = resource2potion(((const item_type*)a->data.v)->rtype); requirement * m; if (ptype==NULL) continue; m = ptype->itype->construction->materials; if (ch==0) { rnl(F); centre(F, "Erforschte Tränke", true); ch = 1; } rnl(F); centre(F, locale_string(f->locale, resourcename(ptype->itype->rtype, 0)), true); sprintf(buf, "Stufe %d", ptype->level); centre(F, buf, true); rnl(F); sprintf(buf, "Benötigte Kräuter: "); while (m->number) { scat(locale_string(f->locale, resourcename(oldresourcetype[m->type], 0))); ++m; if (m->number) scat(", "); } centre(F, buf, true); rnl(F); centre(F, ptype->text, true); } #else for (potion = 0; potion != MAXPOTIONS; potion++) if (f->showpotion[potion] == 1) break; if (potion != MAXPOTIONS) { rnl(F); centre(F, "Erforschte Tränke", true); for (potion = 0; potion != MAXPOTIONS; ++potion) { if (f->showpotion[potion] == 1) { int h; rnl(F); centre(F, potionnames[0][potion], true); sprintf(buf, "Stufe %d", potionlevel[potion]); centre(F, buf, true); rnl(F); sprintf(buf, "Benötigte Kräuter: "); for (h = 0; h < MAXHERBSPERPOTION; h++) { if (potionherbs[potion][h] == NOHERB) break; if (h > 0) { scat(", "); } scat(herbdata[0][potionherbs[potion][h]]); } centre(F, buf, true); rnl(F); centre(F, (char *) potiontext[potion], true); /* Nicht nochmal anzeigen! */ f->showpotion[potion] = 2; } } } #endif rnl(F); centre(F, "Aktueller Status", false); rnl(F); allies(F, f); rpline(F); anyunits = 0; for (sd = seen; sd!=NULL; sd = sd->next) { boolean unit_in_region = false; boolean durchgezogen_in_region = false; int turm_sieht_region = false; switch (sd->mode) { case see_lighthouse: turm_sieht_region = true; break; #ifdef SEE_FAR case see_far: break; #endif case see_travel: durchgezogen_in_region = true; break; case see_unit: unit_in_region = true; anyunits = true; break; default: continue; } r = sd->r; /* Beschreibung */ if (unit_in_region) { describe(F, r, 0, f); if (rterrain(r) != T_OCEAN && rpeasants(r)/TRADE_FRACTION > 0) { rnl(F); prices(F, r, f); } guards(F, r, f); durchreisende(F, r, f); } #ifdef SEE_FAR else if (sd->mode==see_far) { describe(F, r, 3, f); guards(F, r, f); durchreisende(F, r, f); } #endif else if (turm_sieht_region) { describe(F, r, 2, f); durchreisende(F, r, f); } else { describe(F, r, 1, f); durchreisende(F, r, f); } /* Statistik */ if (wants_stats && unit_in_region == 1) statistics(F, r, f); /* Nachrichten an REGION in der Region */ if (unit_in_region || durchgezogen_in_region) { rp_messages(F, r->msgs, f, 0, true, true); } /* Burgen und ihre Einheiten */ for (b = rbuildings(r); b; b = b->next) { rnl(F); report_building(F, r, b, f, sd->mode); } /* Restliche Einheiten */ if (sd->mode>=see_lighthouse) { for (u = r->units; u; u = u->next) { if (!u->building && !u->ship) { if ((u->faction == f) || (unit_in_region && cansee(f, r, u, 0)) || (durchgezogen_in_region && cansee(f, r, u, -1)) || #ifdef SEE_FAR (sd->mode==see_far && cansee(f, r, u, -2)) || #endif (turm_sieht_region && cansee(f, r, u, -2))) { if (dh == 0 && !(rbuildings(r) || r->ships)) { dh = 1; /* rnl(F); */ } rpunit(F, f, u, 4, sd->mode); } } } } /* Schiffe und ihre Einheiten */ for (sh = r->ships; sh; sh = sh->next) { int w = 0; faction *of = NULL; rnl(F); /* Gewicht feststellen */ for (u = r->units; u; u = u->next) if (u->ship == sh && fval(u, FL_OWNER)) { of = u->faction; break; } if (of == f) { for (u = r->units; u; u = u->next) { if (u->ship == sh) { w += weight(u); } } sprintf(buf, "%s, %s, (%d/%d)", shipname(sh), sh->type->name[0], (w + 99) / 100, /* +99 weil sonst die Nachkommastellen ignoriert würden */ shipcapacity(sh) / 100); } else { sprintf(buf, "%s, %s", shipname(sh), sh->type->name[0]); } assert(sh->type->construction->improvement==NULL); /* sonst ist construction::size nicht ship_type::maxsize */ if (sh->size!=sh->type->construction->maxsize) { scat(", im Bau ("); icat(sh->size); scat("/"); icat(sh->type->construction->maxsize); scat(")"); } if (sh->damage) { scat(", "); icat(sh->damage*100/(sh->size*DAMAGE_SCALE)); scat("% beschädigt"); } if (rterrain(r) != T_OCEAN) { if (sh->coast != NODIRECTION) { scat(", "); scat(coasts[sh->coast]); } } ch = 0; if (sh->display[0]) { scat("; "); scat(sh->display); ch = sh->display[strlen(sh->display) - 1]; } if (ch != '!' && ch != '?' && ch != '.') scat("."); rparagraph(F, buf, 2, 0); /* Leere Schiffe verursachten sonst segfault! */ if(shipowner(r,sh) != NULL){ print_curses(F, sh, TYP_SHIP, sh->attribs, (shipowner(r,sh)->faction == f)? 1 : 0, 2); } else { print_curses(F, sh, TYP_SHIP, sh->attribs, 0, 2); } for (u = r->units; u; u = u->next) if (u->ship == sh && fval(u, FL_OWNER)) { rpunit(F, f, u, 6, sd->mode); break; } for (u = r->units; u; u = u->next) if (u->ship == sh && !fval(u, FL_OWNER)) rpunit(F, f, u, 6, sd->mode); } rnl(F); rpline(F); } if (f->no != MONSTER_FACTION) { if (!anyunits) { rnl(F); rparagraph(F, "Unglücklicherweise wurde deine Partei ausgelöscht. " "Du kannst gerne an einer anderen Stelle wieder " "einsteigen. Melde Dich einfach wieder an.", 0, 0); } else { if (wants_addresses) { list_address(F, f); } if (wants_zugvorlage) { order_template(F, f); } } } } /* ------------------------------------------------------------- */ FILE * openbatch(void) { faction *f; FILE * BAT = NULL; /* falls und mind. ein internet spieler gefunden wird, schreibe den * header des batch files. ab nun kann BAT verwendet werden, um zu * pruefen, ob netspieler vorhanden sind und ins mailit batch * geschrieben werden darf. */ for (f = factions; f; f = f->next) { /* bei "internet:" verschicken wir die mail per batchfile. für * unix kann man alles in EIN batchfile schreiben, als * sogenanntes "herefile". Konnte der batchfile nicht geoeffnet * werden, schreiben wir die reports einzeln. der BAT file wird * nur gemacht, wenn es auch internet benutzer gibt. */ if (f->email) { sprintf(buf, "%s/mailit", reportpath()); if ((BAT = fopen(buf, "w")) == NULL) puts("* Fehler: mailit konnte nicht geöffnet werden!"); else fprintf(BAT, "#!/bin/sh\n" "\n" "# MAILIT shell file, vom Eressea Host generiert\n" "#\n" "# Verwendung: nohup mailit &\n" "#\n" "\n" "PATH=%s\n" "\n" "chmod 755 *.sh\n" "\n" ,MAILITPATH); break; } } return BAT; } void closebatch(FILE * BAT) { if (BAT) { fputs("\n", BAT); fclose(BAT); } } /* ------------------------------------------------------------- */ void base36conversion(void) { region * r; for (r=regions;r;r=r->next) { unit * u; for (u=r->units;u;u=u->next) { if (forbiddenid(u->no)) { uunhash(u); u->no = newunitid(); uhash(u); } } } } extern void init_intervals(void); #ifdef USE_MERIAN #define xp(r, f) ((region_x((r),(f)))*2 + region_y((r),(f))) #define yp(r, f) (region_y((r),(f))) void merian(FILE * out, faction * f) { int x1 = INT_MAX, x2=INT_MIN, y1=INT_MAX, y2=INT_MIN; region *left=0, *right=0, *top=0, *bottom=0; char ** line; char * c; int y, xw; seen_region * sd; if (!seen) return; for (sd = seen; sd!=NULL; sd = sd->next) { region * r = sd->r; if (r->planeid != 0) continue; if (!left || xp(r, f)xp(right, f)) right=r; if (!top || yp(r, f)>yp(top, f)) top=r; if (!bottom || yp(r, f)99) xw = 3; else if (xw>9) xw = 2; else xw = 1; fputs("CR-Version: 42\nMERIAN Version 1.02\nKartenart: Hex(Standard)\n\nLEGENDE\nHochland = H\nGletscher = G\nGebirge = B\nWald = W\nSumpf = S\nOzean = .\nWüste = D\nEbene = E\n\n", out); line = (char**)calloc(1, (y2-y1)*sizeof(char*)); c = (char*)calloc(1, (y2-y1)*((x2-x1)*sizeof(char)+13)); memset(c, ' ', (y2-y1)*((x2-x1)*sizeof(char)+13)); { int width = 1+x2-x1; int lx = region_x(top, f) - (xp(top, f)-x1) / 2; int i; if ((y2-y1) % 2 ==0) ++lx; fputs(" ", out); for (i=0;i!=width;++i) { int x = lx+((i+1)/2); int o = abs(x1-y2); if (i%2 == o % 2) fprintf(out, "%c", (x<0)?'-':(x>0)?'+':' '); else putc(' ', out); } putc('\n', out); switch (xw) { case 3: fputs(" ", out); for (i=0;i!=width;++i) { int x = lx+((i+1)/2); int o = abs(x1-y2); if (i%2 == o % 2) fprintf(out, "%c", (abs(x)/100)?((abs(x)/100)+'0'):' '); else putc(' ', out); } putc('\n', out); case 2: fputs(" ", out); for (i=0;i!=width;++i) { int x = lx+((i+1)/2); int o = abs(x1-y2); if (i%2 == o % 2) fprintf(out, "%c", (abs(x)%100)/10?((abs(x)%100)/10+'0'):(abs(x)<100?' ':'0')); else putc(' ', out); } putc('\n', out); default: fputs(" ", out); for (i=0;i!=width;++i) { int x = lx+(i/2); int o = abs(x1-y2); if (i%2 == o % 2) fprintf(out, "%c", abs(x)%10+'0'); else putc(' ', out); } putc('\n', out); } fputs(" ", out); for (i=0;i!=width;++i) { int o = abs(x1-y2); if (i%2 == o % 2) putc('/', out); else putc(' ', out); } } putc('\n', out); for (y=y1;y!=y2;++y) { line[y-y1] = c + (y-y1)*((x2-x1)*sizeof(char)+13); sprintf(line[y-y1], "%4d", y); line[y-y1][4] = ' '; sprintf(line[y-y1]+7+(x2-x1), "%4d", y); } for (sd = seen; sd!=NULL; sd = sd->next) { region * r = sd->r; if (getplane(r)) continue; line[yp(r, f)-y1][5+(xp(r, f)-x1)] = ' '; line[yp(r, f)-y1][6+(xp(r, f)-x1)] = terrain[rterrain(r)].symbol; } /* print header */ for (y=y1;y!=y2;++y) { fputs(line[y2-y-1], out); putc('\n', out); } { int width = 1+x2-x1; int lx = region_x(bottom, f) - (xp(bottom, f)-x1) / 2; int i; fputs(" ", out); for (i=0;i!=width;++i) { int o = abs(x1-y1); if (i%2 == o % 2) putc('/', out); else putc(' ', out); } putc('\n', out); fputs(" ", out); for (i=0;i!=width;++i) { int x = lx+((i+1)/2); int o = abs(x1-y1); if (i%2 == o % 2) fprintf(out, "%c", (x<0)?'-':(x>0)?'+':' '); else putc(' ', out); } putc('\n', out); switch (xw) { case 3: fputs(" ", out); for (i=0;i!=width;++i) { int x = lx+((i+1)/2); int o = abs(x1-y1); if (i%2 == o % 2) fprintf(out, "%c", (abs(x)/100)?((abs(x)/100)+'0'):' '); else putc(' ', out); } putc('\n', out); case 2: fputs(" ", out); for (i=0;i!=width;++i) { int x = lx+((i+1)/2); int o = abs(x1-y1); if (i%2 == o % 2) fprintf(out, "%c", (abs(x)%100)/10?((abs(x)%100)/10+'0'):(abs(x)<100?' ':'0')); else putc(' ', out); } putc('\n', out); default: fputs(" ", out); for (i=0;i!=width;++i) { int x = lx+((i+1)/2); int o = abs(x1-y1); if (i%2 == o % 2) fprintf(out, "%c", abs(x)%10+'0'); else putc(' ', out); } putc('\n', out); } } free(c); free(line); } #endif seen_region * seen; seen_region * reuse; seen_region** append; seen_region * last; #define MAXSEEHASH 4095 seen_region * seehash[MAXSEEHASH]; static void seen_done(void) { while (seen) { seen_region * r = seen; seen = seen->next; free(r); } while (reuse) { seen_region * r = reuse; reuse = reuse->next; free(r); } } #if FAST_SEEN static void init_intervals() { region * r; for (r=regions;r;r=r->next) { unit * u; attrib * a; for (a=a_find(r->attribs, &at_travelunit);a;a=a->nexttype) { unit * v = (unit*)a->data.v; faction * f = v->faction; if (!f) continue; if (!f->first) { assert(!f->last); f->first = r; } f->last = r->next; } for (a=a_find(r->attribs, &at_lighthouse);a;a=a->nexttype) { building * b = (building*)a->data.v; region * br = b->region; if (!b->region) continue; for (u=br->units;u;u=u->next) { faction * f = u->faction; if (!f->first) { assert(!f->last); f->first = r; } f->last = r->next; } } for (u=r->units;u;u=u->next) { faction * f = u->faction; if (!f->first) { assert(!f->last); f->first = r; } f->last = r->next; } } } #endif static boolean add_seen(region * r, unsigned char mode, boolean dis) { seen_region * find; int index = abs((r->x & 0xffff) + ((r->y) << 16)) % MAXSEEHASH; for (find=seehash[index];find;find=find->nextHash) { if (find->r==r) { if (find->mode < mode) { if (find->mode<=see_neighbour && find->next) { find->next->prev = find->prev; if (find->prev) find->prev->next = find->next; else seen = find->next; last->next = find; find->prev = last; find->next = NULL; last = find; append = &last->next; } find->mode = mode; find->disbelieves |= dis; return true; } else return false; } } if (!reuse) reuse = (seen_region*)calloc(1, sizeof(seen_region)); *append = reuse; reuse = reuse->next; (*append)->next = NULL; (*append)->prev = last; if (last) last->next = *append; last = *append; (*append)->nextHash = seehash[index]; seehash[index] = *append; (*append)->r = r; (*append)->mode = mode; (*append)->disbelieves = dis; append = &(*append)->next; return true; } #define DBG_CACHE 1 #if DBG_CACHE int chits = 0; int ctries = 0; #endif static void prepare_report(faction * f) { region * r; region * end = lastregion(f); append = &reuse; memset(seehash, 0, sizeof(seehash)); while (*append) append = &(*append)->next; *append = seen; seen = NULL; last = NULL; append = &seen; for (r = firstregion(f); r != end; r = r->next) { attrib *ru; unit * u; unsigned char mode = see_none; boolean dis = false; #if DBG_CACHE ++ctries; #endif for (u = r->units; u; u = u->next) { if (u->faction == f) { if (u->race != RC_SPELL || u->number == RS_FARVISION) { mode = see_unit; if (fval(u, FL_DISBELIEVES)) { dis = true; break; } } } } if (mode==see_none) for (ru = a_find(r->attribs, &at_travelunit); ru; ru = ru->nexttype) { unit * u = (unit*)ru->data.v; if (u->faction == f) { mode = see_travel; break; } } if (mode==see_none && rterrain(r) == T_OCEAN) { if (check_leuchtturm(r, f)) mode = see_lighthouse; } if (mode == see_none) continue; #if DBG_CACHE else ++chits; #endif add_seen(r, mode, dis); /* nicht, wenn Verwirrung herrscht: */ if (!is_cursed(r->attribs, C_REGCONF, 0)) { direction_t dir; for (dir=0;dir!=MAXDIRECTIONS;++dir) { region * r2 = rconnect(r, dir); if (r2) { border * b = get_borders(r, r2); while (b) { if (!b->type->transparent(b, f)) break; b = b->next; } #ifdef SEE_FAR if (!b) { if (add_seen(r2, see_far, false)) { direction_t dir; for (dir=0;dir!=MAXDIRECTIONS;++dir) { region * r3 = rconnect(r2, dir); if (r3) { border * b = get_borders(r2, r3); while (b) { if (!b->type->transparent(b, f)) break; b = b->next; } if (!b) add_seen(r3, see_neighbour, false); } } } } #else if (!b) add_seen(r2, see_neighbour, false); #endif } } } } #if DBG_CACHE if (!quiet) printf(" - cache hits: %.2f%%\n", 100*chits/(double)ctries); #endif } #define FMAXHASH 1023 struct fsee { struct fsee * nexthash; faction * f; struct see { struct see * next; faction * seen; unit * proof; } * see; } * fsee[FMAXHASH]; void reports(void) { faction *f; region *r; boolean gotit; FILE *shfp, *F, *BAT; int wants_report, wants_computer_report, wants_compressed, wants_bzip2; nmr_warnings(); #ifdef DMALLOC assert(dmalloc_verify ( NULL )); #endif makedir(reportpath(), 0700); if (global.data_versionnext) { invert_list(&r->msgs); } report_donations(); #if FAST_SEEN init_intervals(); #endif remove_empty_units(); for (f = factions; f; f = f->next) { struct bmsg * b; attrib * a = a_find(f->attribs, &at_reportspell); for (b = f->battles;b;b=b->next) invert_list(&b->msgs); invert_list(&f->battles); current_faction = f; gotit = false; if (quiet) { printf("Reports für %s: ", factionname(f)); fflush(stdout); } else printf("%s\n", factionname(f)); prepare_report(f); invert_list(&f->msgs); if (!nonr && (f->options & wants_report)) { sprintf(buf, "%s/%s.nr", reportpath(), factionid(f)); F = cfopen(buf, "wt"); if (F) { report(F, f); fclose(F); gotit = true; } } if (!nocr && (f->options & wants_computer_report || f->age<3)) { sprintf(buf, "%s/%s.cr", reportpath(), factionid(f)); F = cfopen(buf, "wt"); if (F) { report_computer(F, f); fclose(F); gotit = true; } } if (f->email && BAT) { sprintf(buf, "%s/%s.sh", reportpath(), factionid(f)); shfp = fopen(buf, "w"); fprintf(shfp,"#!/bin/sh\n\nPATH=%s\n\n",MAILITPATH); fprintf(shfp,"if [ $# -ge 1 ]; then\n"); fprintf(shfp,"\taddr=$1\n"); fprintf(shfp,"else\n"); fprintf(shfp,"\taddr=%s\n", f->email); fprintf(shfp,"fi\n\n"); fprintf(BAT, "\n\ndate;echo %s\n", f->email); if (f->no > 0 && f->options & wants_compressed) { if(f->age == 1) { fprintf(BAT, "ls %s.nr %s.cr ../newbie.txt | zip -m -j -9 -@ %s.zip\n", factionid(f), factionid(f), factionid(f)); } else { fprintf(BAT, "ls %s.nr %s.cr | zip -m -j -9 -@ %s.zip\n", factionid(f), factionid(f), factionid(f)); } fprintf(shfp, "eresseamail.zipped $addr \"%s %s\" \"%s-%d.zip\" " "%s.zip\n", global.gamename, gamedate_short(), factionid(f), turn, factionid(f)); } else if(f->options & wants_bzip2) { if (f->age == 1) { fprintf(shfp, " \\\n\t\"text/plain\" \"Willkommen\" ../newbie.txt"); } fprintf(BAT, "bzip2 -9v `ls %s.nr %s.cr`\n", factionid(f), factionid(f)); fprintf(shfp, "eresseamail.bzip2 $addr \"%s %s\"", global.gamename, gamedate_short()); if (!nonr && f->options & wants_report) fprintf(shfp, " \\\n\t\"application/x-bzip2\" \"Report\" %s.nr.bz2", factionid(f)); if (!nocr && (f->options & wants_computer_report || f->age<3)) fprintf(shfp, " \\\n\t\"application/x-bzip2\" \"Computer-Report\" %s.cr.bz2", factionid(f)); #ifdef USE_MERIAN if (!nomer && f->options & wants_merian) fprintf(shfp, " \\\n\t\"application/x-bzip2\" \"Merian-Karte\" %s.mer.bz2", factionid(f)); #endif } else { fprintf(shfp, MAIL " $addr \"%s %s\"", global.gamename, gamedate_short()); if (f->age == 1) { fprintf(shfp, " \\\n\t\"text/plain\" \"Willkommen\" ../newbie.txt"); } if (!nonr && f->options & wants_report) fprintf(shfp, " \\\n\t\"text/plain\" \"Report\" %s.nr", factionid(f)); if (!nocr && (f->options & wants_computer_report || f->age<3)) fprintf(shfp, " \\\n\t\"text/x-eressea-cr\" \"Computer-Report\" %s.cr", factionid(f)); } fprintf(BAT, ". %s.sh %s\n", factionid(f), f->email); fclose(shfp); } if (!gotit) printf("* Fehler: Kein Report für Partei Nr. %s!\n", factionid(f)); while (a) { attrib * a_old = a; a = a->nexttype; a_remove(&f->attribs, a_old); } if (quiet) printf("\n"); } /* schliesst BAT und verschickt Zeitungen und Kommentare */ current_faction = NULL; seen_done(); closebatch(BAT); } void reports_cleanup(void) { int i; for (i=0;i!=FMAXHASH;++i) { while (fsee[i]) { struct fsee * fs = fsee[i]->nexthash; free(fsee[i]); fsee[i] = fs; } } } unit * can_find(faction * f, faction * f2) { int key = f->no % FMAXHASH; struct fsee * fs = fsee[key]; struct see * ss; if (f==f2) return f->units; while (fs && fs->f!=f) fs=fs->nexthash; if (!fs) return NULL; ss=fs->see; while (ss && ss->seen!=f2) ss=ss->next; if (ss) { assert(ss->proof->faction==f2); return ss->proof; } return NULL; } void add_find(faction * f, unit * u) { /* faction f sees f2 through u */ faction * f2 = u->faction; int key = f->no % FMAXHASH; struct fsee ** fp = &fsee[key]; struct fsee * fs; struct see ** sp; struct see * ss; while (*fp && (*fp)->f!=f) fp=&(*fp)->nexthash; if (!*fp) { fs = *fp = calloc(sizeof(struct fsee), 1); fs->f = f; } else fs = *fp; sp = &fs->see; while (*sp && (*sp)->seen!=f2) sp=&(*sp)->next; if (!*sp) { ss = *sp = calloc(sizeof(struct see), 1); ss->proof = u; ss->seen = f2; } else ss = *sp; ss->proof = u; } void update_find(void) { region * r; static boolean initial = true; if (initial) for (r=regions;r;r=r->next) { unit * u; for (u=r->units;u;u=u->next) { faction * lastf = u->faction; unit * u2; for (u2=r->units;u2;u2=u2->next) { if (u2->faction==lastf || u2->faction==u->faction) continue; if (seefaction(u->faction, r, u2, 0)) { lastf=u2->faction; add_find(u->faction, u2); } } } } initial = false; } boolean kann_finden(faction * f1, faction * f2) { update_find(); return (boolean)(can_find(f1, f2)!=NULL); } /******* summary *******/ int dropouts[2]; int * age = NULL; typedef struct summary { int waffen; int factions; int artefakte; int ruestungen; int schiffe; int gebaeude; int maxskill; int inhabitedregions; int peasants; int peasantmoney; int nunits; int playerpop; int playermoney; int armed_men; int poprace[MAXRACES]; int factionrace[MAXRACES]; int landregionen; int regionen_mit_spielern; int landregionen_mit_spielern; int orkifizierte_regionen; int spielerpferde; int pferde; } summary; summary * make_summary(boolean count_new) { char sk; faction *f; region *r; unit *u; summary * s = calloc(1, sizeof(summary)); for (f = factions; f; f = f->next) { f->nregions = 0; f->nunits = 0; f->number = 0; f->money = 0; if (count_new == true || f->age > 0) s->factions++; } /* Alles zählen */ for (r = regions; r; r = r->next) { s->pferde += rhorses(r); s->schiffe += listlen(r->ships); s->gebaeude += listlen(r->buildings); if (rterrain(r) != T_OCEAN) { s->landregionen++; if (r->units) { s->landregionen_mit_spielern++; } if (fval(r, RF_ORCIFIED)) { s->orkifizierte_regionen++; } } if (r->units) { s->regionen_mit_spielern++; } if (rpeasants(r) || r->units) { s->inhabitedregions++; s->peasants += rpeasants(r); s->peasantmoney += rmoney(r); /* Einheiten Info. nregions darf nur einmal pro Partei * incrementiert werden. */ for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); for (u = r->units; u; u = u->next) { f = u->faction; if (u->faction->no != MONSTER_FACTION) { s->nunits++; s->playerpop += u->number; s->spielerpferde += get_item(u, I_HORSE); s->playermoney += get_money(u); s->armed_men += armedmen(u); s->waffen += get_item(u, I_SWORD); s->waffen += get_item(u, I_SPEAR); s->waffen += get_item(u, I_CATAPULT); s->waffen += get_item(u, I_CROSSBOW); s->waffen += get_item(u, I_LONGBOW); s->waffen += get_item(u, I_RUNESWORD); s->ruestungen += get_item(u, I_CHAIN_MAIL); s->ruestungen += get_item(u, I_PLATE_ARMOR); #ifdef COMPATIBILITY s->ruestungen += get_item(u, I_CLOAK_OF_INVULNERABILITY); s->artefakte += get_item(u, I_AMULET_OF_DARKNESS); s->artefakte += get_item(u, I_AMULET_OF_HEALING); s->artefakte += get_item(u, I_CLOAK_OF_INVULNERABILITY); s->artefakte += get_item(u, I_SHIELDSTONE); s->artefakte += get_item(u, I_STAFF_OF_FIRE); s->artefakte += get_item(u, I_STAFF_OF_LIGHTNING); s->artefakte += get_item(u, I_WAND_OF_TELEPORTATION); #endif s->artefakte += get_item(u, I_AMULET_OF_TRUE_SEEING); s->artefakte += get_item(u, I_RING_OF_INVISIBILITY); s->artefakte += get_item(u, I_RING_OF_POWER); s->artefakte += get_item(u, I_RUNESWORD); s->spielerpferde += get_item(u, I_HORSE); for (sk = 0; sk < MAXSKILLS; sk++) { int aktskill; aktskill = eff_skill(u, sk, r); if (aktskill > s->maxskill) s->maxskill = aktskill; } if (!fval(f, FL_DH)) { f->nregions++; fset(f, FL_DH); } } f->nunits++; f->number += u->number; f->money += get_money(u); s->poprace[u->race] += u->number; } } } /* jetzt noch parteienweise zählen */ /* Problem mit Monsterpartei ... */ for (f = factions; f; f = f->next) if (f->no!=MONSTER_FACTION) { if (count_new == true || f->age > 0) s->factionrace[f->race]++; } return s; } static char * pcomp(int i, int j) { sprintf(buf, "%d (%s%d)", i, (i>=j)?"+":"", i-j); return buf; } static char * rcomp(int i, int j) { sprintf(buf, "%d (%s%d,%s%d%%)", i, (i>=j)?"+":"", i-j, (i>=j)?"+":"",j?((i-j)*100)/j:0); return buf; } static void writemonument(void) { FILE * F; region *r; building *b; building *buildings[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL}; int size[7] = {0,0,0,0,0,0,0}; int i, j, ra; int count = 0; unit *owner; for (r = regions; r; r = r->next) { for (b = r->buildings; b; b = b->next) { if (b->type == &bt_monument && b->display && *b->display) { freset(b, FL_DH); count++; if(b->size > size[6]) { for(i=0; i <= 6; i++) if(b->size >= size[i]) { for(j=5;j >= i; j--) { size[j+1] = size[j]; buildings[j+1] = buildings[j]; } buildings[i] = b; size[i] = b->size; break; } } } } } for(i=0; i <= 6; i++) if(buildings[i]) { fset(buildings[i], FL_DH); } { char zText[MAX_PATH]; sprintf(zText, "%s/news-monument", basepath()); F = cfopen(zText, "w"); if (!F) return; } fprintf(F, "\n--- maintitle ---\n\n"); for(i = 0; i<=6; i++) { if (buildings[i] != NULL) { fprintf(F, "In %s", rname(buildings[i]->region, NULL)); if ((owner=buildingowner(buildings[i]->region,buildings[i]))!=NULL && !fval(owner,FL_PARTEITARNUNG)) { fprintf(F, ", Eigentümer: %s", factionname(owner->faction)); } fprintf(F, "\n\n"); report_building(F, buildings[i]->region, buildings[i], findfaction(0), see_neighbour); fprintf(F, "\n\n"); } } fprintf(F, "\n--- newcomer ---\n\n"); if(count > 7) { ra = rand()%(count-7); j = 0; for (r = regions; r; r = r->next) { for (b = r->buildings; b; b = b->next) { if (b->type == &bt_monument && b->display && *b->display && !fval(b, FL_DH)) { j++; if(j == ra) { fprintf(F, "In %s", rname(b->region, NULL)); if ((owner=buildingowner(b->region,b))!=NULL && !fval(owner,FL_PARTEITARNUNG)) { fprintf(F, ", Eigentümer: %s", factionname(owner->faction)); } fprintf(F, "\n\n"); report_building(F, b->region, b, findfaction(0), see_neighbour); fprintf(F, "\n\n"); } } } } } fclose(F); #ifdef OLD_ITEMS { char zText[MAX_PATH]; sprintf(zText, "%s/news-silly", basepath()); F = cfopen(zText, "w"); if (!F) return; } for (f=factions;f;f=f->next) { unit * u; int k; int count = 0; for (u=f->units;u;u=u->nextF) { count += get_herb(u, h); } for (i=0;i!=3;++i) if (countnext) { if (f->no != MONSTER_FACTION) { fprintf(F, "%s:%s:%s\n", factionname(f), f->email, f->banner); } } fclose(F); } static void writeforward(void) { FILE *forwardFile; region *r; unit *u; { char zText[MAX_PATH]; sprintf(zText, "%s/aliases", basepath()); forwardFile = cfopen(zText, "w"); if (!forwardFile) return; } for (r = regions; r; r = r->next) { for (u = r->units; u; u = u->next) { if (u->faction->no != MONSTER_FACTION) { fprintf(forwardFile,"einheit-%s: %s\n", unitid(u), u->faction->email); } } } fclose(forwardFile); } void report_summary(summary * s, summary * o, boolean full) { FILE * F = NULL; int i, newplayers = 0; faction * f; int nmrs[ORDERGAP]; { char zText[MAX_PATH]; if (full) { sprintf(zText, "%s/parteien.full", basepath()); } else { sprintf(zText, "%s/parteien", basepath()); } F = cfopen(zText, "w"); if (!F) return; } printf("Schreibe Zusammenfassung (parteien)...\n"); fprintf(F, "%s\n%s\n\n", global.gamename, gamedate2()); fprintf(F, "Auswertung Nr: %d\n", turn); fprintf(F, "Parteien: %s\n", pcomp(s->factions, o->factions)); fprintf(F, "Einheiten: %s\n\n", pcomp(s->nunits, o->nunits)); fprintf(F, "Spielerpopulation: %s\n", pcomp(s->playerpop, o->playerpop)); fprintf(F, " davon bewaffnet: %s\n", pcomp(s->armed_men, o->armed_men)); if (full) { fprintf(F, "Regionen: %d\n", listlen(regions)); fprintf(F, "Bewohnte Regionen: %d\n", s->inhabitedregions); fprintf(F, "Landregionen: %d\n", s->landregionen); fprintf(F, "Spielerregionen: %d\n", s->regionen_mit_spielern); fprintf(F, "Landspielerregionen: %d\n", s->landregionen_mit_spielern); fprintf(F, "Orkifizierte Regionen: %d\n\n", s->orkifizierte_regionen); } for (i = 0; i <= RC_AQUARIAN ; i++) if (s->factionrace[i] && !nonplayer_race(i)) { fprintf(F, "%14svölker: %s\n", race[i].name[3], pcomp(s->factionrace[i], o->factionrace[i])); } fprintf(F, "\n"); if (full) { for (i = 0; i < MAXRACES; i++) if (s->poprace[i]) { fprintf(F, "%20s: %s\n",race[i].name[1], rcomp(s->poprace[i],o->poprace[i])); } } else { for (i = 0; i <= RC_AQUARIAN; i++) if (s->poprace[i] && !nonplayer_race(i)) { fprintf(F, "%20s: %s\n",race[i].name[1], rcomp(s->poprace[i],o->poprace[i])); } } if (full) { fprintf(F, "\nWaffen: %s\n", pcomp(s->waffen,o->waffen)); fprintf(F, "Rüstungen: %s\n", pcomp(s->ruestungen,o->ruestungen)); fprintf(F, "ungezähmte Pferde: %s\n", pcomp(s->pferde, o->pferde)); fprintf(F, "gezähmte Pferde: %s\n", pcomp(s->spielerpferde,o->spielerpferde)); fprintf(F, "Artefakte: %s\n", pcomp(s->artefakte,o->artefakte)); fprintf(F, "Schiffe: %s\n", pcomp(s->schiffe, o->schiffe)); fprintf(F, "Gebäude: %s\n", pcomp(s->gebaeude, o->gebaeude)); fprintf(F, "\nBauernpopulation: %s\n", pcomp(s->peasants,o->peasants)); fprintf(F, "Population gesamt: %d\n\n", s->playerpop+s->peasants); fprintf(F, "Reichtum Spieler: $%s\n", pcomp(s->playermoney,o->playermoney)); fprintf(F, "Reichtum Bauern: $%s\n", pcomp(s->peasantmoney, o->peasantmoney)); fprintf(F, "Reichtum gesamt: $%d\n\n", s->playermoney+s->peasantmoney); } fprintf(F, "\n\n"); for (i = 0; i != ORDERGAP; i++) { nmrs[i] = 0; } for (f = factions; f; f = f->next) { if (f->age <= 1 && turn - f->lastorders == 1) { newplayers++; } else if (f->no != MONSTER_FACTION) { nmrs[min(ORDERGAP-1,turn-f->lastorders)]++; } } for (i = 0; i != ORDERGAP; i++) { fprintf(F, "%d %s:\t\t %d\n", i, i != 1 ? "NMRs" : "NMR ", nmrs[i]); } if (age) { if (age[2] != 0) { fprintf(F, "Erstabgaben:\t %d%%\n", 100 - (dropouts[0] * 100 / age[2])); } if (age[3] != 0) { fprintf(F, "Zweitabgaben:\t %d%%\n", 100 - (dropouts[1] * 100 / age[3])); } } fprintf(F, "Neue Spieler:\t %d\n", newplayers); if (factions) fprintf(F, "\nParteien:\n\n"); for (f = factions; f; f = f->next) fprintf(F, "%s (%.3s/%.3s), %d Einh., %d Pers., $%d, %d %s\n", factionname(f), race[f->race].name[0], neue_gebiete[f->magiegebiet], f->nunits, f->number, f->money, turn - f->lastorders, turn - f->lastorders != 1 ? "NMRs" : "NMR "); fclose(F); if (full) { FILE * F; #ifdef PLAYER_CSV region * r; #endif printf("Schreibe Liste der Adressen (adressen)...\n"); writeadresses(); writeforward(); { char zText[MAX_PATH]; sprintf(zText, "%s/datum", basepath()); F = cfopen(zText, "w"); if (!F) return; } printf("Schreibe Datum (datum)...\n"); fputs(gamedate2(), F); fclose(F); #ifdef PLAYER_CSV { strcpy(zText, "%s/players", basepath()); F = cfopen(zText, "w"); } if (!F) return; printf("Schreibe Spielerliste (players)...\n"); r = findregion(0, 0); if (r) for (f=factions;f;f=f->next) { fputs("id;name;email;info;age;x;y;nmr;score;race;magic;units;people;money", F); fprintf(F, "%s;\"%s\";\"%s\";\"%s\";\"%s\";%d;%d;%d;%d;%d;" "\"%s\";\"%s\";" "%d;%d;%d\n", factionid(f), f->name, f->email, f->banner, f->passw, f->age, region_x(r, f), region_y(r, f), turn-f->lastorders, f->score, race[f->race].name[0], neue_gebiete[f->magiegebiet], f->nunits, f->number, f->money); } fclose(F); #endif writemonument(); } } /******* end summary ******/