diff --git a/res/core/de/strings.xml b/res/core/de/strings.xml
index d5b6dba34..ffd0f82dd 100644
--- a/res/core/de/strings.xml
+++ b/res/core/de/strings.xml
@@ -6947,14 +6947,9 @@
is helping
-
- hat die Region durchquert.
- passed through the region.
-
-
-
- haben die Region durchquert.
- passed through the region.
+
+ Die Region wurde durchquert von
+ The region was crossed by
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c2cd21396..a67ad35b4 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -108,6 +108,7 @@ set (ERESSEA_SRC
spy.c
study.c
summary.c
+ travelthru.c
monsters.c
wormhole.c
${SPELLS_SRC}
@@ -180,6 +181,7 @@ set(TESTS_SRC
vortex.test.c
tests.test.c
reports.test.c
+ travelthru.test.c
callback.test.c
direction.test.c
economy.test.c
diff --git a/src/creport.c b/src/creport.c
index 6f8be4f2c..55e2bccce 100644
--- a/src/creport.c
+++ b/src/creport.c
@@ -11,6 +11,7 @@ without prior permission by the authors of Eressea.
#include
#include "buildno.h"
#include "creport.h"
+#include "travelthru.h"
/* tweakable features */
#define RENDER_CRMESSAGES
@@ -1189,7 +1190,7 @@ static void
cr_output_resources(FILE * F, report_context * ctx, seen_region * sr)
{
char cbuf[BUFFERSIZE], *pos = cbuf;
- region *r = sr->r;
+ const region *r = sr->r;
faction *f = ctx->f;
resource_report result[MAX_RAWMATERIALS];
int n, size = report_resources(sr, result, MAX_RAWMATERIALS, f);
@@ -1235,6 +1236,49 @@ cr_region_header(FILE * F, int plid, int nx, int ny, int uid)
fprintf(F, "%d;id\n", uid);
}
+typedef struct travel_data {
+ const faction *f;
+ FILE *file;
+ int n;
+} travel_data;
+
+static void cb_cr_travelthru_ship(region *r, unit *u, void *cbdata) {
+ travel_data *data = (travel_data *)cbdata;
+ const faction *f = data->f;
+ FILE *F = data->file;
+
+ if (u->ship && travelthru_cansee(r, f, u)) {
+ if (data->n++ == 0) {
+ fprintf(F, "DURCHSCHIFFUNG\n");
+ }
+ fprintf(F, "\"%s\"\n", shipname(u->ship));
+ }
+}
+
+static void cb_cr_travelthru_unit(region *r, unit *u, void *cbdata) {
+ travel_data *data = (travel_data *)cbdata;
+ const faction *f = data->f;
+ FILE *F = data->file;
+
+ if (!u->ship && travelthru_cansee(r, f, u)) {
+ if (data->n++ == 0) {
+ fprintf(F, "DURCHREISE\n");
+ }
+ fprintf(F, "\"%s\"\n", unitname(u));
+ }
+}
+
+static void cr_output_travelthru(FILE *F, region *r, const faction *f) {
+ /* describe both passed and inhabited regions */
+ travel_data cbdata = { 0 };
+ cbdata.f = f;
+ cbdata.file = F;
+ cbdata.n = 0;
+ travelthru_map(r, cb_cr_travelthru_ship, &cbdata);
+ cbdata.n = 0;
+ travelthru_map(r, cb_cr_travelthru_unit, &cbdata);
+}
+
static void cr_output_region(FILE * F, report_context * ctx, seen_region * sr)
{
faction *f = ctx->f;
@@ -1409,38 +1453,7 @@ static void cr_output_region(FILE * F, report_context * ctx, seen_region * sr)
}
}
- /* describe both passed and inhabited regions */
- if (fval(r, RF_TRAVELUNIT)) {
- bool seeunits = false, seeships = false;
- const attrib *ru;
- /* show units pulled through region */
- for (ru = a_find(r->attribs, &at_travelunit);
- ru && ru->type == &at_travelunit; ru = ru->next) {
- unit *u = (unit *)ru->data.v;
- if (cansee_durchgezogen(f, r, u, 0) && r != u->region) {
- if (u->ship && ship_owner(u->ship) == u) {
- if (!seeships) {
- fprintf(F, "DURCHSCHIFFUNG\n");
- }
- seeships = true;
- fprintf(F, "\"%s\"\n", shipname(u->ship));
- }
- }
- }
- for (ru = a_find(r->attribs, &at_travelunit);
- ru && ru->type == &at_travelunit; ru = ru->next) {
- unit *u = (unit *)ru->data.v;
- if (cansee_durchgezogen(f, r, u, 0) && r != u->region) {
- if (!u->ship) {
- if (!seeunits) {
- fprintf(F, "DURCHREISE\n");
- }
- seeunits = true;
- fprintf(F, "\"%s\"\n", unitname(u));
- }
- }
- }
- }
+ cr_output_travelthru(F, r, f);
if (sr->mode == see_unit || sr->mode == see_travel) {
message_list *mlist = r_getmessages(r, f);
cr_output_messages(F, r->msgs, f);
diff --git a/src/kernel/region.c b/src/kernel/region.c
index ddaab10d1..999b8dc33 100644
--- a/src/kernel/region.c
+++ b/src/kernel/region.c
@@ -525,18 +525,6 @@ attrib_type at_woodcount = {
ATF_UNIQUE
};
-/*********************/
-/* at_travelunit */
-/*********************/
-attrib_type at_travelunit = {
- "travelunit",
- DEFAULT_INIT,
- DEFAULT_FINALIZE,
- DEFAULT_AGE,
- NO_WRITE,
- NO_READ
-};
-
void rsetroad(region * r, direction_t d, int val)
{
connection *b;
diff --git a/src/kernel/region.h b/src/kernel/region.h
index 5c2c3a17c..c29732b78 100644
--- a/src/kernel/region.h
+++ b/src/kernel/region.h
@@ -169,7 +169,6 @@ extern "C" {
extern struct attrib_type at_horseluck;
extern struct attrib_type at_woodcount;
extern struct attrib_type at_deathcount;
- extern struct attrib_type at_travelunit;
void initrhash(void);
void rhash(struct region *r);
diff --git a/src/move.c b/src/move.c
index 131d87e74..f775acb79 100644
--- a/src/move.c
+++ b/src/move.c
@@ -23,6 +23,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "laws.h"
#include "reports.h"
#include "alchemy.h"
+#include "travelthru.h"
#include "vortex.h"
#include "monster.h"
#include "lighthouse.h"
@@ -501,29 +502,6 @@ static ship *do_maelstrom(region * r, unit * u)
return u->ship;
}
-/** sets a marker in the region telling that the unit has travelled through it
- * this is used for two distinctly different purposes:
- * - to report that a unit has travelled through. the report function
- * makes sure to only report the ships of travellers, not the travellers
- * themselves
- * - to report the region to the traveller
- */
-void travelthru(const unit * u, region * r)
-{
- attrib *ru = a_add(&r->attribs, a_new(&at_travelunit));
-
- fset(r, RF_TRAVELUNIT);
-
- ru->data.v = (void *)u;
-
- /* the first and last region of the faction gets reset, because travelthrough
- * could be in regions that are located before the [first, last] interval,
- * and recalculation is needed */
-#ifdef SMART_INTERVALS
- update_interval(u->faction, r);
-#endif
-}
-
static direction_t
koor_reldirection(int ax, int ay, int bx, int by, const struct plane *pl)
{
@@ -589,12 +567,12 @@ static void leave_trail(ship * sh, region * from, region_list * route)
}
static void
-mark_travelthru(const unit * u, region * r, const region_list * route,
+mark_travelthru(unit * u, region * r, const region_list * route,
const region_list * route_end)
{
/* kein travelthru in der letzten region! */
while (route != route_end) {
- travelthru(u, r);
+ travelthru_add(r, u);
r = route->data;
route = route->next;
}
diff --git a/src/move.h b/src/move.h
index 7674c43d7..013885564 100644
--- a/src/move.h
+++ b/src/move.h
@@ -64,7 +64,6 @@ extern "C" {
int enoughsailors(const struct ship *sh, int sumskill);
bool canswim(struct unit *u);
bool canfly(struct unit *u);
- void travelthru(const struct unit *u, struct region *r);
struct ship *move_ship(struct ship *sh, struct region *from,
struct region *to, struct region_list *route);
int walkingcapacity(const struct unit *u);
diff --git a/src/report.c b/src/report.c
index 7b8680ed8..b63b45755 100644
--- a/src/report.c
+++ b/src/report.c
@@ -23,6 +23,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "reports.h"
#include "laws.h"
+#include "travelthru.h"
#include "monster.h"
/* modules includes */
@@ -140,6 +141,7 @@ void write_spaces(stream *out, size_t num) {
}
}
+
static void centre(stream *out, const char *s, bool breaking)
{
/* Bei Namen die genau 80 Zeichen lang sind, kann es hier Probleme
@@ -183,7 +185,7 @@ char marker)
str = x + 2;
hanging_indent -= 2;
}
- }
+ }
else {
mark = ▮
}
@@ -1344,85 +1346,6 @@ static void statistics(stream *out, const region * r, const faction * f)
i_free(i_remove(&items, items));
}
-static void durchreisende(stream *out, const region * r, const faction * f)
-{
- if (fval(r, RF_TRAVELUNIT)) {
- attrib *abegin = a_find(r->attribs, &at_travelunit), *a;
- int counter = 0, maxtravel = 0;
- char buf[8192];
- char *bufp = buf;
- int bytes;
- size_t size = sizeof(buf) - 1;
-
- /* How many are we listing? For grammar. */
- for (a = abegin; a && a->type == &at_travelunit; a = a->next) {
- unit *u = (unit *)a->data.v;
-
- if (r != u->region && (!u->ship || ship_owner(u->ship) == u)) {
- if (cansee_durchgezogen(f, r, u, 0)) {
- ++maxtravel;
- }
- }
- }
-
- if (maxtravel == 0) {
- return;
- }
-
- /* Auflisten. */
- newline(out);
-
- for (a = abegin; a && a->type == &at_travelunit; a = a->next) {
- unit *u = (unit *)a->data.v;
-
- if (r != u->region && (!u->ship || ship_owner(u->ship) == u)) {
- if (cansee_durchgezogen(f, r, u, 0)) {
- ++counter;
- if (u->ship != NULL) {
- bytes = (int)strlcpy(bufp, shipname(u->ship), size);
- }
- else {
- bytes = (int)strlcpy(bufp, unitname(u), size);
- }
- if (wrptr(&bufp, &size, bytes) != 0) {
- INFO_STATIC_BUFFER();
- break;
- }
-
- if (counter + 1 < maxtravel) {
- bytes = (int)strlcpy(bufp, ", ", size);
- if (wrptr(&bufp, &size, bytes) != 0) {
- INFO_STATIC_BUFFER();
- break;
- }
- }
- else if (counter + 1 == maxtravel) {
- bytes = (int)strlcpy(bufp, LOC(f->locale, "list_and"), size);
- if (wrptr(&bufp, &size, bytes) != 0) {
- INFO_STATIC_BUFFER();
- break;
- }
- }
- }
- }
- }
- if (size > 0) {
- CHECK_ERRNO();
- if (maxtravel == 1) {
- bytes = _snprintf(bufp, size, " %s", LOC(f->locale, "has_moved_one"));
- }
- else {
- bytes = _snprintf(bufp, size, " %s", LOC(f->locale, "has_moved_many"));
- }
- CHECK_ERRNO();
- if (wrptr(&bufp, &size, bytes) != 0)
- WARN_STATIC_BUFFER_EX("durchreisende");
- CHECK_ERRNO();
- }
- *bufp = 0;
- paragraph(out, buf, 0, 0, 0);
- }
-}
static int buildingmaintenance(const building * b, const resource_type * rtype)
{
@@ -2007,6 +1930,104 @@ static void nr_paragraph(stream *out, message * m, faction * f)
paragraph(out, buf, 0, 0, 0);
}
+typedef struct cb_data {
+ stream *out;
+ char *start, *writep;
+ size_t size;
+ const faction *f;
+ int maxtravel, counter;
+} cb_data;
+
+static void init_cb(cb_data *data, stream *out, char *buffer, size_t size, const faction *f) {
+ data->out = out;
+ data->writep = buffer;
+ data->start = buffer;
+ data->size = size;
+ data->f = f;
+ data->maxtravel = 0;
+ data->counter = 0;
+}
+
+static void cb_write_travelthru(region *r, unit *u, void *cbdata) {
+ cb_data *data = (cb_data *)cbdata;
+ const faction *f = data->f;
+
+ if (data->counter >= data->maxtravel) {
+ return;
+ }
+ if (travelthru_cansee(r, f, u)) {
+ ++data->counter;
+ do {
+ size_t len, size = data->size - (data->writep - data->start);
+ const char *str;
+ char *writep = data->writep;
+
+ if (u->ship != NULL) {
+ str = shipname(u->ship);
+ }
+ else {
+ str = unitname(u);
+ }
+ len = strlen(str);
+ if (len < size && data->counter <= data->maxtravel) {
+ memcpy(writep, str, len);
+ writep += len;
+ size -= len;
+ if (data->counter == data->maxtravel) {
+ str = ".";
+ }
+ else if (data->counter + 1 == data->maxtravel) {
+ str = LOC(f->locale, "list_and");
+ }
+ else {
+ str = ", ";
+ }
+ len = strlen(str);
+ if (len < size) {
+ memcpy(writep, str, len);
+ writep += len;
+ size -= len;
+ data->writep = writep;
+ }
+ }
+ if (len >= size || data->counter == data->maxtravel) {
+ // buffer is full
+ *writep = 0;
+ paragraph(data->out, data->start, 0, 0, 0);
+ data->writep = data->start;
+ if (data->counter == data->maxtravel) {
+ break;
+ }
+ }
+ } while (data->writep == data->start);
+ }
+}
+
+void write_travelthru(stream *out, region * r, const faction * f)
+{
+ int maxtravel;
+ char buf[8192];
+
+ assert(r);
+ assert(f);
+ if (!fval(r, RF_TRAVELUNIT)) {
+ return;
+ }
+
+ /* How many are we listing? For grammar. */
+ maxtravel = count_travelthru(r, f);
+ if (maxtravel > 0) {
+ cb_data cbdata;
+
+ init_cb(&cbdata, out, buf, sizeof(buf), f);
+ cbdata.maxtravel = maxtravel;
+ cbdata.writep +=
+ strlcpy(buf, LOC(f->locale, "travelthru_header"), sizeof(buf));
+ travelthru_map(r, cb_write_travelthru, &cbdata);
+ return;
+ }
+}
+
int
report_plaintext(const char *filename, report_context * ctx,
const char *charset)
@@ -2118,7 +2139,7 @@ const char *charset)
}
if (no_people != f->num_people) {
f->num_people = no_people;
- }
+}
#else
no_units = count_units(f);
no_people = count_all(f);
@@ -2325,21 +2346,26 @@ const char *charset)
}
}
guards(out, r, f);
- durchreisende(out, r, f);
+ newline(out);
+ write_travelthru(out, r, f);
}
else {
if (sr->mode == see_far) {
describe(out, sr, f);
+ newline(out);
guards(out, r, f);
- durchreisende(out, r, f);
+ newline(out);
+ write_travelthru(out, r, f);
}
else if (sr->mode == see_lighthouse) {
describe(out, sr, f);
- durchreisende(out, r, f);
+ newline(out);
+ write_travelthru(out, r, f);
}
else {
describe(out, sr, f);
- durchreisende(out, r, f);
+ newline(out);
+ write_travelthru(out, r, f);
}
}
/* Statistik */
diff --git a/src/report.h b/src/report.h
index 189a38f0f..657bef36b 100644
--- a/src/report.h
+++ b/src/report.h
@@ -1,3 +1,4 @@
+#pragma once
/*
+-------------------+ Christian Schlittchen
| | Enno Rehling
@@ -11,15 +12,20 @@
*/
#ifndef H_GC_REPORT
#define H_GC_REPORT
+
+#include
+
#ifdef __cplusplus
extern "C" {
#endif
struct stream;
+ struct region;
+ struct faction;
void register_nr(void);
void report_cleanup(void);
void write_spaces(struct stream *out, size_t num);
-
+ void write_travelthru(struct stream *out, const struct region * r, const struct faction * f);
#ifdef __cplusplus
}
#endif
diff --git a/src/reports.c b/src/reports.c
index c381ac831..9d4d7f21c 100644
--- a/src/reports.c
+++ b/src/reports.c
@@ -20,6 +20,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include "reports.h"
#include "laws.h"
+#include "travelthru.h"
#include "lighthouse.h"
/* kernel includes */
@@ -981,6 +982,38 @@ void add_seen_faction(faction *self, faction *seen) {
add_seen_faction_i(&self->seen_factions, seen);
}
+typedef struct address_data {
+ faction *f, *lastf;
+ quicklist **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(u, ut, data->stealthmod)) {
+ add_seen_faction_i(data->flist, sf);
+ data->lastf = sf;
+ break;
+ }
+ }
+ }
+}
+
+static void add_travelthru_addresses(region *r, faction *f, quicklist **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);
+}
static void get_addresses(report_context * ctx)
{
@@ -1025,26 +1058,9 @@ static void get_addresses(report_context * ctx)
}
}
else if (sr->mode == see_travel) {
- unit *u = r->units;
- while (u) {
- faction *sf = visible_faction(ctx->f, u);
- assert(u->faction != ctx->f); /* if this is see_travel only, then I shouldn't be here. */
- 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_seen_faction_i(&flist, sf);
- lastf = sf;
- break;
- }
- }
- a = a->next;
- }
- }
- u = u->next;
- }
+ /* 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 (sr->mode > see_travel) {
const unit *u = r->units;
@@ -1465,6 +1481,13 @@ void reorder_units(region * r)
}
}
+static void cb_add_seen(region *r, unit *u, void *cbdata) {
+ unused_arg(cbdata);
+ if (u->faction) {
+ add_seen(u->faction->seen, r, see_travel, false);
+ }
+}
+
static void prepare_reports(void)
{
region *r;
@@ -1478,7 +1501,6 @@ static void prepare_reports(void)
}
for (r = regions; r; r = r->next) {
- attrib *ru;
unit *u;
plane *p = rplane(r);
@@ -1532,19 +1554,18 @@ static void prepare_reports(void)
if (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;
-
- /* make sure the faction has not been removed this turn: */
- if (u->faction) {
- add_seen(u->faction->seen, r, see_travel, false);
- }
- }
+ travelthru_map(r, cb_add_seen, r);
}
}
}
+static void cb_set_last(region *r, unit *u, void *cbdata) {
+ faction *f = (faction *)cbdata;
+ if (u->faction == f) {
+ f->last = r;
+ }
+}
+
static region *lastregion(faction * f)
{
#ifdef SMART_INTERVALS
@@ -1571,15 +1592,7 @@ static region *lastregion(faction * f)
/* search the region for travelthru-attributes: */
if (fval(r, RF_TRAVELUNIT)) {
- attrib *ru = a_find(r->attribs, &at_travelunit);
- while (ru && ru->type == &at_travelunit) {
- u = (unit *)ru->data.v;
- if (u->faction == f) {
- f->last = r;
- break;
- }
- ru = ru->next;
- }
+ travelthru_map(r, cb_set_last, f);
}
if (f->last == r)
continue;
@@ -2378,6 +2391,28 @@ int stream_printf(struct stream * out, const char *format, ...) {
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;
+}
+
void register_reports(void)
{
/* register datatypes for the different message objects */
diff --git a/src/reports.h b/src/reports.h
index ed0687985..c72cebab1 100644
--- a/src/reports.h
+++ b/src/reports.h
@@ -157,6 +157,8 @@ extern "C" {
int stream_printf(struct stream * out, const char *format, ...);
+ int count_travelthru(struct region *r, const struct faction *f);
+
#define GR_PLURAL 0x01 /* grammar: plural */
#define MAX_INVENTORY 128 /* maimum number of different items in an inventory */
#define MAX_RAWMATERIALS 8 /* maximum kinds of raw materials in a regions */
diff --git a/src/reports.test.c b/src/reports.test.c
index 89bca6e05..b30c3214b 100644
--- a/src/reports.test.c
+++ b/src/reports.test.c
@@ -3,6 +3,8 @@
#include "reports.h"
#include "report.h"
#include "creport.h"
+#include "move.h"
+#include "travelthru.h"
#include
#include
@@ -11,6 +13,8 @@
#include
#include
+#include
+
#include
#include
#include
@@ -178,6 +182,53 @@ static void test_cr_unit(CuTest *tc) {
test_cleanup();
}
+static void test_write_travelthru(CuTest *tc) {
+ stream out = { 0 };
+ char buf[1024];
+ size_t len;
+ region *r;
+ faction *f;
+ unit *u;
+ struct locale *lang;
+
+ test_cleanup();
+ lang = get_or_create_locale("de");
+ locale_setstring(lang, "travelthru_header", "Durchreise: ");
+ mstream_init(&out);
+ r = test_create_region(0, 0, 0);
+ r->flags |= RF_TRAVELUNIT;
+ f = test_create_faction(0);
+ f->locale = lang;
+ u = test_create_unit(f, 0);
+ unit_setname(u, "Hodor");
+ unit_setid(u, 1);
+
+ write_travelthru(&out, r, f);
+ out.api->rewind(out.handle);
+ len = out.api->read(out.handle, buf, sizeof(buf));
+ CuAssertIntEquals_Msg(tc, "no travelers, no report", 0, (int)len);
+ mstream_done(&out);
+
+ mstream_init(&out);
+ travelthru_add(r, u);
+ write_travelthru(&out, r, f);
+ out.api->rewind(out.handle);
+ len = out.api->read(out.handle, buf, sizeof(buf));
+ buf[len] = '\0';
+ CuAssertStrEquals_Msg(tc, "list one unit", "Durchreise: Hodor (1).\n", buf);
+ mstream_done(&out);
+
+ mstream_init(&out);
+ move_unit(u, r, 0);
+ write_travelthru(&out, r, f);
+ out.api->rewind(out.handle);
+ len = out.api->read(out.handle, buf, sizeof(buf));
+ CuAssertIntEquals_Msg(tc, "do not list units that stopped in the region", 0, (int)len);
+
+ mstream_done(&out);
+ test_cleanup();
+}
+
CuSuite *get_reports_suite(void)
{
CuSuite *suite = CuSuiteNew();
@@ -188,5 +239,6 @@ CuSuite *get_reports_suite(void)
SUITE_ADD_TEST(suite, test_write_spaces);
SUITE_ADD_TEST(suite, test_write_many_spaces);
SUITE_ADD_TEST(suite, test_sparagraph);
+ SUITE_ADD_TEST(suite, test_write_travelthru);
return suite;
}
diff --git a/src/test_eressea.c b/src/test_eressea.c
index e434f6c7f..cef5193cd 100644
--- a/src/test_eressea.c
+++ b/src/test_eressea.c
@@ -79,6 +79,7 @@ int RunAllTests(void)
RUN_TESTS(suite, messages);
/* gamecode */
RUN_TESTS(suite, battle);
+ RUN_TESTS(suite, travelthru);
RUN_TESTS(suite, economy);
RUN_TESTS(suite, give);
RUN_TESTS(suite, laws);
diff --git a/src/tests.c b/src/tests.c
index 028fb888d..bcbd18c29 100644
--- a/src/tests.c
+++ b/src/tests.c
@@ -24,6 +24,7 @@
#include
#include
+#include
#include
#include
@@ -85,6 +86,11 @@ void test_cleanup(void)
mt_register(mt_new_va("missing_message", "name:string", 0));
mt_register(mt_new_va("missing_feedback", "unit:unit", "region:region", "command:order", "name:string", 0));
}
+ if (errno) {
+ int error = errno;
+ errno = 0;
+ log_error("errno: %d", error);
+ }
}
terrain_type *
diff --git a/src/travelthru.c b/src/travelthru.c
new file mode 100644
index 000000000..754a93530
--- /dev/null
+++ b/src/travelthru.c
@@ -0,0 +1,107 @@
+/*
+Copyright (c) 1998-2015, Enno Rehling
+Katja Zedel
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+**/
+
+#include
+#include
+
+#include "travelthru.h"
+#include "laws.h"
+#include "report.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+
+/*********************/
+/* at_travelunit */
+/*********************/
+attrib_type at_travelunit = {
+ "travelunit",
+ DEFAULT_INIT,
+ DEFAULT_FINALIZE,
+ DEFAULT_AGE,
+ NO_WRITE,
+ NO_READ
+};
+
+/** sets a marker in the region telling that the unit has travelled through it
+* this is used for two distinctly different purposes:
+* - to report that a unit has travelled through. the report function
+* makes sure to only report the ships of travellers, not the travellers
+* themselves
+* - to report the region to the traveller
+*/
+void travelthru_add(region * r, unit * u)
+{
+ attrib *a;
+ quicklist *ql;
+
+ assert(r);
+ assert(u);
+
+ a = a_find(r->attribs, &at_travelunit);
+ if (!a) {
+ a = a_add(&r->attribs, a_new(&at_travelunit));
+ }
+ ql = (quicklist *)a->data.v;
+
+ fset(r, RF_TRAVELUNIT);
+ ql_push(&ql, u);
+ a->data.v = ql;
+
+#ifdef SMART_INTERVALS
+ /* the first and last region of the faction gets reset, because travelthrough
+ * could be in regions that are located before the [first, last] interval,
+ * and recalculation is needed */
+ update_interval(u->faction, r);
+#endif
+}
+
+bool travelthru_cansee(const struct region *r, const struct faction *f, const struct unit *u) {
+ if (r != u->region && (!u->ship || ship_owner(u->ship) == u)) {
+ return cansee_durchgezogen(f, r, u, 0);
+ }
+ return false;
+}
+
+void travelthru_map(region * r, void(*cb)(region *, struct unit *, void *), void *cbdata)
+{
+ attrib *a;
+ assert(r);
+ a = a_find(r->attribs, &at_travelunit);
+ if (a) {
+ quicklist *ql;
+ ql_iter qi;
+ ql = (quicklist *)a->data.v;
+ for (qi = qli_init(&ql); qli_more(qi);) {
+ unit *u = (unit *)qli_next(&qi);
+ cb(r, u, cbdata);
+ }
+ }
+}
diff --git a/src/travelthru.h b/src/travelthru.h
new file mode 100644
index 000000000..4e43dd25a
--- /dev/null
+++ b/src/travelthru.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#ifndef H_TRAVELTHRU
+#define H_TRAVELTHRU
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ struct attrib;
+ struct stream;
+ struct region;
+ struct faction;
+ struct unit;
+ void travelthru_map(struct region * r, void(*cb)(struct region *r, struct unit *, void *), void *cbdata);
+ bool travelthru_cansee(const struct region *r, const struct faction *f, const struct unit *u);
+ void travelthru_add(struct region * r, struct unit * u);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/travelthru.test.c b/src/travelthru.test.c
new file mode 100644
index 000000000..f92188751
--- /dev/null
+++ b/src/travelthru.test.c
@@ -0,0 +1,80 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "travelthru.h"
+#include "reports.h"
+#include "tests.h"
+
+#include
+
+struct attrib;
+
+static void count_travelers(region *r, unit *u, void *cbdata) {
+ int *n = (int *)cbdata;
+ unused_arg(r);
+ *n += u->number;
+}
+
+typedef struct travel_fixture {
+ region *r;
+ faction *f;
+} travel_fixture;
+
+static void setup_travelthru(travel_fixture *fix, int nunits) {
+ region *r;
+ faction *f;
+
+ test_cleanup();
+ r = test_create_region(0, 0, 0);
+ while (r->attribs) {
+ a_remove(&r->attribs, r->attribs);
+ }
+ f = test_create_faction(0);
+ while (nunits--) {
+ unit *u = test_create_unit(f, 0);
+ travelthru_add(r, u);
+ }
+ fix->r = r;
+ fix->f = f;
+}
+
+static void test_travelthru_count(CuTest *tc) {
+ travel_fixture fix;
+ setup_travelthru(&fix, 0);
+ CuAssertIntEquals(tc, 0, count_travelthru(fix.r, fix.f));
+
+ setup_travelthru(&fix, 1);
+ CuAssertIntEquals(tc, 1, count_travelthru(fix.r, fix.f));
+
+ setup_travelthru(&fix, 2);
+ CuAssertIntEquals(tc, 2, count_travelthru(fix.r, fix.f));
+
+ test_cleanup();
+}
+
+static void test_travelthru_map(CuTest *tc) {
+ int n = 0;
+ travel_fixture fix;
+
+ setup_travelthru(&fix, 0);
+ travelthru_map(fix.r, count_travelers, &n);
+ CuAssertIntEquals(tc, 0, n);
+
+ setup_travelthru(&fix, 1);
+ travelthru_map(fix.r, count_travelers, &n);
+ CuAssertIntEquals(tc, 1, n);
+
+ test_cleanup();
+}
+
+CuSuite *get_travelthru_suite(void)
+{
+ CuSuite *suite = CuSuiteNew();
+ SUITE_ADD_TEST(suite, test_travelthru_count);
+ SUITE_ADD_TEST(suite, test_travelthru_map);
+ return suite;
+}
diff --git a/src/util/bsdstring.test.c b/src/util/bsdstring.test.c
index 6fcb86515..0fffd187c 100644
--- a/src/util/bsdstring.test.c
+++ b/src/util/bsdstring.test.c
@@ -1,5 +1,6 @@
#include
#include "bsdstring.h"
+#include
#include
static void test_strlcat(CuTest * tc)
@@ -38,6 +39,7 @@ static void test_strlcpy(CuTest * tc)
CuAssertIntEquals(tc, 8, (int)strlcpy(buffer, "herpderp", 8));
CuAssertStrEquals(tc, "herpder", buffer);
CuAssertIntEquals(tc, 0x7f, buffer[8]);
+ errno = 0;
}
static void test_slprintf(CuTest * tc)