#ifdef _MSC_VER #include #endif #include "reports.h" #include "battle.h" #include "guard.h" #include "laws.h" #include "spells.h" #include "travelthru.h" #include "lighthouse.h" #include "donations.h" /* attributes includes */ #include "attributes/attributes.h" #include "attributes/follow.h" #include "attributes/otherfaction.h" #include "attributes/racename.h" #include "attributes/stealth.h" #include "spells/unitcurse.h" /* kernel includes */ #include "kernel/config.h" #include "kernel/calendar.h" #include "kernel/ally.h" #include "kernel/alliance.h" #include "kernel/connection.h" #include "kernel/building.h" #include "kernel/curse.h" #include "kernel/faction.h" #include "kernel/group.h" #include "kernel/item.h" #include "kernel/messages.h" #include "kernel/order.h" #include "kernel/plane.h" #include "kernel/race.h" #include "kernel/region.h" #include "kernel/resources.h" #include "kernel/ship.h" #include "kernel/spell.h" #include "kernel/spellbook.h" #include "kernel/terrain.h" #include "kernel/unit.h" /* util includes */ #include "kernel/attrib.h" #include "util/base36.h" #include "util/functions.h" #include "util/goodies.h" #include "util/language.h" #include "util/lists.h" #include "util/log.h" #include "util/macros.h" #include "util/path.h" #include "util/password.h" #include "util/strings.h" #include "util/translation.h" #include #include /* libc includes */ #include #include #include #include #include #include #include #include "move.h" #if defined(_MSC_VER) && _MSC_VER >= 1900 # pragma warning(disable: 4774) /* TODO: remove this */ #endif #define SCALEWEIGHT 100 /* Faktor, um den die Anzeige von Gewichten skaliert wird */ bool nocr = false; bool nonr = false; bool noreports = false; const char *visibility[] = { "none", "neighbour", "lighthouse", "lighthouse", "travel", "far", "unit", "spell", "battle" }; const char *coasts[MAXDIRECTIONS] = { "coast::nw", "coast::ne", "coast::e", "coast::se", "coast::sw", "coast::w" }; const char *options[MAXOPTIONS] = { "AUSWERTUNG", "COMPUTER", "ZUGVORLAGE", NULL, "STATISTIK", "DEBUG", "ZIPPED", "ZEITUNG", /* Option hat Sonderbehandlung! */ NULL, "ADRESSEN", "BZIP2", "PUNKTE", "SHOWSKCHANGE" }; bool omniscient(const faction *f) { static const race *rc_template; static int cache; if (rc_changed(&cache)) { rc_template = get_race(RC_TEMPLATE); } return (f->race == rc_template); } 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, itoa36(f->no)); return buf; } const char *combatstatus[] = { "status_aggressive", "status_front", "status_rear", "status_defensive", "status_avoid", "status_flee" }; void report_status(const unit * u, const struct locale *lang, struct sbstring *sbp) { const char * status = LOC(lang, combatstatus[u->status]); if (!status) { const char *lname = locale_name(lang); struct locale *wloc = get_locale(lname); log_warning("no translation for combat status %s in %s", combatstatus[u->status], lname); locale_setstring(wloc, combatstatus[u->status], combatstatus[u->status] + 7); sbs_strcat(sbp, combatstatus[u->status] + 7); } else { sbs_strcat(sbp, status); } if (fval(u, UFL_NOAID)) { sbs_strcat(sbp, ", "); sbs_strcat(sbp, LOC(lang, "status_noaid")); } } size_t report_status_depr(const unit * u, const struct locale *lang, char *fsbuf, size_t buflen) { sbstring sbs; sbs_init(&sbs, fsbuf, buflen); report_status(u, lang, &sbs); return sbs_length(&sbs); } const char *hp_status(const unit * u) { double p; int max_hp = u->number * unit_max_hp(u); if (u->hp == max_hp) return NULL; p = (double)((double)u->hp / (double)(max_hp)); if (p < 0.50) return mkname("damage", "badly"); if (p < 0.75) return mkname("damage", "wounded"); if (p < 0.99) return mkname("damage", "exhausted"); if (p > 2.00) return mkname("damage", "plusstrong"); if (p > 1.50) return mkname("damage", "strong"); return NULL; } void report_item(const unit * owner, const item * i, const faction * viewer, const char **name, const char **basename, int *number, bool singular) { const resource_type *rsilver = get_resourcetype(R_SILVER); assert(!owner || owner->number); if (owner && owner->faction == viewer) { if (name) *name = LOC(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 (owner && i->type->rtype == rsilver) { int pp = i->number / owner->number; if (number) *number = 1; if (pp > 50000 && dragonrace(u_race(owner))) { if (name) *name = LOC(viewer->locale, "dragonhoard"); if (basename) *basename = "dragonhoard"; } else if (pp > 5000) { if (name) *name = LOC(viewer->locale, "moneychest"); if (basename) *basename = "moneychest"; } else if (pp > 500) { if (name) *name = LOC(viewer->locale, "moneybag"); if (basename) *basename = "moneybag"; } else { if (number) *number = 0; if (name) *name = NULL; if (basename) *basename = NULL; } } else { if (name) *name = LOC(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; } } } #define ORDERS_IN_NR 1 static void buforder(sbstring *sbp, const order * ord, const struct locale *lang, int mode) { sbs_strcat(sbp, ", \""); if (mode < ORDERS_IN_NR) { char cmd[ORDERSIZE]; get_command(ord, lang, cmd, sizeof(cmd)); sbs_strcat(sbp, cmd); } else { sbs_strcat(sbp, "..."); } sbs_strcat(sbp, "\""); } /** create a report of a list of items to a non-owner. * \param result: an array of size items. * \param size: maximum number of items to return * \param owner: the owner of the items, or NULL for faction::items etc. * \param viewer: the faction looking at the items */ int report_items(const unit *u, item * result, int size, const unit * owner, const faction * viewer) { const item *itm, *items = u->items; int n = 0; /* number of results */ assert(owner == NULL || viewer != owner->faction); assert(size); if (u->attribs) { curse * cu = get_curse(u->attribs, &ct_itemcloak); if (cu && curse_active(cu)) { return 0; } } for (itm = items; itm; itm = itm->next) { const char *ic; report_item(owner, itm, viewer, NULL, &ic, NULL, false); if (ic && *ic) { item *ishow; for (ishow = result; ishow != result + n; ++ishow) { const char *sc; if (ishow->type == itm->type) sc = ic; else report_item(owner, ishow, viewer, NULL, &sc, NULL, false); if (sc == ic || strcmp(sc, ic) == 0) { ishow->number += itm->number; break; } } if (ishow == result + n) { if (n == size) { log_error("too many items to report, increase buffer size.\n"); return -1; } result[n].number = itm->number; result[n].type = itm->type; result[n].next = (n + 1 == size) ? NULL : result + n + 1; ++n; } } } if (n > 0) result[n - 1].next = NULL; return n; } static void report_resource(resource_report * result, const resource_type *rtype, int number, int level) { assert(rtype); result->rtype = rtype; result->number = number; result->level = level; } static void bufattack(struct sbstring *sbp, const struct locale *lang, const char *name, const char *dmg) { sbs_strcat(sbp, LOC(lang, name)); if (dmg) { sbs_strcat(sbp, " ("); sbs_strcat(sbp, dmg); sbs_strcat(sbp, ")"); } } void report_raceinfo(const struct race *rc, const struct locale *lang, struct sbstring *sbp) { const char *info; int a, at_count; const char *name, *key; name = rc_name_s(rc, NAME_SINGULAR); sbs_strcat(sbp, LOC(lang, name)); sbs_strcat(sbp, ": "); key = mkname("raceinfo", rc->_name); info = locale_getstring(lang, key); if (info == NULL) { info = LOC(lang, mkname("raceinfo", "no_info")); } if (info) { sbs_strcat(sbp, info); } /* hp_p : Trefferpunkte */ sbs_strcat(sbp, " "); sbs_strcat(sbp, str_itoa(rc->hitpoints)); sbs_strcat(sbp, " "); sbs_strcat(sbp, LOC(lang, "stat_hitpoints")); /* b_attacke : Angriff */ sbs_strcat(sbp, ", "); sbs_strcat(sbp, LOC(lang, "stat_attack")); sbs_strcat(sbp, ": "); sbs_strcat(sbp, str_itoa(rc->at_default + rc->at_bonus)); /* b_defense : Verteidigung */ sbs_strcat(sbp, ", "); sbs_strcat(sbp, LOC(lang, "stat_defense")); sbs_strcat(sbp, ": "); sbs_strcat(sbp, str_itoa(rc->df_default + rc->df_bonus)); /* b_armor : Ruestung */ if (rc->armor > 0) { sbs_strcat(sbp, ", "); sbs_strcat(sbp, LOC(lang, "stat_armor")); sbs_strcat(sbp, ": "); sbs_strcat(sbp, str_itoa(rc->armor)); } sbs_strcat(sbp, "."); /* b_damage : Schaden */ at_count = 0; for (a = 0; a < RACE_ATTACKS; a++) { if (rc->attack[a].type != AT_NONE) { at_count++; } } if (rc->battle_flags & BF_EQUIPMENT) { sbs_strcat(sbp, " "); sbs_strcat(sbp, LOC(lang, "stat_equipment")); } if (rc->battle_flags & BF_RES_PIERCE) { sbs_strcat(sbp, " "); sbs_strcat(sbp, LOC(lang, "stat_pierce")); } if (rc->battle_flags & BF_RES_CUT) { sbs_strcat(sbp, " "); sbs_strcat(sbp, LOC(lang, "stat_cut")); } if (rc->battle_flags & BF_RES_BASH) { sbs_strcat(sbp, " "); sbs_strcat(sbp, LOC(lang, "stat_bash")); } sbs_strcat(sbp, " "); sbs_strcat(sbp, str_itoa(at_count)); sbs_strcat(sbp, " "); sbs_strcat(sbp, LOC(lang, (at_count == 1) ? "stat_attack" : "stat_attacks")); for (a = 0; a < RACE_ATTACKS; a++) { if (rc->attack[a].type != AT_NONE) { sbs_strcat(sbp, (a == 0) ? ": " : ", "); switch (rc->attack[a].type) { case AT_STANDARD: bufattack(sbp, lang, "attack_standard", rc->def_damage); break; case AT_NATURAL: bufattack(sbp, lang, "attack_natural", rc->attack[a].data.dice); break; case AT_SPELL: case AT_COMBATSPELL: case AT_DRAIN_ST: case AT_DRAIN_EXP: case AT_DAZZLE: bufattack(sbp, lang, "attack_magical", NULL); break; case AT_STRUCTURAL: bufattack(sbp, lang, "attack_structural", rc->attack[a].data.dice); break; } } } sbs_strcat(sbp, "."); } void report_building(const struct building *b, const char **name, const char **illusion) { if (name) { *name = buildingtype(b->type, b, b->size); } if (illusion) { *illusion = NULL; if (b->attribs && is_building_type(b->type, "illusioncastle")) { const attrib *a = a_find(b->attribs, &at_icastle); if (a != NULL) { *illusion = buildingtype(icastle_type(a), b, b->size); } } } } int report_resources(const region * r, resource_report * result, int size, const faction * viewer, bool see_unit) { int n = 0; if (r->land) { int peasants = rpeasants(r); int money = rmoney(r); int horses = rhorses(r); int trees = rtrees(r, 2); int saplings = rtrees(r, 1); bool mallorn = fval(r, RF_MALLORN) != 0; const resource_type *rtype; if (saplings) { if (n >= size) return -1; rtype = get_resourcetype(mallorn ? R_MALLORN_SAPLING : R_SAPLING); report_resource(result + n, rtype, saplings, -1); ++n; } if (trees) { if (n >= size) return -1; rtype = get_resourcetype(mallorn ? R_MALLORN_TREE : R_TREE); report_resource(result + n, rtype, trees, -1); ++n; } if (money) { if (n >= size) return -1; report_resource(result + n, get_resourcetype(R_SILVER), money, -1); ++n; } if (peasants) { if (n >= size) return -1; report_resource(result + n, get_resourcetype(R_PEASANT), peasants, -1); ++n; } if (horses) { if (n >= size) return -1; report_resource(result + n, get_resourcetype(R_HORSE), horses, -1); ++n; } } if (see_unit) { rawmaterial *res = r->resources; while (res) { const item_type *itype = resource2item(res->rtype); int minskill = itype->construction->minskill; skill_t skill = itype->construction->skill; int level = res->level + minskill - 1; int visible = -1; rawmaterial_type *raw = rmt_get(res->rtype); if (raw->visible == NULL) { visible = res->amount; level = res->level + minskill - 1; } else { const unit *u; int maxskill = 0; for (u = r->units; visible != res->amount && u != NULL; u = u->next) { if (u->faction == viewer) { int s = effskill(u, skill, NULL); if (s > maxskill) { maxskill = s; visible = raw->visible(res, maxskill); } } } } if (level >= 0 && visible >= 0) { if (n >= size) return -1; report_resource(result + n, res->rtype, visible, level); n++; } res = res->next; } } return n; } static void spskill(sbstring *sbp, const struct locale * lang, const struct unit * u, struct skill * sv, int *dh) { int effsk; if (!u->number) { return; } if (sv->level <= 0) { if (sv->old <= 0 || (u->faction->options & WANT_OPTION(O_SHOWSKCHANGE)) == 0) { return; } } sbs_strcat(sbp, ", "); if (!*dh) { sbs_strcat(sbp, LOC(lang, "nr_skills")); sbs_strcat(sbp, ": "); *dh = 1; } sbs_strcat(sbp, skillname(sv->id, lang)); sbs_strcat(sbp, " "); if (sv->id == SK_MAGIC) { magic_t mtype = unit_get_magic(u); if (mtype != M_GRAY) { sbs_strcat(sbp, magic_name(mtype, lang)); sbs_strcat(sbp, " "); } } if (sv->id == SK_STEALTH && fval(u, UFL_STEALTH)) { int i = u_geteffstealth(u); if (i >= 0) { sbs_strcat(sbp, str_itoa(i)); sbs_strcat(sbp, "/"); } } effsk = eff_skill(u, sv, NULL); sbs_strcat(sbp, str_itoa(effsk)); if (u->faction->options & WANT_OPTION(O_SHOWSKCHANGE)) { int oldeff = 0; int diff; if (sv->old > 0) { oldeff = sv->old + get_modifier(u, sv->id, sv->old, u->region, false); if (oldeff < 0) oldeff = 0; } diff = effsk - oldeff; if (diff != 0) { sbs_strcat(sbp, " ("); sbs_strcat(sbp, (diff > 0) ? "+" : ""); sbs_strcat(sbp, str_itoa(diff)); sbs_strcat(sbp, ")"); } } } void bufunit(const faction * f, const unit * u, const faction *fv, seen_mode mode, int getarnt, struct sbstring *sbp) { int i, dh; const char *pzTmp, *str; bool isbattle = (mode == seen_battle); item *itm, *show = NULL; item results[MAX_INVENTORY]; const struct locale *lang = f->locale; if (!fv) { fv = visible_faction(f, u); } assert(f); sbs_strcat(sbp, unitname(u)); if (!isbattle) { if (u->faction == f) { if (fval(u, UFL_GROUP)) { group *g = get_group(u); if (g) { sbs_strcat(sbp, ", "); sbs_strcat(sbp, groupid(g, f)); } } if (getarnt) { sbs_strcat(sbp, ", "); sbs_strcat(sbp, LOC(lang, "anonymous")); } else if (u->attribs) { faction *otherf = get_otherfaction(u); if (otherf) { sbs_strcat(sbp, ", "); sbs_strcat(sbp, factionname(otherf)); } } } else { if (getarnt) { sbs_strcat(sbp, ", "); sbs_strcat(sbp, LOC(lang, "anonymous")); } else { if (u->attribs && alliedunit(u, f, HELP_FSTEALTH)) { faction *otherf = get_otherfaction(u); if (otherf) { sbs_strcat(sbp, ", "); sbs_strcat(sbp, factionname(otherf)); sbs_strcat(sbp, " ("); sbs_strcat(sbp, factionname(u->faction)); sbs_strcat(sbp, ")"); } else { sbs_strcat(sbp, ", "); sbs_strcat(sbp, factionname(fv)); } } else { sbs_strcat(sbp, ", "); sbs_strcat(sbp, factionname(fv)); } } } } sbs_strcat(sbp, ", "); sbs_strcat(sbp, str_itoa(u->number)); sbs_strcat(sbp, " "); pzTmp = get_racename(u->attribs); if (pzTmp) { const char *name = locale_string(lang, mkname("race", pzTmp), false); sbs_strcat(sbp, name ? name : pzTmp); if (u->faction == f && fval(u_race(u), RCF_SHAPESHIFTANY)) { sbs_strcat(sbp, " ("); sbs_strcat(sbp, racename(lang, u, u_race(u))); sbs_strcat(sbp, ")"); } } else { const race *irace = u_irace(u); const race *urace = u_race(u); sbs_strcat(sbp, racename(lang, u, irace)); if (u->faction == f && irace != urace) { sbs_strcat(sbp, " ("); sbs_strcat(sbp, racename(lang, u, urace)); sbs_strcat(sbp, ")"); } } if (fval(u, UFL_HERO) && (u->faction == f || omniscient(f))) { sbs_strcat(sbp, ", "); sbs_strcat(sbp, LOC(lang, "hero")); } /* status */ if (u->number && (u->faction == f || isbattle)) { const char *c = hp_status(u); c = c ? LOC(lang, c) : 0; sbs_strcat(sbp, ", "); report_status(u, lang, sbp); if (c || fval(u, UFL_HUNGER)) { sbs_strcat(sbp, " ("); if (c) { sbs_strcat(sbp, c); } if (fval(u, UFL_HUNGER)) { if (c) { sbs_strcat(sbp, ", "); } sbs_strcat(sbp, LOC(lang, "unit_hungers")); } sbs_strcat(sbp, ")"); } } if (is_guard(u)) { sbs_strcat(sbp, ", "); sbs_strcat(sbp, LOC(lang, "unit_guards")); } dh = 0; if (u->faction == f) { skill *sv; for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) { spskill(sbp, lang, u, sv, &dh); } } dh = 0; if (f == u->faction || omniscient(f)) { show = u->items; } else if (mode >= seen_unit) { int n = report_items(u, results, MAX_INVENTORY, u, f); assert(n >= 0); if (n > 0) { show = results; } } 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; } sbs_strcat(sbp, ", "); if (!dh) { sbs_strcat(sbp, LOC(lang, "nr_inventory")); sbs_strcat(sbp, ": "); dh = 1; } if (in == 1) { sbs_strcat(sbp, ic); } else { sbs_strcat(sbp, str_itoa(in)); sbs_strcat(sbp, " "); sbs_strcat(sbp, ic); } } if (u->faction == f) { spellbook *book = unit_get_spellbook(u); if (book) { selist *ql = book->spells; int qi, header, maxlevel = effskill(u, SK_MAGIC, NULL); sbs_printf(sbp, ". Aura %d/%d", get_spellpoints(u), max_spellpoints(u, NULL)); for (header = 0, qi = 0; ql; selist_advance(&ql, &qi, 1)) { spellbook_entry * sbe = (spellbook_entry *)selist_get(ql, qi); const spell *sp = spellref_get(&sbe->spref); if (sbe->level <= maxlevel) { if (!header) { sbs_printf(sbp, ", %s: ", LOC(lang, "nr_spells")); header = 1; } else { sbs_strcat(sbp, ", "); } /* TODO: no need to deref the spellref here (spref->name is good) */ sbs_strcat(sbp, spell_name(sp, lang)); } } for (i = 0; i != MAXCOMBATSPELLS; ++i) { if (get_combatspell(u, i)) break; } if (i != MAXCOMBATSPELLS) { sbs_printf(sbp, ", %s: ", LOC(lang, "nr_combatspells")); dh = 0; for (i = 0; i < MAXCOMBATSPELLS; i++) { const spell *sp; if (!dh) { dh = 1; } else { sbs_strcat(sbp, ", "); } sp = get_combatspell(u, i); if (sp) { int sl = get_combatspelllevel(u, i); sbs_strcat(sbp, spell_name(sp, lang)); if (sl > 0) { sbs_printf(sbp, "(%d)", sl); } } else { sbs_strcat(sbp, LOC(lang, "nr_nospells")); } } } } if (!isbattle) { int printed = 0; order *ord; for (ord = u->old_orders; ord; ord = ord->next) { keyword_t kwd = getkeyword(ord); if (is_repeated(kwd)) { if (printed >= ORDERS_IN_NR) { break; } buforder(sbp, ord, u->faction->locale, printed++); } } if (printed < ORDERS_IN_NR) { for (ord = u->orders; ord; ord = ord->next) { keyword_t kwd = getkeyword(ord); if (is_repeated(kwd)) { if (printed >= ORDERS_IN_NR) { break; } buforder(sbp, ord, lang, printed++); } } } } } i = 0; str = u_description(u, lang); if (str) { sbs_strcat(sbp, "; "); sbs_strcat(sbp, str); i = str[strlen(str) - 1]; } if (i != '!' && i != '?' && i != '.') { sbs_strcat(sbp, "."); } pzTmp = uprivate(u); if (u->faction == f && pzTmp) { sbs_strcat(sbp, " (Bem: "); sbs_strcat(sbp, pzTmp); sbs_strcat(sbp, ")"); } } int bufunit_depr(const faction * f, const unit * u, seen_mode mode, char *buf, size_t size) { int getarnt = fval(u, UFL_ANON_FACTION); const faction * fv = visible_faction(f, u); sbstring sbs; sbs_init(&sbs, buf, size); bufunit(f, u, fv, mode, getarnt, &sbs); if (!getarnt) { if (alliedfaction(f, fv, HELP_ALL)) { return 1; } } return 0; } void split_paragraph(strlist ** SP, const char *s, unsigned int indent, unsigned int width, char mark) { bool firstline; static char buf[REPORTWIDTH + 1]; /* FIXME: static buffer, artificial limit */ size_t len = strlen(s); assert(width <= REPORTWIDTH); width -= indent; firstline = (mark != 0 && indent > 2); *SP = 0; while (len > 0) { unsigned int j; const char *cut = 0, *space = strchr(s, ' '); while (space && *space && (space - s) <= (ptrdiff_t)width) { cut = space; space = strchr(space + 1, ' '); if (!space && len < width) { cut = space = s + len; } } for (j = 0; j != indent; j++) buf[j] = ' '; if (firstline) { buf[indent - 2] = mark; firstline = false; } if (!cut) { cut = s + ((len < REPORTWIDTH) ? len : REPORTWIDTH); } memcpy(buf + indent, s, cut - s); buf[indent + (cut - s)] = 0; addstrlist(SP, buf); /* TODO: too much string copying, cut out this function */ while (*cut == ' ') { ++cut; } len -= (cut - s); s = cut; } } void sparagraph(strlist ** SP, const char *s, unsigned int indent, char mark) { split_paragraph(SP, s, indent, REPORTWIDTH, mark); } void lparagraph(struct strlist **SP, char *s, unsigned 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 (!buflocal) abort(); 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, unsigned int indent, seen_mode mode) { char buf[DISPLAYSIZE]; int dh = bufunit_depr(f, u, mode, buf, sizeof(buf)); lparagraph(SP, buf, indent, (char)((u->faction == f) ? '*' : (dh ? '+' : '-'))); } struct message *msg_curse(const struct curse *c, const void *obj, objtype_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 { message *msg = cinfo_simple(obj, typ, c, self); if (msg == NULL) { const char *unknown[] = { "unit_unknown", "region_unknown", "building_unknown", "ship_unknown" }; msg = msg_message(mkname("curseinfo", unknown[typ]), "id", c->no); log_warning("no curseinfo function for %s and no fallback either.\n", c->type->cname); } else { log_debug("no curseinfo function for %s, using cinfo_simple fallback.\n", c->type->cname); } return msg; } } int stealth_modifier(const region *r, const faction *f, seen_mode mode) { switch (mode) { case seen_spell: return get_observer(r, f); case seen_unit: return 0; case seen_lighthouse: return -2; case seen_travel: return -1; default: return INT_MIN; } } static void transfer_seen(selist ** dst, selist ** src) { assert(!*dst); *dst = *src; *src = NULL; } int cmp_faction(const void *lhs, const void *rhs) { const faction *lhf = (const faction *)lhs; const faction *rhf = (const faction *)rhs; if (lhf->no == rhf->no) return 0; if (lhf->no > rhf->no) return 1; return -1; } static void add_seen_faction_i(struct selist **flist, faction *f) { selist_set_insert(flist, f, cmp_faction); } void add_seen_faction(faction *self, faction *seen) { add_seen_faction_i(&self->seen_factions, seen); } typedef struct address_data { faction *f, *lastf; selist **flist; int stealthmod; } address_data; static void cb_add_address(region *r, unit *ut, void *cbdata) { address_data *data = (address_data *)cbdata; faction *f = data->f; if (ut->faction == f) { unit *u; for (u = r->units; u; u = u->next) { faction *sf = visible_faction(f, u); assert(u->faction != f); /* if this is see_travel only, then I shouldn't be here. */ if (data->lastf != sf && cansee_unit(ut, u, data->stealthmod)) { add_seen_faction_i(data->flist, sf); data->lastf = sf; } } } } static void add_travelthru_addresses(region *r, faction *f, selist **flist, int stealthmod) { /* for each traveling unit: add the faction of any unit is can see */ address_data cbdata = { 0 }; cbdata.f = f; cbdata.flist = flist; cbdata.stealthmod = stealthmod; travelthru_map(r, cb_add_address, &cbdata); } void get_addresses(report_context * ctx) { /* "TODO: travelthru" */ region *r; const faction *lastf = NULL; selist *flist = 0; transfer_seen(&flist, &ctx->f->seen_factions); ctx->f->seen_factions = NULL; /* do not delete it twice */ selist_push(&flist, ctx->f); if (f_get_alliance(ctx->f)) { selist *ql = ctx->f->alliance->members; int qi; for (qi = 0; ql; selist_advance(&ql, &qi, 1)) { add_seen_faction_i(&flist, (faction *)selist_get(ql, qi)); } } /* find the first region that this faction can see */ for (r = ctx->first; r != ctx->last; r = r->next) { if (r->seen.mode > seen_none) break; } for (; r != NULL; r = r->next) { if (r->seen.mode >= seen_lighthouse) { int stealthmod = stealth_modifier(r, ctx->f, r->seen.mode); if (r->seen.mode == seen_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_seen_faction_i(&flist, sf); lastf = sf; } } } } else if (r->seen.mode == seen_travel) { /* when we travel through a region, then we must add * the factions of any units we saw */ add_travelthru_addresses(r, ctx->f, &flist, stealthmod); } else if (r->seen.mode > seen_travel) { const unit *u = r->units; while (u != NULL) { if (u->faction != ctx->f) { faction *sf = visible_faction(ctx->f, u); bool ballied = sf && sf != ctx->f && sf != lastf && !fval(u, UFL_ANON_FACTION) && cansee(ctx->f, r, u, stealthmod); if (ballied || is_allied(ctx->f, sf)) { add_seen_faction_i(&flist, sf); lastf = sf; } } u = u->next; } } } } if (f_get_alliance(ctx->f)) { faction *f2; for (f2 = factions; f2; f2 = f2->next) { if (f2->alliance == ctx->f->alliance) { add_seen_faction_i(&flist, f2); } } } ctx->addresses = flist; } 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 = (report_type *)malloc(sizeof(report_type)); if (!type) abort(); type->extension = extension; type->write = write; type->flag = flag; type->next = report_types; report_types = type; } void reports_done(void) { report_type **rtp = &report_types; while (*rtp) { report_type *rt = *rtp; *rtp = rt->next; free(rt); } } int get_regions_distance_arr(region *rc, int radius, region *result[], int size) { int n = 0, i; if (size > n) { result[n++] = rc; fset(rc, RF_MARK); } for (i = 0; i != n; ++i) { region *r; int dist; r = result[i]; dist = distance(rc, r); if (dist < radius) { region *adj[MAXDIRECTIONS]; int d; get_neighbours(r, adj); for (d = 0; d != MAXDIRECTIONS; ++d) { r = adj[d]; if (r) { if (size > n) { if (!fval(r, RF_MARK) && dist < distance(rc, r)) { result[n++] = r; fset(r, RF_MARK); } } else { return -1; } } } } } for (i = 0; i != n; ++i) { freset(result[i], RF_MARK); } return n; } selist *get_regions_distance(region * root, int radius) { selist *ql, *rlist = NULL; int qi = 0; selist_push(&rlist, root); fset(root, RF_MARK); ql = rlist; while (ql) { region *r = (region *)selist_get(ql, qi); region * next[MAXDIRECTIONS]; int d; get_neighbours(r, next); for (d = 0; d != MAXDIRECTIONS; ++d) { if (next[d] && !fval(next[d], RF_MARK) && distance(next[d], root) <= radius) { selist_push(&rlist, next[d]); fset(next[d], RF_MARK); } } selist_advance(&ql, &qi, 1); } for (ql = rlist, qi = 0; ql; selist_advance(&ql, &qi, 1)) { region *r = (region *)selist_get(ql, qi); freset(r, RF_MARK); } return rlist; } static void add_seen(region *r, seen_mode mode) { if (r->seen.mode < mode) { r->seen.mode = mode; } } static void add_seen_nb(faction *f, region *r, seen_mode mode) { region *first = r, *last = r; add_seen(r, mode); if (mode > seen_neighbour) { region *next[MAXDIRECTIONS]; int d; get_neighbours(r, next); for (d = 0; d != MAXDIRECTIONS; ++d) { region *rn = next[d]; if (rn && rn->seen.mode < seen_neighbour) { rn->seen.mode = seen_neighbour; if (first->index > rn->index) first = rn; if (last->index < rn->index) last = rn; } } } update_interval(f, first); update_interval(f, last); } static void add_seen_lighthouse(region *r, faction *f) { if (r->terrain->flags & SEA_REGION) { add_seen_nb(f, r, seen_lighthouse); } else { add_seen_nb(f, r, seen_lighthouse_land); } } /** mark all regions seen by the lighthouse. */ static void prepare_lighthouse_ql(faction *f, selist *rlist) { selist *ql; int qi; for (ql = rlist, qi = 0; ql; selist_advance(&ql, &qi, 1)) { region *rl = (region *)selist_get(ql, qi); add_seen_lighthouse(rl, f); } } static void prepare_lighthouse(faction *f, region *r, int range) { if (range > 3) { selist *rlist = get_regions_distance(r, range); prepare_lighthouse_ql(f, rlist); selist_free(rlist); } else { region *result[64]; int n, i; n = get_regions_distance_arr(r, range, result, 64); assert(n > 0 && n <= 64); for (i = 0; i != n; ++i) { region *rl = result[i]; add_seen_lighthouse(rl, f); } } } void reorder_units(region * r) { unit **unext = &r->units; if (r->buildings) { building *b = r->buildings; while (*unext && b) { unit **ufirst = unext; /* where the first unit in the building should go */ unit **umove = unext; /* a unit we consider moving */ unit *owner = building_owner(b); while (owner && *umove) { unit *u = *umove; if (u->building == b) { unit **uinsert = unext; if (u == owner) { uinsert = ufirst; } if (umove != uinsert) { *umove = u->next; u->next = *uinsert; *uinsert = u; } else { /* no need to move, skip ahead */ umove = &u->next; } if (unext == uinsert) { /* we have a new well-placed unit. jump over it */ unext = &u->next; } } else { umove = &u->next; } } b = b->next; } } if (r->ships) { ship *sh = r->ships; /* first, move all units up that are not on ships */ unit **umove = unext; /* a unit we consider moving */ while (*umove) { unit *u = *umove; if (u->number && !u->ship) { if (umove != unext) { *umove = u->next; u->next = *unext; *unext = u; } else { /* no need to move, skip ahead */ umove = &u->next; } /* we have a new well-placed unit. jump over it */ unext = &u->next; } else { umove = &u->next; } } while (*unext && sh) { unit **ufirst = unext; /* where the first unit in the building should go */ unit *owner = ship_owner(sh); umove = unext; while (owner && *umove) { unit *u = *umove; if (u->number && u->ship == sh) { unit **uinsert = unext; if (u == owner) { uinsert = ufirst; owner = u; } if (umove != uinsert) { *umove = u->next; u->next = *uinsert; *uinsert = u; } else { /* no need to move, skip ahead */ umove = &u->next; } if (unext == uinsert) { /* we have a new well-placed unit. jump over it */ unext = &u->next; } } else { umove = &u->next; } } sh = sh->next; } } } static region *lastregion(faction * f) { if (!f->units) { return NULL; } return f->last ? f->last->next : NULL; } static region *firstregion(faction * f) { region *r = f->first; if (f->units == NULL) return NULL; if (r != NULL) return r; return f->first = regions; } static void cb_add_seen(region *r, unit *u, void *cbdata) { faction *f = (faction *)cbdata; if (u->faction == f) { add_seen_nb(f, r, seen_travel); } } void report_warnings(faction *f, int now) { if (f->age < NewbieImmunity()) { ADDMSG(&f->msgs, msg_message("newbieimmunity", "turns", NewbieImmunity() - f->age)); } if (f->race == get_race(RC_INSECT)) { season_t season = calendar_season(now + 1); if (season == SEASON_WINTER) { ADDMSG(&f->msgs, msg_message("nr_insectwinter", "")); } else if (season == SEASON_AUTUMN) { /* warning: next turn is the last week of autumn */ if (calendar_season(now + 2) == SEASON_WINTER) { ADDMSG(&f->msgs, msg_message("nr_insectfall", "")); } } } } /** set region.seen based on visibility by one faction. * * this function may also update ctx->last and ctx->first for potential * lighthouses and travelthru reports */ void prepare_report(report_context *ctx, faction *f, const char *password) { region *r; static int config; static bool rule_region_owners; static bool rule_lighthouse_units; const struct building_type *bt_lighthouse = bt_find("lighthouse"); /* Insekten-Winter-Warnung */ report_warnings(f, turn); if (bt_lighthouse && config_changed(&config)) { rule_region_owners = config_token("rules.region_owner_pay_building", bt_lighthouse->_name); rule_lighthouse_units = config_get_int("rules.lighthouse.unit_capacity", 0) != 0; } ctx->password = password; ctx->f = f; ctx->report_time = time(NULL); ctx->addresses = NULL; ctx->userdata = NULL; /* [first,last) interval of regions with a unit in it: */ if (f->units) { ctx->first = firstregion(f); ctx->last = lastregion(f); for (r = ctx->first; r != ctx->last; r = r->next) { unit *u; building *b; int br = 0, c = 0, range = 0; if (fval(r, RF_OBSERVER)) { int skill = get_observer(r, f); if (skill >= 0) { add_seen_nb(f, r, seen_spell); } } if (fval(r, RF_LIGHTHOUSE)) { /* region owners get the report from lighthouses */ if (rule_region_owners && f == region_get_owner(r)) { for (b = rbuildings(r); b; b = b->next) { if (b && b->type == bt_lighthouse) { /* region owners get maximum range */ int lhr = lighthouse_view_distance(b, NULL); if (lhr > range) range = lhr; } } } } b = NULL; for (u = r->units; u; u = u->next) { /* if we have any unit in this region, then we get seen_unit access */ if (u->faction == f) { add_seen_nb(f, r, seen_unit); /* units inside the lighthouse get range based on their perception * or the size, if perception is not a skill */ if (!fval(r, RF_LIGHTHOUSE)) { /* it's enough to add the region once, and if there are * no lighthouses here, there is no need to look at more units */ break; } } if (range == 0 && u->building && u->building->type == bt_lighthouse) { if (u->building && b != u->building) { b = u->building; c = buildingcapacity(b); br = 0; } if (rule_lighthouse_units) { --c; } else { c -= u->number; } if (u->faction == f && c >= 0) { /* unit is one of ours, and inside the current lighthouse */ if (br == 0) { /* lazy-calculate the range */ br = lighthouse_view_distance(b, u); if (br > range) { range = br; } } } } } if (range > 0) { /* we are in at least one lighthouse. add the regions we can see from here! */ prepare_lighthouse(f, r, range); } if (fval(r, RF_TRAVELUNIT) && r->seen.mode < seen_travel) { travelthru_map(r, cb_add_seen, f); } } } /* [fast,last) interval of seen regions (with lighthouses and travel) * TODO: what about neighbours? when are they included? do we need * them outside of the CR? */ ctx->first = firstregion(f); ctx->last = lastregion(f); } void finish_reports(report_context *ctx) { region *r; selist_free(ctx->addresses); for (r = ctx->first; r != ctx->last; r = r->next) { r->seen.mode = seen_none; } } int write_reports(faction * f) { bool gotit = false; struct report_context ctx; const unsigned char utf8_bom[4] = { 0xef, 0xbb, 0xbf, 0 }; report_type *rtype; char buffer[PASSWORD_MAXSIZE], *password = NULL; if (noreports) { return false; } if (f->lastorders == 0 || f->age <= 1) { /* neue Parteien, oder solche die noch NIE einen Zug gemacht haben, * kriegen ein neues Passwort: */ password = faction_genpassword(f, buffer); } prepare_report(&ctx, f, password); get_addresses(&ctx); log_debug("Reports for %s", factionname(f)); for (rtype = report_types; rtype != NULL; rtype = rtype->next) { if (f->options & rtype->flag) { int error = 0; do { char filename[32]; char path[4096]; sprintf(filename, "%d-%s.%s", turn, itoa36(f->no), rtype->extension); path_join(reportpath(), filename, path, sizeof(path)); errno = 0; if (rtype->write(path, &ctx, (const char *)utf8_bom) == 0) { gotit = true; } if (errno) { error = errno; log_fatal("error %d during %s report for faction %s: %s", errno, rtype->extension, factionname(f), strerror(error)); errno = 0; } } while (error); } } if (!gotit) { log_warning("No report for faction %s!", itoa36(f->no)); } finish_reports(&ctx); return 0; } static void write_script(FILE * F, const faction * f) { report_type *rtype; char buf[1024]; if (check_email(faction_getemail(f)) != 0) { return; } fprintf(F, "faction=%s:email=%s:lang=%s", itoa36(f->no), faction_getemail(f), locale_name(f->locale)); 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]) { str_strlcat(buf, ",", sizeof(buf)); } str_strlcat(buf, rtype->extension, sizeof(buf)); } } fputs(buf, F); fputc('\n', F); } static void check_messages_exist(void) { ct_checknames(); } int init_reports(void) { region *r; check_messages_exist(); create_directories(); for (r = regions; r; r = r->next) { reorder_units(r); } return 0; } int reports(void) { faction *f; FILE *mailit; int retval = 0; char path[PATH_MAX]; const char * rpath = reportpath(); log_info("Writing reports for turn %d:", turn); report_donations(); remove_empty_units(); path_join(rpath, "reports.txt", path, sizeof(path)); mailit = fopen(path, "w"); if (mailit == NULL) { log_error("%s could not be opened!\n", path); } for (f = factions; f; f = f->next) { if (f->email && !fval(f, FFL_NPC)) { int error = write_reports(f); if (error) retval = error; if (mailit) write_script(mailit, f); } } if (mailit) fclose(mailit); return retval; } static variant var_copy_string(variant x) { x.v = x.v ? str_strdup((const char *)x.v) : 0; 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)); if (!res) abort(); res->number = isrc->number; res->type = isrc->type->rtype; *rptr = res; rptr = &res->next; } *rptr = NULL; x.v = rdst; return x; } static variant var_copy_resources(variant x) { resource *rsrc; resource *rdst = NULL, **rptr = &rdst; for (rsrc = (resource *)x.v; rsrc != NULL; rsrc = rsrc->next) { resource *res = malloc(sizeof(resource)); if (!res) abort(); res->number = rsrc->number; res->type = rsrc->type; *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->next; free(rsrc); rsrc = res; } x.v = 0; } static void var_free_regions(variant x) /*-V524 */ { free(x.v); } const char *trailinto(const region * r, const struct locale *lang) { if (r) { static char ref[32]; const char *s; const char *tname = terrain_name(r); size_t sz; sz = str_strlcpy(ref, tname, sizeof(ref)); sz += str_strlcat(ref + sz, "_trail", sizeof(ref) - sz); s = LOC(lang, ref); if (s && *s) { if (strstr(s, "%s")) return s; } } return "%s"; } size_t f_regionid(const region * r, const faction * f, char *buffer, size_t size) { size_t len; if (!r) { len = str_strlcpy(buffer, "(Chaos)", size); } else { plane *pl = rplane(r); const char *name = pl ? pl->name : 0; int nx = r->x, ny = r->y; int named = (name && name[0]); pnormalize(&nx, &ny, pl); adjust_coordinates(f, &nx, &ny, pl); len = str_strlcpy(buffer, rname(r, f ? f->locale : 0), size); snprintf(buffer + len, size - len, " (%d,%d%s%s)", nx, ny, named ? "," : "", (named) ? name : ""); buffer[size - 1] = 0; len = strlen(buffer); } return len; } static char *f_regionid_s(const region * r, const faction * f) { static char buf[NAMESIZE + 20]; /* FIXME: static return value */ f_regionid(r, f, buf, sizeof(buf)); return buf; } /*** BEGIN MESSAGE RENDERING ***/ static void eval_localize(struct opstack **stack, const void *userdata) { /* (string, locale) -> string */ const struct faction *f = (const struct faction *)userdata; const struct locale *lang = f ? f->locale : default_locale; const char *c = (const char *)opop_v(stack); c = LOC(lang, c); opush_v(stack, strcpy(balloc(strlen(c) + 1), c)); } static void eval_trailto(struct opstack **stack, const void *userdata) { /* (int, int) -> int */ const struct faction *f = (const struct faction *)userdata; const struct locale *lang = f ? f->locale : default_locale; const struct region *r = (const struct region *)opop(stack).v; const char *trail = trailinto(r, lang); const char *rn = f_regionid_s(r, f); variant var; char *x = var.v = balloc(strlen(trail) + strlen(rn)); sprintf(x, trail, rn); opush(stack, var); } static void eval_unit(struct opstack **stack, const void *userdata) { /* unit -> string */ const struct faction *f = (const struct faction *)userdata; const struct unit *u = (const struct unit *)opop(stack).v; const char *c = u ? unitname(u) : LOC(f->locale, "an_unknown_unit"); size_t len = strlen(c); variant var; var.v = strcpy(balloc(len + 1), c); opush(stack, var); } static void eval_unit_dative(struct opstack **stack, const void *userdata) { /* unit -> string */ const struct faction *f = (const struct faction *)userdata; const struct unit *u = (const struct unit *)opop(stack).v; const char *c = u ? unitname(u) : LOC(f->locale, "unknown_unit_dative"); size_t len = strlen(c); variant var; var.v = strcpy(balloc(len + 1), c); opush(stack, var); } static void eval_spell(struct opstack **stack, const void *userdata) { /* unit -> string */ const struct faction *f = (const struct faction *)userdata; const struct spell *sp = (const struct spell *)opop(stack).v; const char *c = sp ? spell_name(sp, f->locale) : LOC(f->locale, "an_unknown_spell"); variant var; assert(c || !"spell without description!"); var.v = strcpy(balloc(strlen(c) + 1), c); opush(stack, var); } static void eval_curse(struct opstack **stack, const void *userdata) { /* unit -> string */ const struct faction *f = (const struct faction *)userdata; const struct curse_type *sp = (const struct curse_type *)opop(stack).v; const char *c = sp ? curse_name(sp, f->locale) : LOC(f->locale, "an_unknown_curse"); variant var; assert(c || !"spell effect without description!"); var.v = strcpy(balloc(strlen(c) + 1), c); opush(stack, var); } static void eval_unitid(struct opstack **stack, const void *userdata) { /* unit -> int */ const struct faction *f = (const struct faction *)userdata; const struct unit *u = (const struct unit *)opop(stack).v; const char *c = u ? unit_getname(u) : LOC(f->locale, "an_unknown_unit"); size_t len = strlen(c); variant var; var.v = strcpy(balloc(len + 1), c); opush(stack, var); } static void eval_unitsize(struct opstack **stack, const void *userdata) { /* unit -> int */ const struct unit *u = (const struct unit *)opop(stack).v; variant var; UNUSED_ARG(userdata); var.i = u->number; opush(stack, var); } static void eval_faction(struct opstack **stack, const void *userdata) { /* faction -> string */ const struct faction *f = (const struct faction *)opop(stack).v; const char *c = factionname(f); size_t len = strlen(c); variant var; UNUSED_ARG(userdata); var.v = strcpy(balloc(len + 1), c); opush(stack, var); } static void eval_alliance(struct opstack **stack, const void *userdata) { /* faction -> string */ const struct alliance *al = (const struct alliance *)opop(stack).v; const char *c = alliancename(al); variant var; UNUSED_ARG(userdata); if (c != NULL) { size_t len = strlen(c); var.v = strcpy(balloc(len + 1), c); } else var.v = NULL; opush(stack, var); } static void eval_region(struct opstack **stack, const void *userdata) { /* region -> string */ char name[NAMESIZE + 32]; const struct faction *f = (const struct faction *)userdata; const struct region *r = (const struct region *)opop(stack).v; const char *c = write_regionname(r, f, name, sizeof(name)); size_t len = strlen(c); variant var; var.v = strcpy(balloc(len + 1), c); opush(stack, var); } static void eval_terrain(struct opstack **stack, const void *userdata) { /* region -> string */ const struct faction *f = (const struct faction *)userdata; const struct region *r = (const struct region *)opop(stack).v; const char *c = LOC(f->locale, terrain_name(r)); size_t len = strlen(c); variant var; var.v = strcpy(balloc(len + 1), c); opush(stack, var); } static void eval_ship(struct opstack **stack, const void *userdata) { /* ship -> string */ const struct faction *f = (const struct faction *)userdata; const struct ship *u = (const struct ship *)opop(stack).v; const char *c = u ? shipname(u) : LOC(f->locale, "an_unknown_ship"); size_t len = strlen(c); variant var; var.v = strcpy(balloc(len + 1), c); opush(stack, var); } static void eval_building(struct opstack **stack, const void *userdata) { /* building -> string */ const struct faction *f = (const struct faction *)userdata; const struct building *u = (const struct building *)opop(stack).v; const char *c = u ? buildingname(u) : LOC(f->locale, "an_unknown_building"); size_t len = strlen(c); variant var; var.v = strcpy(balloc(len + 1), c); opush(stack, var); } static void eval_weight(struct opstack **stack, const void *userdata) { /* region -> string */ char buffer[32]; const struct faction *f = (const struct faction *)userdata; const struct locale *lang = f->locale; int weight = opop_i(stack); variant var; if (weight % SCALEWEIGHT == 0) { if (weight == SCALEWEIGHT) { sprintf(buffer, "1 %s", LOC(lang, "weight_unit")); } else { sprintf(buffer, "%d %s", weight / SCALEWEIGHT, LOC(lang, "weight_unit_p")); } } else { if (weight == 1) { sprintf(buffer, "1 %s %d", LOC(lang, "weight_per"), SCALEWEIGHT); } else { sprintf(buffer, "%d %s %d", weight, LOC(lang, "weight_per_p"), SCALEWEIGHT); } } var.v = strcpy(balloc(strlen(buffer) + 1), buffer); opush(stack, var); } static void eval_resource(struct opstack **stack, const void *userdata) { const faction *report = (const faction *)userdata; const struct locale *lang = report ? report->locale : default_locale; int j = opop(stack).i; const struct resource_type *res = (const struct resource_type *)opop(stack).v; const char *name = resourcename(res, j != 1); const char *c = LOC(lang, name); variant var; if (c) { size_t len = strlen(c); var.v = strcpy(balloc(len + 1), c); } else { log_error("missing translation for %s in eval_resource", name); var.v = NULL; } opush(stack, var); } static void eval_race(struct opstack **stack, const void *userdata) { const faction *report = (const faction *)userdata; const struct locale *lang = report ? report->locale : default_locale; int j = opop(stack).i; const race *r = (const race *)opop(stack).v; const char *name = rc_name_s(r, (j == 1) ? NAME_SINGULAR : NAME_PLURAL); const char *c = LOC(lang, name); variant var; if (c) { size_t len = strlen(c); var.v = strcpy(balloc(len + 1), c); } else { log_error("missing translation for %s in eval_race", name); var.v = NULL; } opush(stack, var); } static void eval_order(struct opstack **stack, const void *userdata) { /* order -> string */ const faction *f = (const faction *)userdata; const struct order *ord = (const struct order *)opop(stack).v; char buf[4096]; size_t len; variant var; const struct locale *lang = f ? f->locale : default_locale; UNUSED_ARG(userdata); write_order(ord, lang, buf, sizeof(buf)); len = strlen(buf); var.v = strcpy(balloc(len + 1), buf); opush(stack, var); } void report_battle_start(battle * b) { bfaction *bf; char zText[32 * MAXSIDES]; sbstring sbs; for (bf = b->factions; bf; bf = bf->next) { message *m; faction *f = bf->faction; const char *lastf = NULL; bool first = false; side *s; sbs_init(&sbs, zText, sizeof(zText)); for (s = b->sides; s != b->sides + b->nsides; ++s) { fighter *df; for (df = s->fighters; df; df = df->next) { if (is_attacker(df)) { if (first) { sbs_strcat(&sbs, ", "); } if (lastf) { sbs_strcat(&sbs, lastf); first = true; } if (seematrix(f, s)) lastf = sidename(s); else lastf = LOC(f->locale, "unknown_faction_dative"); break; } } } if (lastf) { if (first) { sbs_strcat(&sbs, " "); sbs_strcat(&sbs, LOC(f->locale, "and")); sbs_strcat(&sbs, " "); } sbs_strcat(&sbs, lastf); } m = msg_message("start_battle", "factions", zText); battle_message_faction(b, f, m); msg_release(m); } } static void eval_resources(struct opstack **stack, const void *userdata) { /* order -> string */ const faction *f = (const faction *)userdata; const struct locale *lang = f ? f->locale : default_locale; const struct resource *res = (const struct resource *)opop(stack).v; char buf[1024]; /* but we only use about half of this */ variant var; sbstring sbs; sbs_init(&sbs, buf, sizeof(buf)); while (res != NULL) { const char *rname = resourcename(res->type, (res->number != 1) ? NMF_PLURAL : 0); sbs_strcat(&sbs, str_itoa(res->number)); sbs_strcat(&sbs, " "); sbs_strcat(&sbs, LOC(lang, rname)); res = res->next; if (res != NULL) { sbs_strcat(&sbs, ", "); } } var.v = strcpy(balloc(sbs_length(&sbs)), buf); opush(stack, var); } static void eval_regions(struct opstack **stack, const void *userdata) { /* order -> string */ const faction *report = (const faction *)userdata; int i = opop(stack).i; int handle_end, begin = opop(stack).i; const arg_regions *aregs = (const arg_regions *)opop(stack).v; char buf[256]; variant var; sbstring sbs; sbs_init(&sbs, buf, sizeof(buf)); if (aregs == NULL) { handle_end = begin; } else if (i >= 0) { handle_end = begin + i; } else { handle_end = aregs->nregions + i; } for (i = begin; i < handle_end; ++i) { const char *rname = (const char *)regionname(aregs->regions[i], report); sbs_strcat(&sbs, rname); if (i + 1 < handle_end) { sbs_strcat(&sbs, ", "); } } var.v = strcpy(balloc(sbs_length(&sbs)), buf); opush(stack, var); } const char *get_mailcmd(const struct locale *loc) { static char result[64]; /* FIXME: static return buffer */ snprintf(result, sizeof(result), "%s %d %s", game_mailcmd(), game_id(), LOC(loc, "mailcmd")); return result; } static void print_trail(const faction *f, const region *r, const struct locale *lang, struct sbstring *sbp) { char buf[64]; const char *trail = trailinto(r, lang); const char *rn = f_regionid_s(r, f); if (snprintf(buf, sizeof(buf), trail, rn) != 0) { sbs_strcat(sbp, buf); } } static void eval_trail(struct opstack **stack, const void *userdata) { /* order -> string */ const faction *report = (const faction *)userdata; const struct locale *lang = report ? report->locale : default_locale; const arg_regions *aregs = (const arg_regions *)opop(stack).v; char buf[512]; variant var; sbstring sbs; #ifdef _SECURECRT_ERRCODE_VALUES_DEFINED /* MSVC touches errno in snprintf */ int eold = errno; #endif sbs_init(&sbs, buf, sizeof(buf)); if (aregs != NULL) { int i, handle_end = 0, begin = 0; handle_end = aregs->nregions; for (i = begin; i < handle_end; ++i) { region *r = aregs->regions[i]; print_trail(report, r, lang, &sbs); if (i + 2 < handle_end) { sbs_strcat(&sbs, ", "); } else if (i + 1 < handle_end) { sbs_strcat(&sbs, LOC(lang, "list_and")); } } } var.v = strcpy(balloc(sbs_length(&sbs)), buf); opush(stack, var); #ifdef _SECURECRT_ERRCODE_VALUES_DEFINED if (errno == ERANGE) { errno = eold; } #endif } void report_race_skills(const race *rc, const struct locale *lang, sbstring *sbp) { int dh = 0, dh1 = 0, sk; for (sk = 0; sk < MAXSKILLS; ++sk) { if (skill_enabled(sk) && rc->bonus[sk] > -5) dh++; } for (sk = 0; sk < MAXSKILLS; sk++) { if (skill_enabled(sk) && rc->bonus[sk] > -5) { dh--; if (dh1 == 0) { dh1 = 1; } else { if (dh == 0) { sbs_strcat(sbp, LOC(lang, "list_and")); } else { sbs_strcat(sbp, ", "); } } sbs_strcat(sbp, skillname((skill_t)sk, lang)); } } } void report_race_skills_depr(const race *rc, char *zText, size_t length, const struct locale *lang) { sbstring sbs; sbs_init(&sbs, zText, length); report_race_skills(rc, lang, &sbs); } static void eval_direction(struct opstack **stack, const void *userdata) { const faction *report = (const faction *)userdata; const struct locale *lang = report ? report->locale : default_locale; int i = opop(stack).i; const char *c = LOC(lang, (i >= 0) ? directions[i] : "unknown_direction"); size_t len = strlen(c); variant var; var.v = strcpy(balloc(len + 1), c); opush(stack, var); } static void eval_skill(struct opstack **stack, const void *userdata) { const faction *report = (const faction *)userdata; const struct locale *lang = report ? report->locale : default_locale; skill_t sk = (skill_t)opop(stack).i; const char *c = skillname(sk, lang); size_t len = strlen(c); variant var; var.v = strcpy(balloc(len + 1), c); opush(stack, var); } static void eval_int36(struct opstack **stack, const void *userdata) { int i = opop(stack).i; const char *c = itoa36(i); size_t len = strlen(c); variant var; var.v = strcpy(balloc(len + 1), c); opush(stack, var); UNUSED_ARG(userdata); } /*** END MESSAGE RENDERING ***/ int stream_printf(struct stream * out, const char *format, ...) { va_list args; int result; char buffer[4096]; size_t bytes = sizeof(buffer); /* TODO: should be in storage/stream.c (doesn't exist yet) */ va_start(args, format); result = vsnprintf(buffer, bytes, format, args); if (result >= 0 && (size_t)result < bytes) { bytes = (size_t)result; /* TODO: else = buffer too small */ } out->api->write(out->handle, buffer, bytes); va_end(args); return result; } typedef struct count_data { int n; const struct faction *f; } count_data; static void count_cb(region *r, unit *u, void *cbdata) { count_data *data = (count_data *)cbdata; const struct faction *f = data->f; if (r != u->region && (!u->ship || ship_owner(u->ship) == u)) { if (cansee_durchgezogen(f, r, u, 0)) { ++data->n; } } } int count_travelthru(struct region *r, const struct faction *f) { count_data data = { 0 }; data.f = f; travelthru_map(r, count_cb, &data); return data.n; } bool visible_unit(const unit *u, const faction *f, int stealthmod, seen_mode mode) { if (u->faction == f) { return true; } else { if (stealthmod > INT_MIN && mode >= seen_lighthouse) { if (mode != seen_travel || u->building || u->ship || is_guard(u)) { return cansee(f, u->region, u, stealthmod); } } } return false; } bool see_region_details(const region *r) { return r->seen.mode >= seen_travel; } void register_reports(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, var_copy_resources, VAR_VOIDPTR); register_argtype("items", var_free_resources, var_copy_items, VAR_VOIDPTR); register_argtype("regions", var_free_regions, NULL, VAR_VOIDPTR); /* register functions that turn message contents to readable strings */ add_function("alliance", &eval_alliance); add_function("region", &eval_region); add_function("terrain", &eval_terrain); add_function("weight", &eval_weight); add_function("resource", &eval_resource); add_function("race", &eval_race); add_function("faction", &eval_faction); add_function("ship", &eval_ship); add_function("unit", &eval_unit); add_function("unit.dative", &eval_unit_dative); add_function("unit.id", &eval_unitid); add_function("unit.size", &eval_unitsize); add_function("building", &eval_building); add_function("skill", &eval_skill); add_function("order", &eval_order); add_function("direction", &eval_direction); add_function("int36", &eval_int36); add_function("trailto", &eval_trailto); add_function("localize", &eval_localize); add_function("spell", &eval_spell); add_function("curse", &eval_curse); add_function("resources", &eval_resources); add_function("regions", &eval_regions); add_function("trail", &eval_trail); }