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)