forked from github/server
940d236edc
- moved some summary functionality (emails, aliases) to Lua code - made default.lua an include for all the different run-scripts report.c is the last file that needs some work, everything else should be fine. Most importantly, all the messages are not internationalized, so that means just about no more German in the code. Yay!
1613 lines
40 KiB
C
1613 lines
40 KiB
C
/* vi: set ts=2:
|
|
*
|
|
* Eressea PB(E)M host Copyright (C) 1998-2003
|
|
* Christian Schlittchen (corwin@amber.kn-bremen.de)
|
|
* Katja Zedel (katze@felidae.kn-bremen.de)
|
|
* Henning Peters (faroul@beyond.kn-bremen.de)
|
|
* Enno Rehling (enno@eressea-pbem.de)
|
|
* Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
|
|
*
|
|
* based on:
|
|
*
|
|
* Atlantis v1.0 13 September 1993 Copyright 1993 by Russell Wallace
|
|
* Atlantis v1.7 Copyright 1996 by Alex Schröder
|
|
*
|
|
* This program may not be used, modified or distributed without
|
|
* prior permission by the authors of Eressea.
|
|
* This program may not be sold or used commercially without prior written
|
|
* permission from the authors.
|
|
*/
|
|
#include <config.h>
|
|
#include "eressea.h"
|
|
#include "reports.h"
|
|
|
|
/* kernel includes */
|
|
#include <kernel/border.h>
|
|
#include <kernel/building.h>
|
|
#include <kernel/curse.h>
|
|
#include <kernel/faction.h>
|
|
#include <kernel/group.h>
|
|
#include <kernel/item.h>
|
|
#include <kernel/karma.h>
|
|
#include <kernel/magic.h>
|
|
#include <kernel/message.h>
|
|
#include <kernel/order.h>
|
|
#include <kernel/plane.h>
|
|
#include <kernel/race.h>
|
|
#include <kernel/region.h>
|
|
#include <kernel/ship.h>
|
|
#include <kernel/skill.h>
|
|
#include <kernel/terrain.h>
|
|
#include <kernel/unit.h>
|
|
|
|
/* util includes */
|
|
#include <util/attrib.h>
|
|
#include <util/bsdstring.h>
|
|
#include <util/base36.h>
|
|
#include <util/functions.h>
|
|
#include <util/goodies.h>
|
|
#include <util/lists.h>
|
|
#include <util/log.h>
|
|
|
|
/* libc includes */
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
|
|
/* attributes includes */
|
|
#include <attributes/follow.h>
|
|
#include <attributes/otherfaction.h>
|
|
#include <attributes/racename.h>
|
|
#include <attributes/viewrange.h>
|
|
|
|
const char * g_reportdir;
|
|
const char * visibility[] = {
|
|
"none",
|
|
"neighbour",
|
|
"lighthouse",
|
|
"travel",
|
|
"far",
|
|
"unit",
|
|
"battle"
|
|
};
|
|
|
|
const char *coasts[MAXDIRECTIONS] =
|
|
{
|
|
"coast::nw",
|
|
"coast::ne",
|
|
"coast::e",
|
|
"coast::se",
|
|
"coast::sw",
|
|
"coast::w"
|
|
};
|
|
|
|
const char *
|
|
reportpath(void)
|
|
{
|
|
static char zText[MAX_PATH];
|
|
if (g_reportdir) return g_reportdir;
|
|
return strcat(strcpy(zText, basepath()), "/reports");
|
|
}
|
|
|
|
static char *
|
|
groupid(const struct group * g, const struct faction * f)
|
|
{
|
|
typedef char name[OBJECTIDSIZE + 1];
|
|
static name idbuf[8];
|
|
static int nextbuf = 0;
|
|
char *buf = idbuf[(++nextbuf) % 8];
|
|
sprintf(buf, "%s (%s)", g->name, factionid(f));
|
|
return buf;
|
|
}
|
|
|
|
const xmlChar *
|
|
report_kampfstatus(const unit * u, const struct locale * lang)
|
|
{
|
|
static char fsbuf[64];
|
|
static const char * azstatus[] = {
|
|
"status_aggressive", "status_front",
|
|
"status_rear", "status_defensive",
|
|
"status_avoid", "status_flee" };
|
|
|
|
xstrlcpy(fsbuf, LOC(lang, azstatus[u->status]), sizeof(fsbuf));
|
|
if (fval(u, UFL_NOAID)) {
|
|
strcat(fsbuf, ", ");
|
|
xstrcat(fsbuf, LOC(lang, "status_noaid"));
|
|
}
|
|
|
|
return (xmlChar *)fsbuf;
|
|
}
|
|
|
|
const char *
|
|
hp_status(const unit * u)
|
|
{
|
|
double p = (double) ((double) u->hp / (double) (u->number * unit_max_hp(u)));
|
|
|
|
if (p > 2.00) return mkname("damage", "critical");
|
|
if (p > 1.50) return mkname("damage", "heavily");
|
|
if (p < 0.50) return mkname("damage", "badly");
|
|
if (p < 0.75) return mkname("damage", "wounded");
|
|
if (p < 0.99) return mkname("damage", "exhausted");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
report_item(const unit * owner, const item * i, const faction * viewer, const char ** name, const char ** basename, int * number, boolean singular)
|
|
{
|
|
assert(owner->number);
|
|
if (owner->faction == viewer) {
|
|
if (name) *name = locale_string(viewer->locale, resourcename(i->type->rtype, ((i->number!=1 && !singular)?GR_PLURAL:0)));
|
|
if (basename) *basename = resourcename(i->type->rtype, 0);
|
|
if (number) *number = i->number;
|
|
} else if (i->type->rtype==r_silver) {
|
|
int pp = i->number/owner->number;
|
|
if (number) *number = 1;
|
|
if (pp > 50000 && dragonrace(owner->race)) {
|
|
if (name) *name = locale_string(viewer->locale, "dragonhoard");
|
|
if (basename) *basename = "dragonhoard";
|
|
} else if (pp > 5000) {
|
|
if (name) *name = locale_string(viewer->locale, "moneychest");
|
|
if (basename) *basename = "moneychest";
|
|
} else if (pp > 500) {
|
|
if (name) *name = locale_string(viewer->locale, "moneybag");
|
|
if (basename) *basename = "moneybag";
|
|
} else {
|
|
if (number) *number = 0;
|
|
if (name) *name = NULL;
|
|
if (basename) *basename = NULL;
|
|
}
|
|
} else {
|
|
if (name) *name = locale_string(viewer->locale, resourcename(i->type->rtype, NMF_APPEARANCE|((i->number!=1 && !singular)?GR_PLURAL:0)));
|
|
if (basename) *basename = resourcename(i->type->rtype, NMF_APPEARANCE);
|
|
if (number) {
|
|
if (fval(i->type, ITF_HERB)) *number = 1;
|
|
else *number = i->number;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int * nmrs = NULL;
|
|
|
|
int
|
|
update_nmrs(void)
|
|
{
|
|
int i, newplayers =0;
|
|
faction *f;
|
|
int turn = global.data_turn;
|
|
|
|
if (nmrs==NULL) nmrs = malloc(sizeof(int)*(NMRTimeout()+1));
|
|
for (i = 0; i <= NMRTimeout(); ++i) {
|
|
nmrs[i] = 0;
|
|
}
|
|
|
|
for (f = factions; f; f = f->next) {
|
|
if (fval(f, FFL_ISNEW)) {
|
|
++newplayers;
|
|
} else if (f->no != MONSTER_FACTION) {
|
|
int nmr = turn-f->lastorders+1;
|
|
if (nmr<0 || nmr>NMRTimeout()) {
|
|
log_error(("faction %s has %d NMRS\n", factionid(f), nmr));
|
|
nmr = max(0, nmr);
|
|
nmr = min(nmr, NMRTimeout());
|
|
}
|
|
++nmrs[nmr];
|
|
}
|
|
}
|
|
return newplayers;
|
|
}
|
|
|
|
#define ORDERS_IN_NR 1
|
|
static size_t
|
|
buforder(char * bufp, size_t size, const order * ord, int mode)
|
|
{
|
|
size_t tsize = 0, rsize;
|
|
|
|
rsize = strlcpy(bufp, ", \"", size);
|
|
tsize += rsize;
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
if (mode<ORDERS_IN_NR) {
|
|
char * cmd = getcommand(ord);
|
|
rsize = strlcpy(bufp, cmd, size);
|
|
free(cmd);
|
|
} else {
|
|
rsize = strlcpy(bufp, "...", size);
|
|
}
|
|
tsize += rsize;
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
|
|
if (size>1) {
|
|
strcpy(bufp, "\"");
|
|
++tsize;
|
|
}
|
|
return tsize;
|
|
}
|
|
|
|
int
|
|
bufunit(const faction * f, const unit * u, int indent, int mode, char * buf, size_t size)
|
|
{
|
|
int i, dh;
|
|
int getarnt = fval(u, UFL_PARTEITARNUNG);
|
|
const char *pzTmp, *str;
|
|
building * b;
|
|
boolean isbattle = (boolean)(mode == see_battle);
|
|
int telepath_see = 0;
|
|
attrib *a_fshidden = NULL;
|
|
item * itm;
|
|
item * show;
|
|
faction *fv = visible_faction(f, u);
|
|
char * bufp = buf;
|
|
boolean itemcloak = false;
|
|
static const curse_type * itemcloak_ct = 0;
|
|
static boolean init = false;
|
|
size_t rsize;
|
|
|
|
if (!init) {
|
|
init = true;
|
|
itemcloak_ct = ct_find("itemcloak");
|
|
}
|
|
if (itemcloak_ct!=NULL) {
|
|
itemcloak = curse_active(get_curse(u->attribs, itemcloak_ct));
|
|
}
|
|
|
|
#ifdef KARMA_MODULE
|
|
if (fspecial(u->faction, FS_HIDDEN)) {
|
|
a_fshidden = a_find(u->attribs, &at_fshidden);
|
|
}
|
|
telepath_see = fspecial(f, FS_TELEPATHY);
|
|
#endif /* KARMA_MODULE */
|
|
|
|
rsize = strlcpy(bufp, unitname(u), size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
|
|
if (!isbattle) {
|
|
attrib *a_otherfaction = a_find(u->attribs, &at_otherfaction);
|
|
if (u->faction == f) {
|
|
if (fval(u, UFL_GROUP)) {
|
|
attrib *a = a_find(u->attribs, &at_group);
|
|
if (a) {
|
|
group * g = (group*)a->data.v;
|
|
rsize = strlcpy(bufp, ", ", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
rsize = strlcpy(bufp, groupid(g, f), size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
}
|
|
}
|
|
if (getarnt) {
|
|
rsize = strlcpy(bufp, ", ", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
rsize = strlcpy(bufp, LOC(f->locale, "anonymous"), size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
} else if (a_otherfaction) {
|
|
faction * otherfaction = get_otherfaction(a_otherfaction);
|
|
if (otherfaction) {
|
|
rsize = strlcpy(bufp, ", ", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
rsize = strlcpy(bufp, factionname(otherfaction), size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
}
|
|
}
|
|
} else {
|
|
if (getarnt) {
|
|
rsize = strlcpy(bufp, ", ", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
rsize = strlcpy(bufp, LOC(f->locale, "anonymous"), size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
} else {
|
|
if (a_otherfaction && alliedunit(u, f, HELP_FSTEALTH)) {
|
|
faction * f = get_otherfaction(a_otherfaction);
|
|
bufp += snprintf(bufp, size, ", %s (%s)", factionname(f), factionname(u->faction));
|
|
size = sizeof(buf)-(bufp-buf);
|
|
} else {
|
|
rsize = strlcpy(bufp, ", ", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
rsize = strlcpy(bufp, factionname(fv), size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
rsize = strlcpy(bufp, ", ", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
|
|
if (u->faction != f && a_fshidden && a_fshidden->data.ca[0] == 1 && effskill(u, SK_STEALTH) >= 6) {
|
|
rsize = strlcpy(bufp, "? ", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
} else {
|
|
bufp += snprintf(bufp, size, "%d ", u->number);
|
|
size = sizeof(buf)-(bufp-buf);
|
|
}
|
|
|
|
pzTmp = get_racename(u->attribs);
|
|
if (pzTmp) {
|
|
rsize = strlcpy(bufp, pzTmp, size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
if (u->faction==f && fval(u->race, RCF_SHAPESHIFTANY)) {
|
|
rsize = strlcpy(bufp, " (", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
rsize = strlcpy(bufp, racename(f->locale, u, u->race), size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
if (size>1) {
|
|
strcpy(bufp++, ")");
|
|
--size;
|
|
}
|
|
}
|
|
} else {
|
|
rsize = strlcpy(bufp, racename(f->locale, u, u->irace), size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
if (u->faction==f && u->irace!=u->race) {
|
|
rsize = strlcpy(bufp, " (", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
rsize = strlcpy(bufp, racename(f->locale, u, u->race), size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
if (size>1) {
|
|
strcpy(bufp++, ")");
|
|
--size;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef HEROES
|
|
if (fval(u, UFL_HERO) && (u->faction == f || omniscient(f))) {
|
|
rsize = strlcpy(bufp, ", ", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
rsize = strlcpy(bufp, LOC(f->locale, "hero"), size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
}
|
|
#endif
|
|
/* status */
|
|
|
|
if (u->number && (u->faction == f || telepath_see || isbattle)) {
|
|
const char * c = locale_string(f->locale, hp_status(u));
|
|
rsize = strlcpy(bufp, ", ", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
rsize = strlcpy(bufp, report_kampfstatus(u, f->locale), size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
if (c || fval(u, UFL_HUNGER)) {
|
|
rsize = strlcpy(bufp, " (", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
if (c) {
|
|
rsize = strlcpy(bufp, c, size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
}
|
|
if (fval(u, UFL_HUNGER)) {
|
|
if (c) rsize = strlcpy(bufp, ", hungert", size);
|
|
else rsize = strlcpy(bufp, "hungert", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
}
|
|
if (size>1) {
|
|
strcpy(bufp++, ")");
|
|
--size;
|
|
}
|
|
}
|
|
}
|
|
if (getguard(u)) {
|
|
rsize = strlcpy(bufp, ", ", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
rsize = strlcpy(bufp, LOC(f->locale, "unit_guards"), size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
}
|
|
|
|
if ((b = usiege(u))!=NULL) {
|
|
rsize = strlcpy(bufp, ", belagert ", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
rsize = strlcpy(bufp, buildingname(b), size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
}
|
|
|
|
dh = 0;
|
|
if (u->faction == f || telepath_see) {
|
|
skill * sv;
|
|
for (sv = u->skills;sv!=u->skills+u->skill_size;++sv) {
|
|
rsize = spskill(bufp, size, f->locale, u, sv, &dh, 1);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
}
|
|
}
|
|
|
|
dh = 0;
|
|
if (f == u->faction || telepath_see || omniscient(f)) {
|
|
show = u->items;
|
|
} else if (!itemcloak && mode >= see_unit && !(a_fshidden
|
|
&& a_fshidden->data.ca[1] == 1 && effskill(u, SK_STEALTH) >= 3))
|
|
{
|
|
show = NULL;
|
|
for (itm=u->items;itm;itm=itm->next) {
|
|
item * ishow;
|
|
const char * ic;
|
|
int in;
|
|
report_item(u, itm, f, NULL, &ic, &in, false);
|
|
if (ic && *ic && in>0) {
|
|
for (ishow = show; ishow; ishow=ishow->next) {
|
|
const char * sc;
|
|
int sn;
|
|
if (ishow->type==itm->type) sc=ic;
|
|
else report_item(u, ishow, f, NULL, &sc, &sn, false);
|
|
if (sc==ic || strcmp(sc, ic)==0) {
|
|
ishow->number+=itm->number;
|
|
break;
|
|
}
|
|
}
|
|
if (ishow==NULL) {
|
|
ishow = i_add(&show, i_new(itm->type, itm->number));
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
show = NULL;
|
|
}
|
|
for (itm=show; itm; itm=itm->next) {
|
|
const char * ic;
|
|
int in;
|
|
report_item(u, itm, f, &ic, NULL, &in, false);
|
|
if (in==0 || ic==NULL) continue;
|
|
rsize = strlcpy(bufp, ", ", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
|
|
if (!dh) {
|
|
bufp += snprintf(bufp, size, "%s: ", LOC(f->locale, "nr_inventory"));
|
|
size = sizeof(buf)-(bufp-buf);
|
|
dh = 1;
|
|
}
|
|
if (in == 1) {
|
|
rsize = strlcpy(bufp, ic, size);
|
|
} else {
|
|
rsize = snprintf(bufp, size, "%d %s", in, ic);
|
|
}
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
}
|
|
if (show!=u->items) while (show) i_free(i_remove(&show, show));
|
|
|
|
if (u->faction == f || telepath_see) {
|
|
sc_mage * m = get_mage(u);
|
|
|
|
if (m!=NULL) {
|
|
spell_list *slist = m->spells;
|
|
int t = effskill(u, SK_MAGIC);
|
|
bufp += snprintf(bufp, size, ". Aura %d/%d", get_spellpoints(u), max_spellpoints(u->region,u));
|
|
size = sizeof(buf)-(bufp-buf);
|
|
|
|
for (dh=0; slist; slist=slist->next) {
|
|
spell * sp = slist->data;
|
|
if (sp->level > t) continue;
|
|
if (!dh) {
|
|
rsize = snprintf(bufp, size, ", %s: ", LOC(f->locale, "nr_spells"));
|
|
dh = 1;
|
|
} else {
|
|
rsize = strlcpy(bufp, ", ", size);
|
|
}
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
rsize = strlcpy(bufp, spell_name(sp, f->locale), size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
}
|
|
|
|
for (i=0; i!=MAXCOMBATSPELLS; ++i) {
|
|
if (get_combatspell(u, i)) break;
|
|
}
|
|
if (i!=MAXCOMBATSPELLS) {
|
|
bufp += snprintf(bufp, size, ", %s: ", LOC(f->locale, "nr_combatspells"));
|
|
size = sizeof(buf)-(bufp-buf);
|
|
dh = 0;
|
|
for (i = 0; i < MAXCOMBATSPELLS; i++){
|
|
const spell *sp;
|
|
if (!dh){
|
|
dh = 1;
|
|
} else {
|
|
rsize = strlcpy(bufp, ", ", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
}
|
|
sp = get_combatspell(u,i);
|
|
if (sp) {
|
|
int sl = get_combatspelllevel(u, i);
|
|
rsize = strlcpy(bufp, spell_name(sp, u->faction->locale), size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
|
|
if (sl > 0) {
|
|
bufp += snprintf(bufp, size, " (%d)", sl);
|
|
size = sizeof(buf)-(bufp-buf);
|
|
}
|
|
} else {
|
|
rsize = strlcpy(bufp, LOC(f->locale, "nr_nospells"), size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#ifdef LASTORDER
|
|
if (!isbattle && u->lastorder) {
|
|
rsize = buforder(bufp, size, u->lastorder, 0);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
}
|
|
#else
|
|
if (!isbattle) {
|
|
boolean printed = 0;
|
|
order * ord;;
|
|
for (ord=u->old_orders;ord;ord=ord->next) {
|
|
if (is_repeated(ord)) {
|
|
if (printed<ORDERS_IN_NR) {
|
|
rsize = buforder(bufp, size, ord, printed++);
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
} else break;
|
|
}
|
|
}
|
|
if (printed<ORDERS_IN_NR) for (ord=u->orders;ord;ord=ord->next) {
|
|
if (is_repeated(ord)) {
|
|
if (printed<ORDERS_IN_NR) {
|
|
rsize = buforder(bufp, size, ord, printed++);
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
} else break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
i = 0;
|
|
|
|
str = u_description(u, f->locale);
|
|
if (str) {
|
|
rsize = strlcpy(bufp, "; ", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
|
|
rsize = strlcpy(bufp, str, size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
|
|
i = str[strlen(str) - 1];
|
|
}
|
|
if (i != '!' && i != '?' && i != '.') {
|
|
if (size>1) {
|
|
strcpy(bufp++, ".");
|
|
--size;
|
|
}
|
|
}
|
|
pzTmp = uprivate(u);
|
|
if (u->faction == f && pzTmp) {
|
|
rsize = strlcpy(bufp, " (Bem: ", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
rsize = strlcpy(bufp, pzTmp, size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
rsize = strlcpy(bufp, ")", size);
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
}
|
|
|
|
dh=0;
|
|
if (!getarnt && f) {
|
|
if (alliedfaction(u->region->planep, f, fv, HELP_ALL)) {
|
|
dh = 1;
|
|
}
|
|
}
|
|
return dh;
|
|
}
|
|
|
|
/* TODO: telepath_see wird nicht berücksichtigt: Parteien mit
|
|
* telepath_see sollten immer einzelne Einheiten zu sehen
|
|
* bekommen, alles andere ist darstellungsteschnisch kompliziert.
|
|
*/
|
|
|
|
size_t
|
|
spskill(char * buffer, size_t size, const struct locale * lang, const struct unit * u, struct skill * sv, int *dh, int days)
|
|
{
|
|
char * bufp = buffer;
|
|
int i, effsk;
|
|
size_t rsize;
|
|
size_t tsize = 0;
|
|
|
|
if (!u->number) return 0;
|
|
if (sv->level<=0) {
|
|
if (sv->old<=0 || (u->faction->options & want(O_SHOWSKCHANGE))==0) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
rsize = strlcpy(bufp, ", ", size);
|
|
tsize += rsize;
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
|
|
if (!*dh) {
|
|
rsize = strlcpy(bufp, LOC(lang, "nr_skills"), size);
|
|
tsize += rsize;
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
|
|
rsize = strlcpy(bufp, ": ", size);
|
|
tsize += rsize;
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
|
|
*dh = 1;
|
|
}
|
|
rsize = strlcpy(bufp, skillname(sv->id, lang), size);
|
|
tsize += rsize;
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
|
|
rsize = strlcpy(bufp, " ", size);
|
|
tsize += rsize;
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
|
|
if (sv->id == SK_MAGIC){
|
|
if (find_magetype(u) != M_GRAU){
|
|
rsize = strlcpy(bufp, LOC(lang, mkname("school", magietypen[find_magetype(u)])), size);
|
|
tsize += rsize;
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
|
|
rsize = strlcpy(bufp, " ", size);
|
|
tsize += rsize;
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
}
|
|
}
|
|
|
|
if (sv->id == SK_STEALTH && fval(u, UFL_STEALTH)) {
|
|
i = u_geteffstealth(u);
|
|
if (i>=0) {
|
|
rsize = slprintf(bufp, size, "%d/", i);
|
|
tsize += rsize;
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
}
|
|
}
|
|
|
|
effsk = effskill(u, sv->id);
|
|
rsize = slprintf(bufp, size, "%d", effsk);
|
|
tsize += rsize;
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
|
|
if (u->faction->options & want(O_SHOWSKCHANGE)) {
|
|
int oldeff = 0;
|
|
int diff;
|
|
|
|
if (sv->old > 0) {
|
|
oldeff = sv->old + get_modifier(u, sv->id, sv->old, u->region, false);
|
|
}
|
|
|
|
oldeff = max(0, oldeff);
|
|
diff = effsk - oldeff;
|
|
|
|
if (diff != 0) {
|
|
rsize = slprintf(bufp, size, " (%s%d)", (diff>0)?"+":"", diff);
|
|
tsize += rsize;
|
|
if (rsize>size) rsize = size-1;
|
|
size -= rsize;
|
|
bufp += rsize;
|
|
}
|
|
}
|
|
return tsize;
|
|
}
|
|
|
|
void
|
|
lparagraph(struct strlist ** SP, 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 (). */
|
|
|
|
char *buflocal = calloc(strlen(s) + indent + 1, sizeof(char));
|
|
|
|
if (indent) {
|
|
memset(buflocal, ' ', indent);
|
|
if (mark)
|
|
buflocal[indent - 2] = mark;
|
|
}
|
|
strcpy(buflocal + indent, s);
|
|
addstrlist(SP, buflocal);
|
|
free(buflocal);
|
|
}
|
|
|
|
void
|
|
spunit(struct strlist ** SP, const struct faction * f, const unit * u, int indent,
|
|
int mode)
|
|
{
|
|
char buf[DISPLAYSIZE];
|
|
int dh = bufunit(f, u, indent, mode, buf, sizeof(buf));
|
|
lparagraph(SP, buf, indent, (char) ((u->faction == f) ? '*' : (dh ? '+' : '-')));
|
|
}
|
|
|
|
struct message *
|
|
msg_curse(const struct curse * c, const void * obj, typ_t typ, int self)
|
|
{
|
|
if (c->type->curseinfo) {
|
|
/* if curseinfo returns NULL, then we don't want to tell the viewer anything. */
|
|
return c->type->curseinfo(obj, typ, c, self);
|
|
} else if (c->type->info_str!=NULL) {
|
|
return msg_message("curseinfo::info_str", "text id", c->type->info_str, c->no);
|
|
} else {
|
|
const char * unknown[] = { "unit_unknown", "region_unknown", "building_unknown", "ship_unknown" };
|
|
log_error(("no curseinfo for %s\n", c->type->cname));
|
|
return msg_message(mkname("curseinfo", unknown[typ]), "id", c->no);
|
|
}
|
|
}
|
|
|
|
const struct unit *
|
|
ucansee(const struct faction *f, const struct unit *u, const struct unit *x)
|
|
{
|
|
if (cansee(f, u->region, u, 0)) return u;
|
|
return x;
|
|
}
|
|
|
|
static void
|
|
add_faction(faction_list ** flist, faction * sf)
|
|
{
|
|
faction_list ** fnew = flist;
|
|
while (*fnew && (*fnew)->data->no < sf->no) {
|
|
fnew =&(*fnew)->next;
|
|
}
|
|
if ((*fnew==NULL) || (*fnew)->data!=sf) {
|
|
faction_list * finsert = malloc(sizeof(faction_list));
|
|
finsert->next = *fnew;
|
|
*fnew = finsert;
|
|
finsert->data = sf;
|
|
}
|
|
}
|
|
|
|
int
|
|
stealth_modifier(int seen_mode)
|
|
{
|
|
switch (seen_mode) {
|
|
case see_unit:
|
|
return 0;
|
|
case see_far:
|
|
case see_lighthouse:
|
|
return -2;
|
|
case see_travel:
|
|
return -1;
|
|
default:
|
|
return INT_MIN;
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_addresses(report_context * ctx)
|
|
{
|
|
/* "TODO: travelthru" */
|
|
seen_region * sr = NULL;
|
|
region *r;
|
|
const faction * lastf = NULL;
|
|
faction_list * flist = calloc(1, sizeof(faction_list));
|
|
|
|
flist->data = ctx->f;
|
|
|
|
for (r=ctx->first;sr==NULL && r!=ctx->last;r=r->next) {
|
|
sr = find_seen(ctx->seen, r);
|
|
}
|
|
|
|
for (;sr!=NULL;sr=sr->next) {
|
|
int stealthmod = stealth_modifier(sr->mode);
|
|
r = sr->r;
|
|
if (sr->mode==see_lighthouse) {
|
|
unit * u = r->units;
|
|
for (;u;u=u->next) {
|
|
faction * sf = visible_faction(ctx->f, u);
|
|
if (lastf!=sf) {
|
|
if (u->building || u->ship || (stealthmod>INT_MIN && cansee(ctx->f, r, u, stealthmod))) {
|
|
add_faction(&flist, sf);
|
|
lastf = sf;
|
|
}
|
|
}
|
|
}
|
|
} if (sr->mode==see_travel) {
|
|
unit * u = r->units;
|
|
while (u) {
|
|
faction * sf = visible_faction(ctx->f, u);
|
|
assert(u->faction!=ctx->f);
|
|
if (lastf!=sf) {
|
|
attrib * a = a_find(r->attribs, &at_travelunit);
|
|
while (a && a->type==&at_travelunit) {
|
|
unit * u2 = (unit*)a->data.v;
|
|
if (u2->faction==ctx->f) {
|
|
if (cansee_unit(u2, u, stealthmod)) {
|
|
add_faction(&flist, sf);
|
|
lastf = sf;
|
|
break;
|
|
}
|
|
}
|
|
a = a->next;
|
|
}
|
|
}
|
|
u = u->next;
|
|
}
|
|
} else if (sr->mode>see_travel) {
|
|
const unit * u = r->units;
|
|
while (u!=NULL) {
|
|
if (u->faction!=ctx->f) {
|
|
faction * sf = visible_faction(ctx->f, u);
|
|
boolean ballied = sf && sf!=ctx->f && sf!=lastf
|
|
&& !fval(u, UFL_PARTEITARNUNG) && cansee(ctx->f, r, u, stealthmod);
|
|
if (ballied || ALLIED(ctx->f, sf)) {
|
|
add_faction(&flist, sf);
|
|
lastf = sf;
|
|
}
|
|
}
|
|
u = u->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ctx->f->alliance != NULL) {
|
|
faction *f2;
|
|
for (f2 = factions; f2; f2 = f2->next) {
|
|
if (f2->alliance != NULL && f2->alliance == ctx->f->alliance) {
|
|
add_faction(&flist, f2);
|
|
}
|
|
}
|
|
}
|
|
ctx->addresses = flist;
|
|
}
|
|
|
|
#define MAXSEEHASH 0x3000
|
|
seen_region * reuse;
|
|
|
|
seen_region **
|
|
seen_init(void)
|
|
{
|
|
return (seen_region **)calloc(MAXSEEHASH, sizeof(seen_region*));
|
|
}
|
|
|
|
void
|
|
seen_done(seen_region * seehash[])
|
|
{
|
|
int i;
|
|
for (i=0;i!=MAXSEEHASH;++i) {
|
|
seen_region * sd = seehash[i];
|
|
if (sd==NULL) continue;
|
|
while (sd->nextHash!=NULL) sd = sd->nextHash;
|
|
sd->nextHash = reuse;
|
|
reuse = seehash[i];
|
|
seehash[i] = NULL;
|
|
}
|
|
free(seehash);
|
|
}
|
|
|
|
void
|
|
free_seen(void)
|
|
{
|
|
while (reuse) {
|
|
seen_region * r = reuse;
|
|
reuse = reuse->nextHash;
|
|
free(r);
|
|
}
|
|
}
|
|
|
|
void
|
|
link_seen(seen_region * seehash[], const region * first, const region * last)
|
|
{
|
|
const region * r = first;
|
|
seen_region * sr = NULL;
|
|
|
|
if (first==last) return;
|
|
|
|
do {
|
|
sr = find_seen(seehash, r);
|
|
r = r->next;
|
|
} while (sr==NULL && r!=last);
|
|
|
|
while (r!=last) {
|
|
seen_region * sn = find_seen(seehash, r);
|
|
if (sn!=NULL) {
|
|
sr->next = sn;
|
|
sr = sn;
|
|
}
|
|
r = r->next;
|
|
}
|
|
sr->next = 0;
|
|
}
|
|
|
|
seen_region *
|
|
find_seen(struct seen_region * seehash[], const region * r)
|
|
{
|
|
unsigned int index = reg_hashkey(r) & (MAXSEEHASH-1);
|
|
seen_region * find = seehash[index];
|
|
while (find) {
|
|
if (find->r==r) return find;
|
|
find=find->nextHash;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
get_seen_interval(report_context * ctx)
|
|
{
|
|
/* this is required to find the neighbour regions of the ones we are in,
|
|
* which may well be outside of [firstregion, lastregion) */
|
|
int i;
|
|
for (i=0;i!=MAXSEEHASH;++i) {
|
|
seen_region * sr = ctx->seen[i];
|
|
while (sr!=NULL) {
|
|
if (ctx->first==NULL || sr->r->index<ctx->first->index) {
|
|
ctx->first = sr->r;
|
|
}
|
|
if (ctx->last!=NULL && sr->r->index>=ctx->last->index) {
|
|
ctx->last = sr->r->next;
|
|
}
|
|
sr = sr->nextHash;
|
|
}
|
|
}
|
|
link_seen(ctx->seen, ctx->first, ctx->last);
|
|
}
|
|
|
|
boolean
|
|
add_seen(struct seen_region * seehash[], struct region * r, unsigned char mode, boolean dis)
|
|
{
|
|
seen_region * find = find_seen(seehash, r);
|
|
if (find==NULL) {
|
|
unsigned int index = reg_hashkey(r) & (MAXSEEHASH-1);
|
|
if (!reuse) reuse = (seen_region*)calloc(1, sizeof(struct seen_region));
|
|
find = reuse;
|
|
reuse = reuse->nextHash;
|
|
find->nextHash = seehash[index];
|
|
seehash[index] = find;
|
|
find->r = r;
|
|
} else if (find->mode >= mode) {
|
|
return false;
|
|
}
|
|
find->mode = mode;
|
|
find->disbelieves |= dis;
|
|
return true;
|
|
}
|
|
|
|
typedef struct report_type {
|
|
struct report_type * next;
|
|
report_fun write;
|
|
const char * extension;
|
|
int flag;
|
|
} report_type;
|
|
|
|
static report_type * report_types;
|
|
|
|
void
|
|
register_reporttype(const char * extension, report_fun write, int flag)
|
|
{
|
|
report_type * type = malloc(sizeof(report_type));
|
|
type->extension = extension;
|
|
type->write = write;
|
|
type->flag = flag;
|
|
type->next = report_types;
|
|
report_types = type;
|
|
}
|
|
|
|
static region_list *
|
|
get_regions_distance(region * root, int radius)
|
|
{
|
|
region_list * rptr, * rlist = NULL;
|
|
region_list ** rp = &rlist;
|
|
add_regionlist(rp, root);
|
|
fset(root, RF_MARK);
|
|
while (*rp) {
|
|
region_list * r = *rp;
|
|
direction_t d;
|
|
rp = &r->next;
|
|
for (d=0;d!=MAXDIRECTIONS;++d) {
|
|
region * rn = rconnect(r->data, d);
|
|
if (rn!=NULL && !fval(rn, RF_MARK) && distance(rn, root)<=radius) {
|
|
add_regionlist(rp, rn);
|
|
fset(rn, RF_MARK);
|
|
}
|
|
}
|
|
}
|
|
for (rptr=rlist;rptr;rptr=rptr->next) {
|
|
freset(rptr->data, RF_MARK);
|
|
}
|
|
return rlist;
|
|
}
|
|
|
|
static void
|
|
view_default(struct seen_region ** seen, region *r, faction *f)
|
|
{
|
|
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;
|
|
}
|
|
if (!b) add_seen(seen, r2, see_neighbour, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
view_neighbours(struct seen_region ** seen, region * r, faction * f)
|
|
{
|
|
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;
|
|
}
|
|
if (!b) {
|
|
if (add_seen(seen, r2, see_far, false)) {
|
|
if (!(fval(r2->terrain, FORBIDDEN_REGION))) {
|
|
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(seen, r3, see_neighbour, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
recurse_regatta(struct seen_region ** seen, region *center, region *r, faction *f, int maxdist)
|
|
{
|
|
direction_t dir;
|
|
int dist = distance(center, r);
|
|
for (dir=0;dir!=MAXDIRECTIONS;++dir) {
|
|
region * r2 = rconnect(r, dir);
|
|
if (r2) {
|
|
int ndist = distance(center, r2);
|
|
if (ndist>dist && fval(r2->terrain, SEA_REGION)) {
|
|
border * b = get_borders(r, r2);
|
|
while (b) {
|
|
if (!b->type->transparent(b, f)) break;
|
|
b = b->next;
|
|
}
|
|
if (!b) {
|
|
if (ndist<maxdist) {
|
|
if (add_seen(seen, r2, see_far, false)) {
|
|
recurse_regatta(seen, center, r2, f, maxdist);
|
|
}
|
|
} else add_seen(seen, r2, see_neighbour, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
view_regatta(struct seen_region ** seen, region * r, faction * f)
|
|
{
|
|
unit *u;
|
|
int skill = 0;
|
|
for (u=r->units; u; u=u->next) {
|
|
if (u->faction==f) {
|
|
int es = effskill(u, SK_OBSERVATION);
|
|
if (es>skill) skill=es;
|
|
}
|
|
}
|
|
recurse_regatta(seen, r, r, f, skill/2);
|
|
}
|
|
|
|
static struct seen_region **
|
|
prepare_report(faction * f)
|
|
{
|
|
region * r;
|
|
region * end = lastregion(f);
|
|
struct seen_region ** seen = seen_init();
|
|
|
|
static const struct building_type * bt_lighthouse = NULL;
|
|
if (bt_lighthouse==NULL) bt_lighthouse = bt_find("lighthouse");
|
|
|
|
for (r = firstregion(f); r != end; r = r->next) {
|
|
attrib *ru;
|
|
unit * u;
|
|
plane * p = r->planep;
|
|
unsigned char mode = see_none;
|
|
boolean dis = false;
|
|
int light = 0;
|
|
|
|
if (p) {
|
|
watcher * w = p->watchers;
|
|
while (w) {
|
|
if (f==w->faction) {
|
|
mode = w->mode;
|
|
break;
|
|
}
|
|
w = w->next;
|
|
}
|
|
}
|
|
|
|
for (u = r->units; u; u = u->next) {
|
|
if (u->faction == f) {
|
|
if (u->building && u->building->type==bt_lighthouse) {
|
|
int r = lighthouse_range(u->building, f);
|
|
if (r>light) light = r;
|
|
}
|
|
if (u->race != new_race[RC_SPELL] || u->number == RS_FARVISION) {
|
|
mode = see_unit;
|
|
if (fval(u, UFL_DISBELIEVES)) {
|
|
dis = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (light) {
|
|
/* we are in a lighthouse. add the others! */
|
|
region_list * rlist = get_regions_distance(r, light);
|
|
region_list * rp = rlist;
|
|
while (rp) {
|
|
region * rl = rp->data;
|
|
if (fval(rl->terrain, SEA_REGION)) {
|
|
direction_t d;
|
|
add_seen(seen, rl, see_lighthouse, false);
|
|
for (d=0;d!=MAXDIRECTIONS;++d) {
|
|
region * rn = rconnect(rl, d);
|
|
if (rn!=NULL) {
|
|
add_seen(seen, rn, see_neighbour, false);
|
|
}
|
|
}
|
|
}
|
|
rp = rp->next;
|
|
}
|
|
free_regionlist(rlist);
|
|
}
|
|
|
|
if (mode<see_travel && fval(r, RF_TRAVELUNIT)) {
|
|
for (ru = a_find(r->attribs, &at_travelunit); ru && ru->type==&at_travelunit; ru = ru->next) {
|
|
unit * u = (unit*)ru->data.v;
|
|
if (u->faction == f) {
|
|
mode = see_travel;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mode == see_none)
|
|
continue;
|
|
|
|
add_seen(seen, r, mode, dis);
|
|
/* nicht, wenn Verwirrung herrscht: */
|
|
if (!is_cursed(r->attribs, C_REGCONF, 0)) {
|
|
void (*view)(struct seen_region **, region * r, faction * f) = view_default;
|
|
if (p && fval(p, PFL_SEESPECIAL)) {
|
|
attrib * a = a_find(p->attribs, &at_viewrange);
|
|
if (a) view = (void (*)(struct seen_region **, region * r, faction * f))a->data.f;
|
|
}
|
|
view(seen, r, f);
|
|
}
|
|
}
|
|
return seen;
|
|
}
|
|
|
|
int
|
|
write_reports(faction * f, time_t ltime)
|
|
{
|
|
int backup = 1, maxbackup = 128;
|
|
boolean gotit = false;
|
|
struct report_context ctx;
|
|
|
|
ctx.f = f;
|
|
ctx.report_time = time(NULL);
|
|
ctx.seen = prepare_report(f);
|
|
ctx.first = firstregion(f);
|
|
ctx.last = lastregion(f);
|
|
ctx.addresses = NULL;
|
|
ctx.userdata = NULL;
|
|
get_seen_interval(&ctx);
|
|
get_addresses(&ctx);
|
|
|
|
do {
|
|
report_type * rtype = report_types;
|
|
|
|
errno = 0;
|
|
printf("Reports for %s:", factionname(f));
|
|
fflush(stdout);
|
|
|
|
for (;rtype!=NULL;rtype=rtype->next) {
|
|
if (f->options & rtype->flag) {
|
|
char filename[MAX_PATH];
|
|
sprintf(filename, "%s/%d-%s.%s", reportpath(), turn, factionid(f), rtype->extension);
|
|
if (rtype->write(filename, &ctx)==0) {
|
|
gotit = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (errno) {
|
|
char zText[64];
|
|
puts(" ERROR");
|
|
sprintf(zText, "Waiting %u seconds before retry", backup);
|
|
perror(zText);
|
|
sleep(backup);
|
|
if (backup<maxbackup) {
|
|
backup *= 2;
|
|
}
|
|
}
|
|
else {
|
|
puts(" DONE");
|
|
}
|
|
} while (errno);
|
|
if (!gotit) {
|
|
log_warning(("No report for faction %s!\n", factionid(f)));
|
|
}
|
|
freelist(ctx.addresses);
|
|
seen_done(ctx.seen);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
nmr_warnings(void)
|
|
{
|
|
faction *f,*fa;
|
|
#define FRIEND (HELP_GUARD|HELP_MONEY)
|
|
for (f=factions;f;f=f->next) {
|
|
if (f->no != MONSTER_FACTION && (turn-f->lastorders) >= 2) {
|
|
message * msg = NULL;
|
|
for (fa=factions;fa;fa=fa->next) {
|
|
if (alliedfaction(NULL, f, fa, FRIEND) && alliedfaction(NULL, fa, f, FRIEND)) {
|
|
if (msg==NULL) {
|
|
char buf[4096];
|
|
sprintf(buf, "Achtung: %s hat einige Zeit keine "
|
|
"Züge eingeschickt und könnte dadurch in Kürze aus dem "
|
|
"Spiel ausscheiden.", factionname(f));
|
|
msg = msg_message("msg_event", "string", buf);
|
|
}
|
|
add_message(&fa->msgs, msg);
|
|
}
|
|
}
|
|
if (msg!=NULL) msg_release(msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
report_donations(void)
|
|
{
|
|
region * r;
|
|
for (r=regions;r;r=r->next) {
|
|
while (r->donations) {
|
|
donation * sp = r->donations;
|
|
if (sp->amount > 0) {
|
|
struct message * msg = msg_message("donation",
|
|
"from to amount", sp->f1, sp->f2, sp->amount);
|
|
r_addmessage(r, sp->f1, msg);
|
|
r_addmessage(r, sp->f2, msg);
|
|
msg_release(msg);
|
|
}
|
|
r->donations = sp->next;
|
|
free(sp);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
write_script(FILE * F, const faction * f)
|
|
{
|
|
report_type * rtype;
|
|
char buf[1024];
|
|
|
|
fprintf(F, "faction=%s:email=%s", factionid(f), f->email);
|
|
if (f->options & (1<<O_BZIP2)) fputs(":compression=bz2", F);
|
|
else fputs(":compression=zip", F);
|
|
|
|
fputs(":reports=", F);
|
|
buf[0] = 0;
|
|
for (rtype=report_types;rtype!=NULL;rtype=rtype->next) {
|
|
if (f->options&rtype->flag) {
|
|
if (buf[0]) strcat(buf, ",");
|
|
strcat(buf, rtype->extension);
|
|
}
|
|
}
|
|
fputs(buf, F);
|
|
fputc('\n', F);
|
|
}
|
|
|
|
#undef GLOBAL_REPORT
|
|
#ifdef GLOBAL_REPORT
|
|
static void
|
|
global_report(const char * filename)
|
|
{
|
|
FILE * F = fopen(filename, "w");
|
|
region * r;
|
|
faction * f;
|
|
faction * monsters = findfaction(MONSTER_FACTION);
|
|
faction_list * addresses = NULL;
|
|
struct seen_region ** seen;
|
|
|
|
if (!monsters) return;
|
|
if (!F) return;
|
|
|
|
/* list of all addresses */
|
|
for (f=factions;f;f=f->next) {
|
|
faction_list * flist = calloc(1, sizeof(faction_list));
|
|
flist->data = f;
|
|
flist->next = addresses;
|
|
addresses = flist;
|
|
}
|
|
|
|
seen = seen_init();
|
|
for (r = regions; r; r = r->next) {
|
|
add_seen(seen, r, see_unit, true);
|
|
}
|
|
report_computer(F, monsters, seen, addresses, time(NULL));
|
|
freelist(addresses);
|
|
seen_done(seen);
|
|
fclose(F);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
writeaddresses(void)
|
|
{
|
|
faction *f;
|
|
FILE *F;
|
|
char zText[MAX_PATH];
|
|
sprintf(zText, "%s/addresses", basepath());
|
|
F = cfopen(zText, "w");
|
|
if (!F) return;
|
|
|
|
for (f = factions; f; f = f->next) {
|
|
if (f->no != MONSTER_FACTION && playerrace(f->race)) {
|
|
fprintf(F, "%s:%s:%s\n", factionname(f), f->email, f->banner);
|
|
}
|
|
}
|
|
fclose(F);
|
|
}
|
|
|
|
int
|
|
reports(void)
|
|
{
|
|
faction *f;
|
|
FILE *mailit;
|
|
time_t ltime = time(NULL);
|
|
const char * str;
|
|
int retval = 0;
|
|
char path[MAX_PATH];
|
|
|
|
nmr_warnings();
|
|
report_donations();
|
|
remove_empty_units();
|
|
|
|
sprintf(path, "%s/reports.txt", reportpath());
|
|
mailit = fopen(path, "w");
|
|
if (mailit == NULL) {
|
|
log_error(("%s konnte nicht geöffnet werden!\n", path));
|
|
}
|
|
|
|
for (f = factions; f; f = f->next) {
|
|
int error = write_reports(f, ltime);
|
|
if (error) retval = error;
|
|
if (mailit) write_script(mailit, f);
|
|
}
|
|
if (mailit) fclose(mailit);
|
|
free_seen();
|
|
str = get_param(global.parameters, "globalreport");
|
|
#ifdef GLOBAL_REPORT
|
|
if (str!=NULL) {
|
|
sprintf(path, "%s/%s.%u.cr", reportpath(), str, turn);
|
|
global_report(path);
|
|
}
|
|
#endif
|
|
return retval;
|
|
}
|
|
|
|
static variant
|
|
var_copy_string(variant x)
|
|
{
|
|
x.v = strdup((const char*)x.v);
|
|
return x;
|
|
}
|
|
|
|
static void
|
|
var_free_string(variant x)
|
|
{
|
|
free(x.v);
|
|
}
|
|
|
|
static variant
|
|
var_copy_order(variant x)
|
|
{
|
|
x.v = copy_order((order*)x.v);
|
|
return x;
|
|
}
|
|
|
|
static void
|
|
var_free_order(variant x)
|
|
{
|
|
free_order(x.v);
|
|
}
|
|
|
|
static variant
|
|
var_copy_items(variant x)
|
|
{
|
|
item * isrc;
|
|
resource * rdst = NULL, ** rptr = &rdst;
|
|
|
|
for (isrc = (item*)x.v; isrc!=NULL; isrc=isrc->next) {
|
|
resource * res = malloc(sizeof(resource));
|
|
res->number = isrc->number;
|
|
res->type = isrc->type->rtype;
|
|
*rptr = res;
|
|
rptr = &res->next;
|
|
}
|
|
*rptr = NULL;
|
|
x.v = rdst;
|
|
return x;
|
|
}
|
|
|
|
static void
|
|
var_free_resources(variant x)
|
|
{
|
|
resource ** rsrc = (resource**)&x.v;
|
|
while (*rsrc) {
|
|
resource * res = *rsrc;
|
|
*rsrc = res->next;
|
|
free(res);
|
|
}
|
|
}
|
|
|
|
static variant
|
|
var_copy_regions(variant x)
|
|
{
|
|
region_list * rsrc;
|
|
size_t size = 0;
|
|
|
|
for (rsrc = (region_list*)x.v; rsrc!=NULL; rsrc=rsrc->next) {
|
|
++size;
|
|
}
|
|
|
|
if (size>0) {
|
|
arg_regions * dst = (arg_regions *)malloc(sizeof(arg_regions) + sizeof(region*) * size);
|
|
dst->nregions = size;
|
|
dst->regions = (region**)(dst+1);
|
|
size = 0;
|
|
for (rsrc = (region_list*)x.v; rsrc!=NULL; rsrc=rsrc->next) {
|
|
dst->regions[size++] = rsrc->data;
|
|
}
|
|
x.v = dst;
|
|
} else {
|
|
x.v = NULL;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
static void
|
|
var_free_regions(variant x)
|
|
{
|
|
free(x.v);
|
|
}
|
|
|
|
void
|
|
reports_init(void)
|
|
{
|
|
/* register datatypes for the different message objects */
|
|
register_argtype("alliance", NULL, NULL, VAR_VOIDPTR);
|
|
register_argtype("building", NULL, NULL, VAR_VOIDPTR);
|
|
register_argtype("direction", NULL, NULL, VAR_INT);
|
|
register_argtype("faction", NULL, NULL, VAR_VOIDPTR);
|
|
register_argtype("race", NULL, NULL, VAR_VOIDPTR);
|
|
register_argtype("region", NULL, NULL, VAR_VOIDPTR);
|
|
register_argtype("resource", NULL, NULL, VAR_VOIDPTR);
|
|
register_argtype("ship", NULL, NULL, VAR_VOIDPTR);
|
|
register_argtype("skill", NULL, NULL, VAR_VOIDPTR);
|
|
register_argtype("spell", NULL, NULL, VAR_VOIDPTR);
|
|
register_argtype("curse", NULL, NULL, VAR_VOIDPTR);
|
|
register_argtype("unit", NULL, NULL, VAR_VOIDPTR);
|
|
register_argtype("int", NULL, NULL, VAR_INT);
|
|
register_argtype("string", var_free_string, var_copy_string, VAR_VOIDPTR);
|
|
register_argtype("order", var_free_order, var_copy_order, VAR_VOIDPTR);
|
|
register_argtype("resources", var_free_resources, NULL, VAR_VOIDPTR);
|
|
register_argtype("items", var_free_resources, var_copy_items, VAR_VOIDPTR);
|
|
register_argtype("regions", var_free_regions, var_copy_regions, VAR_VOIDPTR);
|
|
|
|
/* register alternative visibility functions */
|
|
register_function((pf_generic)view_neighbours, "view_neighbours");
|
|
register_function((pf_generic)view_regatta, "view_regatta");
|
|
}
|