From 2276a2fcbdd54faae6a80e919d385eb2d6871eb5 Mon Sep 17 00:00:00 2001 From: Steffen Mecke Date: Wed, 12 Sep 2018 09:41:18 +0200 Subject: [PATCH 01/52] fix NOTLOST items lost in battle --- src/battle.c | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/battle.c b/src/battle.c index 8c1627d97..fe61c8adf 100644 --- a/src/battle.c +++ b/src/battle.c @@ -1123,26 +1123,23 @@ static bool survives(fighter *af, troop dt, battle *b) { } static void destroy_items(troop dt) { - unit *du = dt.fighter->unit; - - item **pitm; - - for (pitm = &du->items; *pitm;) { - item *itm = *pitm; - const item_type *itype = itm->type; - if (!(itype->flags & ITF_CURSED) && dt.index < itm->number) { - /* 25% Grundchance, das ein Item kaputtgeht. */ - if (rng_int() % 4 < 1) { - i_change(pitm, itype, -1); - } - if (*pitm == itm) { - pitm = &itm->next; - } - } - else { - pitm = &itm->next; - } - } + unit *du = dt.fighter->unit; + + item **pitm; + + for (pitm = &du->items; *pitm;) { + item *itm = *pitm; + const item_type *itype = itm->type; + if (!(itype->flags & (ITF_CURSED | ITF_NOTLOST)) && dt.index < itm->number) { + /* 25% Grundchance, dass ein Item kaputtgeht. */ + if (rng_int() % 4 < 1) { + i_change(pitm, itype, -1); + } + } + if (*pitm == itm) { + pitm = &itm->next; + } + } } static void calculate_defense_type(troop at, troop dt, int type, bool missile, From 12ce08800c1cd6d1308cdc5efdfc3e07c5b06ee6 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 17 Jun 2020 00:29:23 +0200 Subject: [PATCH 02/52] fix tests for parsing single-letter keywords. --- src/kernel/order.test.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/kernel/order.test.c b/src/kernel/order.test.c index 14cdeb157..1ce993a69 100644 --- a/src/kernel/order.test.c +++ b/src/kernel/order.test.c @@ -103,7 +103,8 @@ static void test_parse_make(CuTest *tc) { locale_setstring(lang, keyword(K_MAKE), "MAKE"); locale_setstring(lang, keyword(K_MAKETEMP), "MAKETEMP"); init_locale(lang); - ord = parse_order("M hurrdurr", lang); + CuAssertPtrEquals(tc, NULL, parse_order("M herp", lang)); + ord = parse_order("MA hurrdurr", lang); CuAssertPtrNotNull(tc, ord); CuAssertIntEquals(tc, K_MAKE, getkeyword(ord)); CuAssertStrEquals(tc, "MAKE hurrdurr", get_command(ord, lang, cmd, sizeof(cmd))); @@ -163,7 +164,8 @@ static void test_parse_make_temp(CuTest *tc) { locale_setstring(lang, parameters[P_TEMP], "TEMP"); init_locale(lang); - ord = parse_order("M T herp", lang); + CuAssertPtrEquals(tc, NULL, parse_order("M T herp", lang)); + ord = parse_order("MA TE herp", lang); CuAssertPtrNotNull(tc, ord); CuAssertIntEquals(tc, K_MAKETEMP, getkeyword(ord)); CuAssertStrEquals(tc, "MAKETEMP herp", get_command(ord, lang, cmd, sizeof(cmd))); From b780bf9c6067c58f9c2ff7cc65d0aead8a3a81b5 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 7 Jun 2020 10:10:51 +0200 Subject: [PATCH 03/52] remove legacy filereader code --- src/kernel/save.c | 1 - src/orderfile.c | 1 - src/util/CMakeLists.txt | 2 - src/util/filereader.c | 198 ---------------------------------------- src/util/filereader.h | 14 --- 5 files changed, 216 deletions(-) delete mode 100644 src/util/filereader.c delete mode 100644 src/util/filereader.h diff --git a/src/kernel/save.c b/src/kernel/save.c index 4d9574a61..b401256ae 100644 --- a/src/kernel/save.c +++ b/src/kernel/save.c @@ -43,7 +43,6 @@ #include #include #include -#include #include #include #include diff --git a/src/orderfile.c b/src/orderfile.c index a42d5ee69..0f0c951a2 100644 --- a/src/orderfile.c +++ b/src/orderfile.c @@ -11,7 +11,6 @@ #include "util/message.h" #include "util/language.h" #include "util/log.h" -#include "util/filereader.h" #include "util/param.h" #include "util/parser.h" #include "util/password.h" diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 19bf70b12..eeb16df1a 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -6,7 +6,6 @@ SET(_TEST_FILES base36.test.c # crmessage.test.c # dice.test.c -# filereader.test.c functions.test.c # goodies.test.c keyword.test.c @@ -35,7 +34,6 @@ SET(_FILES base36.c crmessage.c dice.c -filereader.c functions.c goodies.c keyword.c diff --git a/src/util/filereader.c b/src/util/filereader.c deleted file mode 100644 index 5973c8f1c..000000000 --- a/src/util/filereader.c +++ /dev/null @@ -1,198 +0,0 @@ -#include -#include "filereader.h" - -#include -#include - -#include -#include -#include - -#define COMMENT_CHAR ';' -#define CONTINUE_CHAR '\\' -#define MAXLINE 4096*16 -static char lbuf[MAXLINE]; -static char fbuf[MAXLINE]; - -static void unicode_warning(const char *bp) -{ - log_warning("invalid sequence in UTF-8 string: %s\n", bp); -} - -static int eatwhite(const char *ptr, size_t * total_size) -{ - int ret = 0; - - *total_size = 0; - - while (*ptr) { - wint_t wc; - size_t size = 0; - ret = unicode_utf8_decode(&wc, ptr, &size); - if (ret != 0) - break; - if (!iswspace(wc)) - break; - *total_size += size; - ptr += size; - } - return ret; -} - -static const char *getbuf_utf8(FILE * F) -{ - bool cont = false; - char quote = 0; - bool comment = false; - char *cp = fbuf; - char *tail = lbuf + MAXLINE - 2; - - tail[1] = '@'; /* if this gets overwritten by fgets then the line was very long. */ - do { - const char *bp = fgets(lbuf, MAXLINE, F); - size_t white; - if (bp == NULL) { - return NULL; - } - - eatwhite(bp, &white); /* decoding errors will get caught later on, don't have to check */ - bp += white; - - comment = (comment && cont); - quote = (quote && cont); - - if (tail[1] == 0) { - /* we read the maximum number of bytes! */ - if (tail[0] != '\n') { - /* it wasn't enough space to finish the line, eat the rest */ - for (;;) { - tail[1] = '@'; - bp = fgets(lbuf, MAXLINE, F); - if (bp == NULL) - return NULL; - if (tail[1]) { - /* read enough this time to end the line */ - break; - } - } - comment = false; - cont = false; - bp = NULL; - continue; - } - else { - tail[1] = '@'; - } - } - cont = false; - while (*bp && cp < fbuf + MAXLINE) { - wint_t wc; - size_t size; - int ret; - - if (!quote) { - while (*bp == COMMENT_CHAR) { - /* comment begins. we need to keep going, to look for CONTINUE_CHAR */ - comment = true; - ++bp; - } - } - - if (*bp == '\n' || *bp == '\r') { - /* line breaks, shmine breaks */ - break; - } - - if (*bp == '"' || *bp == '\'') { - if (quote == *bp) { - quote = 0; - if (!comment && cp < fbuf + MAXLINE) - *cp++ = *bp; - ++bp; - continue; - } - else if (!quote) { - quote = *bp++; - if (!comment && cp < fbuf + MAXLINE) - *cp++ = quote; - continue; - } - } - - ret = unicode_utf8_decode(&wc, bp, &size); - - if (ret != 0) { - unicode_warning(bp); - break; - } - - if (iswspace(wc)) { - if (!quote) { - bp += size; - ret = eatwhite(bp, &size); - bp += size; - if (!comment && *bp && *bp != COMMENT_CHAR && cp < fbuf + MAXLINE) - *(cp++) = ' '; - if (ret != 0) { - unicode_warning(bp); - break; - } - } - else if (!comment) { - if (cp + size <= fbuf + MAXLINE) { - while (size--) { - *(cp++) = *(bp++); - } - } - else - bp += size; - } - else { - bp += size; - } - } - else if (iswcntrl(wc)) { - if (!comment && cp < fbuf + MAXLINE) { - *cp++ = '?'; - } - bp += size; - } - else { - if (*bp == CONTINUE_CHAR) { - const char *handle_end; - eatwhite(bp + 1, &white); - handle_end = bp + 1 + white; - if (*handle_end == '\0') { - bp = handle_end; - cont = true; - continue; - } - if (!comment && cp < fbuf + MAXLINE) - *cp++ = *bp++; - else - ++bp; - } - else { - if (!comment && cp + size <= fbuf + MAXLINE) { - while (size--) { - *(cp++) = *(bp++); - } - } - else { - bp += size; - } - } - } - } - if (cp == fbuf + MAXLINE) { - --cp; - } - *cp = 0; - } while (cont || cp == fbuf); - return fbuf; -} - -const char *getbuf(FILE * F) -{ - return getbuf_utf8(F); -} diff --git a/src/util/filereader.h b/src/util/filereader.h deleted file mode 100644 index 53c24f697..000000000 --- a/src/util/filereader.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef UTIL_FILEREADER_H -#define UTIL_FILEREADER_H - -#include -#ifdef __cplusplus -extern "C" { -#endif - - const char *getbuf(FILE *); - -#ifdef __cplusplus -} -#endif -#endif From 140eebc4f1d166bc2639e1a615ea30c05dd048b3 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 7 Jun 2020 12:16:23 +0200 Subject: [PATCH 04/52] Bug 2670 trim   and others from strings. --- src/util/unicode.c | 2 +- src/util/unicode.test.c | 37 +++++++++++++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/util/unicode.c b/src/util/unicode.c index e87c27fc3..c7fada910 100644 --- a/src/util/unicode.c +++ b/src/util/unicode.c @@ -25,7 +25,7 @@ #define B00000001 0x01 static bool char_trimmed(wint_t wc) { - if (wc >= 0x2000 && wc <= 0x200f) { + if (wc == 0xa0 || wc == 0x202f || (wc >= 0x2000 && wc <= 0x200f)) { /* only weird stuff here */ return true; } diff --git a/src/util/unicode.test.c b/src/util/unicode.test.c index 8a5d77488..f746c77da 100644 --- a/src/util/unicode.test.c +++ b/src/util/unicode.test.c @@ -154,7 +154,7 @@ static void test_unicode_compare(CuTest *tc) } static void test_unicode_trim_zwnj(CuTest *tc) { - const char zwnj[] = { 0xe2, 0x80, 0x8c, 0x00 }; + const char zwnj[] = { 0xe2, 0x80, 0x8c, 0 }; char name[64]; char expect[64]; snprintf(name, sizeof(name), "%sA%sB%s ", zwnj, zwnj, zwnj); @@ -163,8 +163,38 @@ static void test_unicode_trim_zwnj(CuTest *tc) { CuAssertStrEquals(tc, expect, name); } +static void test_unicode_trim_nbsp(CuTest *tc) { + const char code[] = { 0xc2, 0xa0, 0 }; + char name[64]; + char expect[64]; + snprintf(name, sizeof(name), "%sA%sB%s ", code, code, code); + snprintf(expect, sizeof(expect), "A%sB", code); + CuAssertIntEquals(tc, 6, unicode_utf8_trim(name)); + CuAssertStrEquals(tc, expect, name); +} + +static void test_unicode_trim_nnbsp(CuTest *tc) { + const char code[] = { 0xe2, 0x80, 0xaf, 0 }; + char name[64]; + char expect[64]; + snprintf(name, sizeof(name), "%sA%sB%s ", code, code, code); + snprintf(expect, sizeof(expect), "A%sB", code); + CuAssertIntEquals(tc, 8, unicode_utf8_trim(name)); + CuAssertStrEquals(tc, expect, name); +} + +static void test_unicode_trim_figure_space(CuTest *tc) { + const char code[] = { 0xe2, 0x80, 0x87, 0 }; + char name[64]; + char expect[64]; + snprintf(name, sizeof(name), "%sA%sB%s ", code, code, code); + snprintf(expect, sizeof(expect), "A%sB", code); + CuAssertIntEquals(tc, 8, unicode_utf8_trim(name)); + CuAssertStrEquals(tc, expect, name); +} + static void test_unicode_trim_ltrm(CuTest *tc) { - const char ltrm[] = { 0xe2, 0x80, 0x8e, 0x00 }; + const char ltrm[] = { 0xe2, 0x80, 0x8e, 0 }; char name[64]; char expect[64]; snprintf(name, sizeof(name), "%sBrot%szeit%s ", ltrm, ltrm, ltrm); @@ -188,6 +218,9 @@ CuSuite *get_unicode_suite(void) CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_unicode_trim); SUITE_ADD_TEST(suite, test_unicode_trim_zwnj); + SUITE_ADD_TEST(suite, test_unicode_trim_nbsp); + SUITE_ADD_TEST(suite, test_unicode_trim_nnbsp); + SUITE_ADD_TEST(suite, test_unicode_trim_figure_space); SUITE_ADD_TEST(suite, test_unicode_trim_ltrm); SUITE_ADD_TEST(suite, test_unicode_trim_emoji); SUITE_ADD_TEST(suite, test_unicode_utf8_to_other); From 2286c8d00584f57d53559e5379adb1c40020e7c4 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 15 Jun 2020 20:05:31 +0200 Subject: [PATCH 05/52] bug 2673 ignore commands that have fewer than 2 characters. --- src/util/keyword.c | 2 +- src/util/param.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/keyword.c b/src/util/keyword.c index 9d5ba1e6a..f1a59f44a 100644 --- a/src/util/keyword.c +++ b/src/util/keyword.c @@ -61,7 +61,7 @@ keyword_t get_keyword(const char *s, const struct locale *lang) { char buffer[64]; char *str = transliterate(buffer, sizeof(buffer) - sizeof(int), s); - if (str) { + if (str && str[0] && str[1]) { int i; void *match; void **tokens = get_translations(lang, UT_KEYWORDS); diff --git a/src/util/param.c b/src/util/param.c index 0c486ce5a..38a33d322 100644 --- a/src/util/param.c +++ b/src/util/param.c @@ -67,7 +67,7 @@ param_t findparam(const char *s, const struct locale * lang) char buffer[64]; char * str = s ? transliterate(buffer, sizeof(buffer) - sizeof(int), s) : 0; - if (str && *str) { + if (str && str[0] && str[1]) { int i; void * match; void **tokens = get_translations(lang, UT_PARAMS); From 98c7b2e13e2fa76e0097fd9d31e9f466cfc2644f Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 15 Jun 2020 20:08:32 +0200 Subject: [PATCH 06/52] update default version number --- src/kernel/version.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kernel/version.c b/src/kernel/version.c index 7546bceac..8c694f7f0 100644 --- a/src/kernel/version.c +++ b/src/kernel/version.c @@ -8,7 +8,7 @@ #ifndef ERESSEA_VERSION /* the version number, if it was not passed to make with -D */ -#define ERESSEA_VERSION "3.24.0" +#define ERESSEA_VERSION "3.25.0" #endif const char *eressea_version(void) { From 00324868d04d84ce6efe477277bcac992e2c0509 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Fri, 26 Jun 2020 21:07:32 +0200 Subject: [PATCH 07/52] install lunitx only once --- s/cmake-init | 1 - 1 file changed, 1 deletion(-) diff --git a/s/cmake-init b/s/cmake-init index 00e6d3d06..bdb5fce41 100755 --- a/s/cmake-init +++ b/s/cmake-init @@ -127,7 +127,6 @@ TOLUA else echo "tolua is $path" fi -luarocks --local install lunitx unset path set -e From f3859be76d140cfe8e72b2f1f427c1d0dbdbdf0a Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 27 Jun 2020 10:09:30 +0200 Subject: [PATCH 08/52] =?UTF-8?q?Durch=20GIB=20"leer"=20gewordene=20Schiff?= =?UTF-8?q?e=20k=C3=B6nnen=20nicht=20sinken.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/give.c | 7 ++++++- src/kernel/ship.c | 13 ++++++++----- src/laws.c | 27 ++++++++++++++++----------- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/give.c b/src/give.c index e1a3cec66..d6813abd3 100644 --- a/src/give.c +++ b/src/give.c @@ -302,7 +302,12 @@ static void transfer_ships(ship *s1, ship *s2, int n) if (s1->coast != NODIRECTION) { s2->coast = s1->coast; } - scale_ship(s1, s1->number - n); + if (n == s1->number) { + s1->number = 0; + } + else { + scale_ship(s1, s1->number - n); + } } static void transfer_units(ship *s1, ship *s2) diff --git a/src/kernel/ship.c b/src/kernel/ship.c index 87194834b..29867ca15 100644 --- a/src/kernel/ship.c +++ b/src/kernel/ship.c @@ -510,12 +510,15 @@ void ship_update_owner(ship * sh) { unit *ship_owner(const ship * sh) { - unit *owner = sh->_owner; - if (!owner || (owner->ship != sh || owner->number <= 0)) { - unit * heir = ship_owner_ex(sh, owner ? owner->faction : 0); - return (heir && heir->number > 0) ? heir : 0; + if (sh->number > 0) { + unit *owner = sh->_owner; + if (!owner || (owner->ship != sh || owner->number <= 0)) { + unit * heir = ship_owner_ex(sh, owner ? owner->faction : 0); + return (heir && heir->number > 0) ? heir : 0; + } + return owner; } - return owner; + return NULL; } void write_ship_reference(const struct ship *sh, struct storage *store) diff --git a/src/laws.c b/src/laws.c index 01aa4be8d..458d7b660 100644 --- a/src/laws.c +++ b/src/laws.c @@ -2576,22 +2576,27 @@ void sinkships(struct region * r) while (*shp) { ship *sh = *shp; - if (!sh->type->construction || sh->size >= sh->type->construction->maxsize) { - if (fval(r->terrain, SEA_REGION)) { - if (!ship_crewed(sh)) { - /* ship is at sea, but not enough people to control it */ - double dmg = config_get_flt("rules.ship.damage.nocrewocean", 0.3); + if (sh->number > 0) { + if (!sh->type->construction || sh->size >= sh->type->construction->maxsize) { + if (fval(r->terrain, SEA_REGION)) { + if (!ship_crewed(sh)) { + /* ship is at sea, but not enough people to control it */ + double dmg = config_get_flt("rules.ship.damage.nocrewocean", 0.3); + damage_ship(sh, dmg); + } + } + else if (!ship_owner(sh)) { + /* any ship lying around without an owner slowly rots */ + double dmg = config_get_flt("rules.ship.damage.nocrew", 0.05); damage_ship(sh, dmg); } } - else if (!ship_owner(sh)) { - /* any ship lying around without an owner slowly rots */ - double dmg = config_get_flt("rules.ship.damage.nocrew", 0.05); - damage_ship(sh, dmg); + if (sh->damage >= sh->size * DAMAGE_SCALE) { + sink_ship(sh); + remove_ship(shp, sh); } } - if (sh->damage >= sh->size * DAMAGE_SCALE) { - sink_ship(sh); + else { remove_ship(shp, sh); } if (*shp == sh) From 2a2c73120f54773de4ac013d04546a5f0e95dbe5 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 27 Jun 2020 10:13:37 +0200 Subject: [PATCH 09/52] bug 2674 send message to player, not monsters --- src/chaos.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/chaos.c b/src/chaos.c index 69b9fa95d..3f9d9b5c7 100644 --- a/src/chaos.c +++ b/src/chaos.c @@ -83,9 +83,10 @@ static void chaos(region * r, faction *monsters) case 0: /* Untote */ if (!(r->terrain->flags & SEA_REGION)) { unit *u = random_unit(r); + faction * f = u->faction; if (u && !undeadrace(u_race(u))) { if (join_monsters(u, monsters)) { - ADDMSG(&u->faction->msgs, msg_message("chaos_disease", "unit", u)); + ADDMSG(&f->msgs, msg_message("chaos_disease", "unit", u)); u_setrace(u, get_race(RC_GHOUL)); } } From 13ed69ed375b31c7ee959fb573da0dd7ca6d70b0 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 27 Jun 2020 14:27:38 +0200 Subject: [PATCH 10/52] bug 2674 fix null-ptr deref --- src/chaos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chaos.c b/src/chaos.c index 3f9d9b5c7..5de776519 100644 --- a/src/chaos.c +++ b/src/chaos.c @@ -83,8 +83,8 @@ static void chaos(region * r, faction *monsters) case 0: /* Untote */ if (!(r->terrain->flags & SEA_REGION)) { unit *u = random_unit(r); - faction * f = u->faction; if (u && !undeadrace(u_race(u))) { + faction * f = u->faction; if (join_monsters(u, monsters)) { ADDMSG(&f->msgs, msg_message("chaos_disease", "unit", u)); u_setrace(u, get_race(RC_GHOUL)); From f726176942a3374ebf4327886405514eaa4e645b Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 27 Jun 2020 16:33:20 +0200 Subject: [PATCH 11/52] Lighthouse shows roads in both CR and NR. This closes bug 2669. --- src/creport.c | 4 ---- src/report.c | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/creport.c b/src/creport.c index 4f92fe132..237a6eff7 100644 --- a/src/creport.c +++ b/src/creport.c @@ -1132,10 +1132,6 @@ cr_borders(const region * r, const faction * f, seen_mode mode, FILE * F) const connection *b; if (!r2) continue; - if (mode == seen_neighbour) { - if (r2->seen.mode <= seen_neighbour) - continue; - } b = get_borders(r, r2); while (b) { bool cs = b->type->fvisible(b, f, r); diff --git a/src/report.c b/src/report.c index 1560f42e2..b8b65a885 100644 --- a/src/report.c +++ b/src/report.c @@ -1155,7 +1155,7 @@ void report_region(struct stream *out, const region * r, faction * f) if (r->seen.mode >= seen_unit) { report_region_schemes(out, r, f); } - if (r->seen.mode >= seen_lighthouse) { + if (r->seen.mode >= seen_lighthouse_land) { report_region_edges(out, r, f, edges, ne); } } From 08becab2acd6652b80ed2c342b91c8a6561a25ed Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 27 Jun 2020 16:59:16 +0200 Subject: [PATCH 12/52] accept some selected changes from stm2's script rewrite. --- README.md | 26 +++++++++++++++++--------- process/compress.py | 2 +- process/compress.sh | 6 ++++-- process/cron/orders.cron | 11 +++++++++-- 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 646d036f2..4be1c324e 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,31 @@ # What is this? -This repository contains the source code for the Play-by-Mail strategy game [Eressea](http://www.eressea.de/). +This repository contains the source code for the Play-by-Mail +strategy game [Eressea](http://www.eressea.de/). # Prerequisites -Eressea depends on a number of external libraries. On a recent Debian-based Linux system, this is the apt-get command to install all of them: +Eressea depends on a number of external libraries. On a recent +Debian-based Linux system, this is the apt-get command to +install all of them: - sudo apt-get install git cmake gcc make libxml2-dev liblua5.2-dev libtolua-dev libncurses5-dev libsqlite3-dev luarocks + sudo apt-get install git cmake gcc make luarocks libxml2-dev \ + liblua5.2-dev libtolua-dev libncurses5-dev libsqlite3-dev # How to check out and build the Eressea server -This repository relies heavily on the use of submodules, and it pulls in most of the code from those. The build system being used is cmake, which can create Makefiles on Unix, or Visual Studio project files on Windows. Here's how you clone and build the source on Ubuntu: +This repository relies heavily on the use of submodules, and it pulls in +most of the code from those. The build system being used is cmake, which +can create Makefiles on Unix, or Visual Studio project files on Windows. +Here's how you clone and build the source on Linux or macOS: - git clone --recursive git://github.com/eressea/server.git - cd server - ./configure - ./s/build + git clone --recursive git://github.com/eressea/server.git source + cd source + git submodule update --init + s/build -If you got this far and all went well, you have built a server (it is linked from the `game` subdirectory), and it will have passed some basic functionality tests. +If you got this far and all went well, you have built the server, and +it will have passed some basic functionality tests. * [![Static Analysis](https://scan.coverity.com/projects/6742/badge.svg?flat=1)](https://scan.coverity.com/projects/6742/) * [![Build Status](https://api.travis-ci.org/eressea/server.svg?branch=develop)](https://travis-ci.org/eressea/server) diff --git a/process/compress.py b/process/compress.py index 204d580d7..0066c232f 100755 --- a/process/compress.py +++ b/process/compress.py @@ -15,7 +15,7 @@ addr=%(email)s [ $# -ge 1 ] && addr=$1 -[ -z $addr ] || send-%(compression)s-report $addr '%(gamename)s Report #%(turn)s' %(files)s +[ -z $addr ] || $ERESSEA/server/bin/send-%(compression)s-report $addr '%(gamename)s Report #%(turn)s' %(files)s """ turn = argv[1] diff --git a/process/compress.sh b/process/compress.sh index 21ac828ef..c4343d95e 100755 --- a/process/compress.sh +++ b/process/compress.sh @@ -5,8 +5,10 @@ if [ -z "$ERESSEA" ]; then exit 2 fi +BINDIR="$ERESSEA/server/bin" + GAME="$ERESSEA/game-$1" -GAME_NAME=$(grep -w name "$GAME/eressea.ini" | sed 's/.*=\s*//') +GAME_NAME=$("$BINDIR/inifile" "$GAME/eressea.ini" get game:name) TURN=$2 if [ -z "$TURN" ] @@ -20,6 +22,6 @@ if [ ! -d "$GAME/reports" ]; then fi cd "$GAME/reports" || exit -"$ERESSEA/server/bin/compress.py" "$TURN" "$GAME_NAME" +"$BINDIR/compress.py" "$TURN" "$GAME_NAME" cd - || exit diff --git a/process/cron/orders.cron b/process/cron/orders.cron index 980f11ee2..dc060a5e1 100755 --- a/process/cron/orders.cron +++ b/process/cron/orders.cron @@ -5,11 +5,18 @@ # this here script to make a non-blocking syntax check and reject or # accept the order file. -if [ "yes" != "$CONFIRM" ] ; then +if [ "no" == "$CONFIRM" ] ; then exit fi +if [ -z "$ERESSEA" ] ; then + ERESSEA="$HOME/eressea" + echo "The ERESSEA environment variable is not set. Assuming $ERESSEA." +fi + +cd $ERESSEA + for GAME in $* do - $HOME/eressea/orders-php/check-orders.sh $GAME + orders-php/check-orders.sh $GAME done From 5a2dc76b38e5620ac8bf3fa1afdf307107b61560 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 27 Jun 2020 17:30:24 +0200 Subject: [PATCH 13/52] delete obsolete scripts, shellcheck the rest --- process/cron/orders.cron | 7 +++--- process/cron/preview.cron | 2 +- process/cron/run-eressea.cron | 44 +++++++++++++++++------------------ process/orders-process | 5 ++-- process/received-mail.sh | 1 - process/received.sh | 2 -- 6 files changed, 30 insertions(+), 31 deletions(-) delete mode 100644 process/received-mail.sh delete mode 100644 process/received.sh diff --git a/process/cron/orders.cron b/process/cron/orders.cron index dc060a5e1..7632ac844 100755 --- a/process/cron/orders.cron +++ b/process/cron/orders.cron @@ -14,9 +14,10 @@ if [ -z "$ERESSEA" ] ; then echo "The ERESSEA environment variable is not set. Assuming $ERESSEA." fi -cd $ERESSEA +cd "$ERESSEA" || exit -for GAME in $* +for GAME in "$@" do - orders-php/check-orders.sh $GAME + orders-php/check-orders.sh "$GAME" done + diff --git a/process/cron/preview.cron b/process/cron/preview.cron index 5192d0f5a..04869c2f2 100755 --- a/process/cron/preview.cron +++ b/process/cron/preview.cron @@ -3,7 +3,7 @@ [ "$PREVIEW" != "yes" ] && exit [ -z "${ERESSEA}" ] && ERESSEA="$HOME/eressea" -eval $(luarocks path) +eval "$(luarocks path)" branch="develop" if [ -e "${ERESSEA}/build/.preview" ]; then branch=$(cat "${ERESSEA}/build/.preview") diff --git a/process/cron/run-eressea.cron b/process/cron/run-eressea.cron index 32ad873df..aa3ed287b 100755 --- a/process/cron/run-eressea.cron +++ b/process/cron/run-eressea.cron @@ -1,55 +1,55 @@ #!/bin/bash -eval $(luarocks path) +eval "$(luarocks path)" GAME=$1 ( -[ "$ENABLED" != "yes" ] && exit -[ -z ${ERESSEA} ] && ERESSEA=$HOME/eressea +[ "$ENABLED" == "no" ] && exit +[ -z "$ERESSEA" ] && ERESSEA="$HOME/eressea" export ERESSEA -BIN=$ERESSEA/server/bin -TURN=$(cat $ERESSEA/game-$GAME/turn) -if [ ! -e $ERESSEA/game-$GAME/data/$TURN.dat ]; then +BIN="$ERESSEA/server/bin" +TURN=$(cat "$ERESSEA/game-$GAME/turn") +if [ ! -e "$ERESSEA/game-$GAME/data/$TURN.dat" ]; then echo "data file $TURN is missing, cannot run turn for game $GAME" exit 1 fi -REPORTS=$ERESSEA/game-$GAME/reports -if [ -d $REPORTS ]; then - rm -rf $REPORTS +REPORTS="$ERESSEA/game-$GAME/reports" +if [ -d "$REPORTS" ]; then + rm -rf "$REPORTS" fi -mkdir $REPORTS +mkdir "$REPORTS" -cd $ERESSEA/game-$GAME +cd "$ERESSEA/game-$GAME" || exit if [ -d test ]; then touch test/execute.lock fi -$BIN/create-orders $GAME $TURN -if [ ! -s $ERESSEA/game-$GAME/orders.$TURN ]; then +"$BIN/create-orders" "$GAME" "$TURN" +if [ ! -s "$ERESSEA/game-$GAME/orders.$TURN" ]; then echo "server did not create orders for turn $TURN in game $GAME" exit 2 fi -$BIN/backup-eressea $GAME $TURN +"$BIN/backup-eressea" "$GAME" "$TURN" rm -f execute.lock -$BIN/run-turn $GAME $TURN +"$BIN/run-turn" "$GAME" "$TURN" touch execute.lock -if [ ! -s $REPORTS/reports.txt ]; then +if [ ! -s "$REPORTS/reports.txt" ]; then echo "server did not create reports.txt in game $GAME" exit 4 fi -$BIN/backup-eressea $GAME $TURN +"$BIN/backup-eressea" "$GAME" "$TURN" let TURN=$TURN+1 -if [ ! -s $ERESSEA/game-$GAME/data/$TURN.dat ]; then +if [ ! -s "$ERESSEA/game-$GAME/data/$TURN.dat" ]; then echo "server did not create data for turn $TURN in game $GAME" exit 3 fi echo "sending reports for game $GAME, turn $TURN" -$BIN/compress.sh $GAME $TURN -$BIN/sendreports.sh $GAME -$BIN/backup-eressea $GAME $TURN +"$BIN/compress.sh" "$GAME" "$TURN" +"$BIN/sendreports.sh" "$GAME" +"$BIN/backup-eressea" "$GAME" "$TURN" rm -f test/execute.lock -) | tee -a $HOME/log/eressea.cron.log +) | tee -a "$HOME/log/eressea.cron.log" diff --git a/process/orders-process b/process/orders-process index 4751a94b1..d77b7fa2f 100755 --- a/process/orders-process +++ b/process/orders-process @@ -1,6 +1,7 @@ #!/bin/sh -SCRIPT=$(readlink -f $0) -cd $(dirname $SCRIPT) +SCRIPT=$(readlink -f "$0") +SCRDIR=$(dirname "$SCRIPT") +cd "$SCRDIR" || exit lockfile -r3 -l120 orders.queue.lock python process-orders.py "$@" diff --git a/process/received-mail.sh b/process/received-mail.sh deleted file mode 100644 index b6b4a0605..000000000 --- a/process/received-mail.sh +++ /dev/null @@ -1 +0,0 @@ -ls -1 orders.dir/turn* | sed -e 's/.*turn-\(.*\),gruenbaer.*/\1/' | sort -u diff --git a/process/received.sh b/process/received.sh deleted file mode 100644 index 9a0c4511d..000000000 --- a/process/received.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -grep -hiw ERESSEA orders.dir/turn-* | cut -d\ -f2 | sort -u From 6e3aef4a6e80594ef1d9c6a5bfab82fe5508e380 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 27 Jun 2020 17:40:18 +0200 Subject: [PATCH 14/52] make coverity understand that there is no buffer overflow here. --- src/gmtool.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/gmtool.c b/src/gmtool.c index 394f1efde..798351a8d 100644 --- a/src/gmtool.c +++ b/src/gmtool.c @@ -1514,14 +1514,12 @@ static void handlekey(state * st, int c) } if (wnd == NULL) { static char kbuffer[80]; - if (kbuffer[0] == 0) { + if (kbuffer[0] == 0 || strlen(kbuffer) > 70) { strcpy(kbuffer, "getch:"); } - sprintf(sbuffer, " 0x%x", c); + snprintf(sbuffer, 10, " 0x%x", c); strncat(kbuffer, sbuffer, sizeof(kbuffer) - 1); statusline(st->wnd_status->handle, kbuffer); - if (strlen(kbuffer) > 70) - kbuffer[0] = 0; } break; } From 513cb401d44f13b024f1244c18790e8408b5692e Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 27 Jun 2020 18:28:42 +0200 Subject: [PATCH 15/52] stop showing unhandled keycodes in status --- src/gmtool.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/gmtool.c b/src/gmtool.c index 798351a8d..033823dc0 100644 --- a/src/gmtool.c +++ b/src/gmtool.c @@ -1512,15 +1512,6 @@ static void handlekey(state * st, int c) break; } } - if (wnd == NULL) { - static char kbuffer[80]; - if (kbuffer[0] == 0 || strlen(kbuffer) > 70) { - strcpy(kbuffer, "getch:"); - } - snprintf(sbuffer, 10, " 0x%x", c); - strncat(kbuffer, sbuffer, sizeof(kbuffer) - 1); - statusline(st->wnd_status->handle, kbuffer); - } break; } } From 672a22e3d091dd22da13dcc4d3e5615ec2fc0d9f Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 4 Jul 2020 11:48:24 +0200 Subject: [PATCH 16/52] bug 2678: fix trade_needs_castle The logic for "insects do not need castles in deserts or swamps" was broken. --- src/economy.c | 5 ++++- src/economy.test.c | 13 +++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/economy.c b/src/economy.c index 4d2c30d90..f0e655b99 100644 --- a/src/economy.c +++ b/src/economy.c @@ -1175,11 +1175,14 @@ bool trade_needs_castle(const terrain_type *terrain, const race *rc) if (rc_changed(&rc_change)) { rc_insect = get_race(RC_INSECT); } + if (rc != rc_insect) { + return true; + } if (terrain_changed(&terrain_change)) { t_swamp = newterrain(T_SWAMP); t_desert = newterrain(T_DESERT); } - return rc != rc_insect && (terrain == t_swamp || terrain == t_desert); + return (terrain != t_swamp && terrain != t_desert); } static building * first_building(region *r, const struct building_type *btype, int minsize) { diff --git a/src/economy.test.c b/src/economy.test.c index 034a6cf44..4cda069d0 100644 --- a/src/economy.test.c +++ b/src/economy.test.c @@ -269,7 +269,7 @@ static void test_trade_needs_castle(CuTest *tc) { region *r; unit *u; building *b; - const terrain_type *t_swamp; + const terrain_type *t_swamp, *t_desert, *t_plain; const item_type *it_luxury; test_setup(); @@ -278,11 +278,15 @@ static void test_trade_needs_castle(CuTest *tc) { setup_terrains(tc); init_terrains(); t_swamp = get_terrain("swamp"); - r = setup_trade_region(tc, t_swamp); + t_desert = get_terrain("desert"); + t_plain = get_terrain("plain"); + r = setup_trade_region(tc, t_plain); it_luxury = r_luxury(r); rc = test_create_race(NULL); CuAssertTrue(tc, trade_needs_castle(t_swamp, rc)); + CuAssertTrue(tc, trade_needs_castle(t_desert, rc)); + CuAssertTrue(tc, trade_needs_castle(t_plain, rc)); u = test_create_unit(test_create_faction(rc), r); unit_addorder(u, create_order(K_BUY, u->faction->locale, "1 %s", @@ -305,6 +309,11 @@ static void test_trade_needs_castle(CuTest *tc) { test_clear_messages(u->faction); produce(r); CuAssertIntEquals(tc, 0, test_count_messagetype(u->faction->msgs, "error119")); + + rc = test_create_race("insect"); + CuAssertTrue(tc, !trade_needs_castle(t_swamp, rc)); + CuAssertTrue(tc, !trade_needs_castle(t_desert, rc)); + CuAssertTrue(tc, trade_needs_castle(t_plain, rc)); test_teardown(); } From 5cc9ce690de64fbc8a5c039aee787241c2a0262d Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 5 Jul 2020 20:23:16 +0200 Subject: [PATCH 17/52] =?UTF-8?q?Bug=202679:=20Nur=20Bergw=C3=A4chter=20be?= =?UTF-8?q?wachen=20Eisen.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- res/e3a/races.xml | 46 +++++++++---------- res/eressea/races.xml | 94 +++++++++++++++++++-------------------- scripts/tests/economy.lua | 18 ++++++++ src/economy.c | 24 ++++++---- 4 files changed, 104 insertions(+), 78 deletions(-) diff --git a/res/e3a/races.xml b/res/e3a/races.xml index 2316b8aa5..197a2f1d1 100644 --- a/res/e3a/races.xml +++ b/res/e3a/races.xml @@ -192,7 +192,7 @@ - + @@ -218,7 +218,7 @@ - + @@ -239,7 +239,7 @@ - + @@ -260,7 +260,7 @@ - + @@ -283,7 +283,7 @@ - + @@ -306,7 +306,7 @@ - + @@ -328,7 +328,7 @@ - + @@ -400,7 +400,7 @@ - + @@ -425,7 +425,7 @@ - + @@ -448,7 +448,7 @@ - + @@ -471,7 +471,7 @@ - + @@ -493,7 +493,7 @@ - + @@ -520,7 +520,7 @@ - + @@ -543,7 +543,7 @@ - + @@ -565,7 +565,7 @@ - + @@ -589,7 +589,7 @@ - + @@ -651,11 +651,11 @@ - + - + @@ -678,11 +678,11 @@ - + - + @@ -694,7 +694,7 @@ - + @@ -710,7 +710,7 @@ - + @@ -830,7 +830,7 @@ - + diff --git a/res/eressea/races.xml b/res/eressea/races.xml index 71eacc458..106ff1cce 100644 --- a/res/eressea/races.xml +++ b/res/eressea/races.xml @@ -11,7 +11,7 @@ - + @@ -26,7 +26,7 @@ - + @@ -59,7 +59,7 @@ - + @@ -87,7 +87,7 @@ - + @@ -114,7 +114,7 @@ - + @@ -143,7 +143,7 @@ - + @@ -172,7 +172,7 @@ - + @@ -202,7 +202,7 @@ - + @@ -236,7 +236,7 @@ - + @@ -266,7 +266,7 @@ - + @@ -298,7 +298,7 @@ - + @@ -328,7 +328,7 @@ - + @@ -357,7 +357,7 @@ - + @@ -387,7 +387,7 @@ - + @@ -416,7 +416,7 @@ - + @@ -447,7 +447,7 @@ - + @@ -478,7 +478,7 @@ - + @@ -507,7 +507,7 @@ - + @@ -539,7 +539,7 @@ - + @@ -569,16 +569,16 @@ - + - + - + @@ -648,28 +648,28 @@ - + - + - + - + - + @@ -683,25 +683,25 @@ - + - + - + - + @@ -711,7 +711,7 @@ - + @@ -721,7 +721,7 @@ - + @@ -732,7 +732,7 @@ - + @@ -927,7 +927,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes"> - + @@ -964,7 +964,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes"> - + @@ -972,7 +972,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes"> - + @@ -986,7 +986,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes"> - + @@ -1003,7 +1003,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes"> - + @@ -1019,7 +1019,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes"> - + @@ -1034,7 +1034,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes"> - + @@ -1047,7 +1047,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes"> - + @@ -1061,7 +1061,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes"> - + @@ -1074,11 +1074,11 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes"> - + - + @@ -1205,7 +1205,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes"> - + diff --git a/scripts/tests/economy.lua b/scripts/tests/economy.lua index 271d245d6..1ed232502 100644 --- a/scripts/tests/economy.lua +++ b/scripts/tests/economy.lua @@ -214,6 +214,7 @@ function test_ironkeeper_guards_iron() local level = r:get_resourcelevel("iron") local u = unit.create(faction.create("human"), r) u:set_skill("mining", level) + u:set_skill("stealth", 1) -- does not help against ironkeeper local guard = unit.create(faction.create("mountainguard"), r, 1, "mountainguard") guard:add_order("BEWACHEN") u:add_order("MACHE EISEN") @@ -223,6 +224,23 @@ function test_ironkeeper_guards_iron() assert_equal(level, u:get_item("iron")) end +-- bug 2679 +function test_stealthy_iron_producer() + local r = region.create(0, 0, "plain") + r:set_resource("iron", 100) + local level = r:get_resourcelevel("iron") + local u = unit.create(faction.create("human"), r) + u:set_skill("stealth", 1) + u:set_skill("mining", level) + local guard = unit.create(faction.create("human"), r, 1, "human") + guard:add_order("BEWACHEN") + u:add_order("MACHE EISEN") + process_orders() + assert_equal(level, u:get_item("iron")) + process_orders() + assert_equal(2 * level, u:get_item("iron")) +end + function test_sawmill() local r = region.create(0, 0, "plain") r:set_resource("tree", 100) diff --git a/src/economy.c b/src/economy.c index f0e655b99..5f3bb8cbf 100644 --- a/src/economy.c +++ b/src/economy.c @@ -574,6 +574,12 @@ static void allocate_resource(unit * u, const resource_type * rtype, int want) int amount, skill, skill_mod = 0; variant save_mod; skill_t sk; + static const struct race *rc_mountainguard; + static int config; + + if (rc_changed(&config)) { + rc_mountainguard = rc_find("mountainguard"); + } /* momentan kann man keine ressourcen abbauen, wenn man dafuer * Materialverbrauch hat: */ @@ -605,14 +611,16 @@ static void allocate_resource(unit * u, const resource_type * rtype, int want) * Als magische Wesen 'sehen' Bergwaechter alles und werden durch * Belagerung nicht aufgehalten. (Ansonsten wie oben bei Elfen anpassen). */ - if (itype->rtype && (itype->rtype == get_resourcetype(R_IRON) || itype->rtype == rt_find("laen"))) { - unit *u2; - for (u2 = r->units; u2; u2 = u2->next) { - if (!fval(u2, UFL_ISNEW) && u2->number - && is_guard(u2) && !alliedunit(u2, u->faction, HELP_GUARD)) { - ADDMSG(&u->faction->msgs, - msg_feedback(u, u->thisorder, "region_guarded", "guard", u2)); - return; + if (rc_mountainguard) { + if (itype->rtype && (itype->rtype == get_resourcetype(R_IRON) || itype->rtype == rt_find("laen"))) { + unit *u2; + for (u2 = r->units; u2; u2 = u2->next) { + if (rc_mountainguard == u_race(u2) && !fval(u2, UFL_ISNEW) && u2->number > 0 + && is_guard(u2) && !alliedunit(u2, u->faction, HELP_GUARD)) { + ADDMSG(&u->faction->msgs, + msg_feedback(u, u->thisorder, "region_guarded", "guard", u2)); + return; + } } } } From e1a65a8069b4ae3112bdb02761e4b6849ce3be40 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 7 Jul 2020 19:45:41 +0200 Subject: [PATCH 18/52] Bug 2680: shapeshift spell cannot be cast twice. Add a message and don't pay for the failed spell. --- res/core/messages.xml | 8 ++++++++ res/translations/messages.de.po | 3 +++ res/translations/messages.en.po | 3 +++ src/spells.c | 13 ++++++++----- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/res/core/messages.xml b/res/core/messages.xml index 1fd1c106f..13383cd0a 100644 --- a/res/core/messages.xml +++ b/res/core/messages.xml @@ -1299,6 +1299,14 @@ + + + + + + + + diff --git a/res/translations/messages.de.po b/res/translations/messages.de.po index ae0f13dd4..22c080f98 100644 --- a/res/translations/messages.de.po +++ b/res/translations/messages.de.po @@ -1250,6 +1250,9 @@ msgstr "\"$unit($unit) verdient in $region($region) $int($amount) Silber durch U msgid "error180" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Der Zauber schlägt fehl.\"" +msgid "sp_shapeshift_twice" +msgstr "\"$unit($unit) in $region($region): '$order($command)' - $unit($target) ist bereits verzaubert.\"" + msgid "sp_shapeshift_fail" msgstr "\"$unit($unit) in $region($region): '$order($command)' - $unit($target) kann nicht $race($race,1) werden.\"" diff --git a/res/translations/messages.en.po b/res/translations/messages.en.po index b140f1690..c65cb788d 100644 --- a/res/translations/messages.en.po +++ b/res/translations/messages.en.po @@ -1253,6 +1253,9 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - The spell fails msgid "sp_shapeshift_fail" msgstr "\"$unit($unit) in $region($region): '$order($command)' - $unit($target) cannot take the form of $race($race,1).\"" +msgid "sp_shapeshift_twice" +msgstr "\"$unit($unit) in $region($region): '$order($command)' - $unit($target) is already under this spell.\"" + msgid "error290" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Eine Einheit kann nur in einem Verband Mitglied sein.\"" diff --git a/src/spells.c b/src/spells.c index de2036225..910e3fe26 100644 --- a/src/spells.c +++ b/src/spells.c @@ -4512,13 +4512,16 @@ int sp_illusionary_shapeshift(castorder * co) return 0; } irace = u_irace(u); - if (irace == u_race(u)) { - trigger *trestore = trigger_changerace(u, NULL, irace); - add_trigger(&u->attribs, "timer", trigger_timeout((int)power + 3, - trestore)); - u->irace = rc; + if (irace != u_race(u)) { + ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, + "sp_shapeshift_twice", "target", u)); + return 0; } + add_trigger(&u->attribs, "timer", trigger_timeout((int)power + 3, + trigger_changerace(u, NULL, irace))); + u->irace = rc; + ADDMSG(&mage->faction->msgs, msg_message("shapeshift_effect", "mage target race", mage, u, rc)); From 34e842a112e007e22983c41d1b36c4b7163f5ef0 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 12 Jul 2020 20:41:24 +0200 Subject: [PATCH 19/52] empty convoys cannot be built on. --- src/kernel/ship.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/kernel/ship.c b/src/kernel/ship.c index 29867ca15..55cddbba8 100644 --- a/src/kernel/ship.c +++ b/src/kernel/ship.c @@ -146,12 +146,14 @@ void sunhash(ship * s) static ship *sfindhash(int i) { - ship *old; + ship *sh; - for (old = shiphash[i % MAXSHIPHASH]; old; old = old->nexthash) - if (old->no == i) - return old; - return 0; + for (sh = shiphash[i % MAXSHIPHASH]; sh; sh = sh->nexthash) { + if (sh->no == i) { + return sh->number > 0 ? sh : NULL; + } + } + return NULL; } struct ship *findship(int i) From 78f0b28c4a286ce9d81c6e9c80fe10d74e4287e5 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 16 Jul 2020 19:41:45 +0200 Subject: [PATCH 20/52] Bug 2683: fix buildings with size 0 Fixing the symptoms, not the cause, becasue I don't know how this happened. --- src/kernel/save.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/kernel/save.c b/src/kernel/save.c index b401256ae..c720441c0 100644 --- a/src/kernel/save.c +++ b/src/kernel/save.c @@ -1251,6 +1251,7 @@ void write_building(gamedata *data, const building *b) write_building_reference(b, store); WRITE_STR(store, b->name); WRITE_STR(store, b->display ? b->display : ""); + assert(b->size > 0); WRITE_INT(store, b->size); WRITE_TOK(store, b->type->_name); write_attribs(store, b->attribs, b); @@ -1276,6 +1277,10 @@ struct building *read_building(gamedata *data) { } b->display = str_strdup(name); READ_INT(store, &b->size); + if (b->size < 1) { + log_warning("trim building %s had size %d", itoa36(b->no), b->size); + b->size = 1; + } READ_STR(store, name, sizeof(name)); b->type = bt_find(name); if (!b->type) { From b73d1bc1c11e8a03bb9b420b3548aa238d1a05b8 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 16 Jul 2020 20:16:17 +0200 Subject: [PATCH 21/52] =?UTF-8?q?Bug=202668=20Spruchst=C3=A4rke=20Ritual?= =?UTF-8?q?=20der=20Aufnahme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zauber sollen force benutzen, nicht level. --- res/translations/messages.de.po | 2 +- src/spells.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/res/translations/messages.de.po b/res/translations/messages.de.po index 22c080f98..f410bcd2c 100644 --- a/res/translations/messages.de.po +++ b/res/translations/messages.de.po @@ -2040,7 +2040,7 @@ msgid "analyse_building_fail" msgstr "\"$unit($mage) meint, dass auf $building($building) ein Zauber liegt, konnte aber über den Zauber nichts herausfinden.\"" msgid "spellfail_toomanytargets" -msgstr "\"$unit($unit) in $region($region): '$order($command)' - So viele Persoenen übersteigen die Kräfte des Magiers.\"" +msgstr "\"$unit($unit) in $region($region): '$order($command)' - So viele Personen übersteigen die Kräfte des Magiers.\"" msgid "harvest_effect" msgstr "\"$if($isnull($mage),\"Ein unentdeckter Magier\",$unit($mage)) segnet in einem kurzen Ritual die Felder.\"" diff --git a/src/spells.c b/src/spells.c index 910e3fe26..518819cdd 100644 --- a/src/spells.c +++ b/src/spells.c @@ -3749,7 +3749,7 @@ static int sp_migranten(castorder * co) unit *target; region *r = co_get_region(co); unit *mage = co_get_caster(co); - int cast_level = co->level; + int max_force = (int) co->force; spellparameter *pa = co->par; /* wenn kein Ziel gefunden, Zauber abbrechen */ @@ -3782,7 +3782,7 @@ static int sp_migranten(castorder * co) return 0; } /* maximal Stufe Personen */ - if (target->number > cast_level || target->number > max_spellpoints_depr(r, mage)) { + if (target->number > max_force || target->number > max_spellpoints_depr(r, mage)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_toomanytargets", "")); return 0; From 2a093ba8534af2fbabf9640de15194d955fc2912 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Fri, 17 Jul 2020 18:27:46 +0200 Subject: [PATCH 22/52] make it impossible to create buildings with size 0 --- src/bind_building.c | 10 +++++++--- src/kernel/build.c | 3 +-- src/kernel/building.c | 4 +++- src/kernel/building.h | 2 +- src/laws.test.c | 4 ++-- src/races/races.c | 3 +-- src/spells.c | 11 +++++------ src/tests.c | 3 +-- src/wormhole.c | 6 +++--- 9 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/bind_building.c b/src/bind_building.c index 93576ed3b..d51122b0a 100644 --- a/src/bind_building.c +++ b/src/bind_building.c @@ -183,16 +183,20 @@ static int tolua_building_set_owner(lua_State * L) static int tolua_building_create(lua_State * L) { - region *r = (region *)tolua_tousertype(L, 1, 0); - const char *bname = tolua_tostring(L, 2, 0); + region *r = (region *)tolua_tousertype(L, 1, NULL); + const char *bname = tolua_tostring(L, 2, NULL); + int size = (int)tolua_tonumber(L, 3, 1); if (!r) { log_error("building.create expects a region as argument 1"); } else if (!bname) { log_error("building.create expects a name as argument 2"); + } + else if (size <= 0) { + log_error("building.create expects a size > 0"); } else { const building_type *btype = bt_find(bname); if (btype) { - building *b = new_building(btype, r, default_locale); + building *b = new_building(btype, r, default_locale, size); tolua_pushusertype(L, (void *)b, TOLUA_CAST "building"); return 1; } diff --git a/src/kernel/build.c b/src/kernel/build.c index 3eb9870de..76cbe9f34 100644 --- a/src/kernel/build.c +++ b/src/kernel/build.c @@ -827,8 +827,7 @@ build_building(unit * u, const building_type * btype, int id, int want, order * b->size += built; } else { /* build a new building */ - b = new_building(btype, r, lang); - b->size = built; + b = new_building(btype, r, lang, built); b->type = btype; fset(b, BLD_MAINTAINED); diff --git a/src/kernel/building.c b/src/kernel/building.c index a6167f880..c8c19cc9d 100644 --- a/src/kernel/building.c +++ b/src/kernel/building.c @@ -369,7 +369,7 @@ building *building_create(int id) } building *new_building(const struct building_type * btype, region * r, - const struct locale * lang) + const struct locale * lang, int size) { building **bptr = &r->buildings; int id = newcontainerid(); @@ -377,6 +377,7 @@ building *new_building(const struct building_type * btype, region * r, const char *bname; char buffer[32]; + assert(size > 0); b->type = btype; b->region = r; while (*bptr) @@ -396,6 +397,7 @@ building *new_building(const struct building_type * btype, region * r, assert(bname); snprintf(buffer, sizeof(buffer), "%s %s", bname, itoa36(b->no)); b->name = str_strdup(bname); + b->size = size; return b; } diff --git a/src/kernel/building.h b/src/kernel/building.h index 2471bbfdc..74128a036 100644 --- a/src/kernel/building.h +++ b/src/kernel/building.h @@ -111,7 +111,7 @@ extern "C" { int buildingcapacity(const struct building *b); struct building *building_create(int id); struct building *new_building(const struct building_type *typ, - struct region *r, const struct locale *lang); + struct region *r, const struct locale *lang, int size); int build_building(struct unit *u, const struct building_type *typ, int id, int size, struct order *ord); bool building_finished(const struct building *b); diff --git a/src/laws.test.c b/src/laws.test.c index 45a5f3023..71ac067fc 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -61,7 +61,7 @@ static void test_rename_building(CuTest * tc) test_create_locale(); btype = test_create_buildingtype("castle"); r = test_create_region(0, 0, NULL); - b = new_building(btype, r, default_locale); + b = new_building(btype, r, default_locale, 1); f = test_create_faction(NULL); u = test_create_unit(f, r); u_set_building(u, b); @@ -84,7 +84,7 @@ static void test_rename_building_twice(CuTest * tc) test_create_locale(); btype = test_create_buildingtype("castle"); r = test_create_region(0, 0, NULL); - b = new_building(btype, r, default_locale); + b = new_building(btype, r, default_locale, 1); f = test_create_faction(NULL); u = test_create_unit(f, r); u_set_building(u, b); diff --git a/src/races/races.c b/src/races/races.c index 7c3e65fc4..a782ba0ce 100644 --- a/src/races/races.c +++ b/src/races/races.c @@ -42,8 +42,7 @@ void equip_newunits(struct unit *u) if (u->building == NULL) { const building_type *btype = bt_find("castle"); if (btype != NULL) { - building *b = new_building(btype, r, u->faction->locale); - b->size = 10; + building *b = new_building(btype, r, u->faction->locale, 10); u_set_building(u, b); building_set_owner(u); } diff --git a/src/spells.c b/src/spells.c index 518819cdd..fbd4d71d5 100644 --- a/src/spells.c +++ b/src/spells.c @@ -4415,7 +4415,7 @@ int sp_icastle(castorder * co) const building_type *type; region *r = co_get_region(co); unit *mage = co_get_caster(co); - int cast_level = co->level; + int size, cast_level = co->level; double power = co->force; spellparameter *pa = co->par; const char *bname; @@ -4431,18 +4431,17 @@ int sp_icastle(castorder * co) type = bt_find("castle"); } - b = new_building(bt_illusion, r, mage->faction->locale); - /* Groesse festlegen. */ if (type == bt_illusion) { - b->size = (rng_int() % (int)((power * power) + 1) * 10); + size = (rng_int() % (int)((power * power) + 1) * 10); } else if (type->maxsize > 0) { - b->size = type->maxsize; + size = type->maxsize; } else { - b->size = ((rng_int() % (int)(power)) + 1) * 5; + size = ((rng_int() % (int)(power)) + 1) * 5; } + b = new_building(bt_illusion, r, mage->faction->locale, size); bname = LOC(mage->faction->locale, buildingtype(type, b, 0)); building_setname(b, bname); diff --git a/src/tests.c b/src/tests.c index 727803e2d..699c093d7 100644 --- a/src/tests.c +++ b/src/tests.c @@ -335,8 +335,7 @@ building * test_create_building(region * r, const building_type * btype) bt_castle->flags |= BTF_FORTIFICATION; btype = bt_castle; } - b = new_building(btype, r, default_locale); - b->size = btype->maxsize > 0 ? btype->maxsize : 1; + b = new_building(btype, r, default_locale, (btype->maxsize > 0) ? btype->maxsize : 1); return b; } diff --git a/src/wormhole.c b/src/wormhole.c index 22f2e3d8b..f4815f158 100644 --- a/src/wormhole.c +++ b/src/wormhole.c @@ -116,13 +116,13 @@ static attrib_type at_wormhole = { static void make_wormhole(const building_type * bt_wormhole, region * r1, region * r2) { - building *b1 = new_building(bt_wormhole, r1, default_locale); - building *b2 = new_building(bt_wormhole, r2, default_locale); + int size = bt_wormhole->maxcapacity * bt_wormhole->capacity; + building *b1 = new_building(bt_wormhole, r1, default_locale, size); + building *b2 = new_building(bt_wormhole, r2, default_locale, size); attrib *a1 = a_add(&b1->attribs, a_new(&at_wormhole)); attrib *a2 = a_add(&b2->attribs, a_new(&at_wormhole)); a1->data.v = b2->region; a2->data.v = b1->region; - b1->size = b2->size = bt_wormhole->maxcapacity * bt_wormhole->capacity; ADDMSG(&r1->msgs, msg_message("wormhole_appear", "region", r1)); ADDMSG(&r2->msgs, msg_message("wormhole_appear", "region", r2)); } From 590daf2aa5b0dc743fda83720d3efeab40ae89e4 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Fri, 17 Jul 2020 18:46:14 +0200 Subject: [PATCH 23/52] fake wormhole in test had negative size --- .editorconfig | 1 + src/wormhole.test.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.editorconfig b/.editorconfig index fafe79d54..b2e1ab85b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,6 +15,7 @@ indent_size = 4 [*.{xml,json}] charset = utf-8 +indent_size = 2 # Tab indentation (no size specified) [Makefile] diff --git a/src/wormhole.test.c b/src/wormhole.test.c index 4b6fe2d3c..6d531f214 100644 --- a/src/wormhole.test.c +++ b/src/wormhole.test.c @@ -37,6 +37,8 @@ static void test_make_wormholes(CuTest *tc) { test_setup(); setup_wormholes(); btype = test_create_buildingtype("wormhole"); + btype->maxsize = 4; + btype->maxcapacity = 4; match[0] = r1 = test_create_plain(0, 0); match[1] = r2 = test_create_plain(1, 0); make_wormholes(match, 2, btype); From df134e9ad33f5a96da20997873b22f5ddb64eb0e Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Fri, 17 Jul 2020 21:20:55 +0200 Subject: [PATCH 24/52] Allow alternative names for some spells. --- conf/e2/locales.json | 41 +++++++++++++---- conf/e3/locales.json | 18 ++++---- scripts/tests/e2/e2features.lua | 37 +++++++++++++++ scripts/tests/e3/rules.lua | 2 +- src/battle.c | 4 +- src/creport.c | 19 ++++---- src/eressea.c | 2 + src/jsonconf.c | 39 ++++++++++++++++ src/kernel/region.c | 1 - src/magic.c | 27 ++++++++--- src/magic.h | 4 +- src/magic.test.c | 8 ++-- src/report.c | 4 +- src/reports.c | 6 +-- src/tests.c | 6 ++- src/util/CMakeLists.txt | 2 + src/util/aliases.c | 79 +++++++++++++++++++++++++++++++++ src/util/aliases.h | 23 ++++++++++ 18 files changed, 272 insertions(+), 50 deletions(-) create mode 100644 src/util/aliases.c create mode 100644 src/util/aliases.h diff --git a/conf/e2/locales.json b/conf/e2/locales.json index 3376cfedc..6532cae3b 100644 --- a/conf/e2/locales.json +++ b/conf/e2/locales.json @@ -1,11 +1,34 @@ { - "include": [ - "config://res/translations/strings.de.po", - "config://res/translations/strings-e2.de.po", - "config://res/translations/strings.en.po", - "config://res/translations/strings-e2.en.po", - "config://res/translations/messages.de.po", - "config://res/translations/messages.en.po", - "config://res/core/messages.xml" - ] + "include": [ + "config://res/translations/strings.de.po", + "config://res/translations/strings-e2.de.po", + "config://res/translations/strings.en.po", + "config://res/translations/strings-e2.en.po", + "config://res/translations/messages.de.po", + "config://res/translations/messages.en.po", + "config://res/core/messages.xml" + ], + "aliases": { + "de": { + "spell::earthquake": [ + "Erdelementar", + "Beschwörung eines Erdelemntares" + ], + "spell::goodwinds": [ + "Wasserelementar", + "Beschwöre einen Wasserelementar" + ], + "spell::stormwinds": [ + "Sturmelementar", + "Beschwörung eines Sturmelementares" + ], + "spell::summonfireelemental": [ + "Hitzeelementar", + "Beschwörung eines Hitzeelementares" + ] + }, + "en": { + "spell::migration": "Rite of Acceptance" + } + } } diff --git a/conf/e3/locales.json b/conf/e3/locales.json index 27e6f93d8..2b908abc4 100644 --- a/conf/e3/locales.json +++ b/conf/e3/locales.json @@ -1,11 +1,11 @@ { - "include": [ - "config://res/translations/strings.de.po", - "config://res/translations/strings-e3.de.po", - "config://res/translations/messages.de.po", - "config://res/translations/strings.en.po", - "config://res/translations/strings-e3.en.po", - "config://res/translations/messages.en.po", - "config://res/core/messages.xml" - ] + "include": [ + "config://res/translations/strings.de.po", + "config://res/translations/strings-e3.de.po", + "config://res/translations/messages.de.po", + "config://res/translations/strings.en.po", + "config://res/translations/strings-e3.en.po", + "config://res/translations/messages.en.po", + "config://res/core/messages.xml" + ] } diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua index f0ee62414..356e496e7 100644 --- a/scripts/tests/e2/e2features.lua +++ b/scripts/tests/e2/e2features.lua @@ -582,3 +582,40 @@ function test_buy_sell() assert_equal(4, u:get_item(item)) assert_not_equal(0, u:get_item('money')) end + +function test_seacast() + local r = region.create(0,0, "plain") + for i = 1,10 do + -- this prevents storms (only high seas have storms) + region.create(i, 1, "plain") + end + for i = 1,10 do + region.create(i, 0, "ocean") + end + local f = faction.create("human", "noreply@eressea.de", "de") + local s1 = ship.create(r, "boat") + local u1 = unit.create(f, r, 2) + u1:set_skill("sailing", 3) + u1:add_item("money", 1000) + u1.ship = s1 + local u2 = unit.create(f, r, 1) + u2.race = "elf" + u2:set_skill("magic", 6) + u2.magic = "gwyrrd" + u2.aura = 60 + u2.ship = s1 + u2:add_spell("stormwinds") + u2:clear_orders() + u2:add_order("Zaubere stufe 2 'Sturmelementar' " .. itoa36(s1.id)) + u1:clear_orders() + u1:add_order("NACH O O O O") + process_orders() + assert_equal(4, u2.region.x) + + u2:clear_orders() + u2:add_order("Zaubere stufe 2 'Beschwörung eines Sturmelementares' " .. itoa36(s1.id)) + u1:clear_orders() + u1:add_order("NACH O O O O") + process_orders() + assert_equal(8, u2.region.x) +end diff --git a/scripts/tests/e3/rules.lua b/scripts/tests/e3/rules.lua index 89a6d822a..8983d4584 100644 --- a/scripts/tests/e3/rules.lua +++ b/scripts/tests/e3/rules.lua @@ -168,7 +168,7 @@ function test_no_teach() -- TODO: assert something (reflecting skills sucks!) end -function test_seecast() +function test_seacast() local r = region.create(0,0, "plain") for i = 1,10 do -- this prevents storms (only high seas have storms) diff --git a/src/battle.c b/src/battle.c index fe61c8adf..4cd8e41cd 100644 --- a/src/battle.c +++ b/src/battle.c @@ -1738,7 +1738,7 @@ void do_combatmagic(battle * b, combatmagic_t was) if (sp == NULL) continue; - ord = create_order(K_CAST, lang, "'%s'", spell_name(sp, lang)); + ord = create_order(K_CAST, lang, "'%s'", spell_name(mkname_spell(sp), lang)); if (!cancast(mage, sp, 1, 1, ord)) { free_order(ord); continue; @@ -1820,7 +1820,7 @@ static void do_combatspell(troop at) fi->magic = 0; /* Hat keinen Kampfzauber, kaempft nichtmagisch weiter */ return; } - ord = create_order(K_CAST, lang, "'%s'", spell_name(sp, lang)); + ord = create_order(K_CAST, lang, "'%s'", spell_name(mkname_spell(sp), lang)); if (!cancast(mage, sp, 1, 1, ord)) { fi->magic = 0; /* Kann nicht mehr Zaubern, kaempft nichtmagisch weiter */ return; diff --git a/src/creport.c b/src/creport.c index 237a6eff7..eaa19e186 100644 --- a/src/creport.c +++ b/src/creport.c @@ -476,10 +476,12 @@ static int cr_spell(variant var, char *buffer, const void *userdata) { const faction *report = (const faction *)userdata; spell *sp = (spell *)var.v; - if (sp != NULL) - sprintf(buffer, "\"%s\"", spell_name(sp, report->locale)); - else + if (sp != NULL) { + sprintf(buffer, "\"%s\"", spell_name(mkname_spell(sp), report->locale)); + } + else { strcpy(buffer, "\"\""); + } return 0; } @@ -708,7 +710,8 @@ static void cr_output_spells(stream *out, const unit * u, int maxlevel) spellbook_entry * sbe = (spellbook_entry *)selist_get(ql, qi); if (sbe->level <= maxlevel) { const spell *sp = spellref_get(&sbe->spref); - const char *name = translate(mkname("spell", sp->sname), spell_name(sp, f->locale)); + const char * spname = mkname("spell", sp->sname); + const char *name = translate(spname, spell_name(spname, f->locale)); if (!header) { stream_printf(out, "SPRUECHE\n"); header = 1; @@ -923,12 +926,12 @@ void cr_output_unit(stream *out, const faction * f, int level; const spell *sp = mage_get_combatspell(mage, i, &level); if (sp) { - const char *name; + const char *name = mkname_spell(sp); if (level > maxlevel) { level = maxlevel; } stream_printf(out, "KAMPFZAUBER %d\n", i); - name = translate(mkname("spell", sp->sname), spell_name(sp, lang)); + name = translate(name, spell_name(name, lang)); stream_printf(out, "\"%s\";name\n", name); stream_printf(out, "%d;level\n", level); } @@ -1057,8 +1060,8 @@ static void cr_find_address(FILE * F, const faction * uf, selist * addresses) static void cr_reportspell(FILE * F, const spell * sp, int level, const struct locale *lang) { int k; - const char *name = - translate(mkname("spell", sp->sname), spell_name(sp, lang)); + const char *spname = mkname_spell(sp); + const char *name = translate(spname, spell_name(spname, lang)); fprintf(F, "ZAUBER %d\n", str_hash(sp->sname)); fprintf(F, "\"%s\";name\n", name); diff --git a/src/eressea.c b/src/eressea.c index 6efb1cb0b..f40a7f994 100644 --- a/src/eressea.c +++ b/src/eressea.c @@ -10,6 +10,7 @@ #include "kernel/faction.h" #include "kernel/item.h" +#include "util/aliases.h" #include "util/functions.h" #include "util/language.h" #include "util/log.h" @@ -53,6 +54,7 @@ void game_done(void) free_config(); free_locales(); #endif + free_aliases(); free_prefixes(); free_special_directions(); kernel_done(); diff --git a/src/jsonconf.c b/src/jsonconf.c index 8761f36be..27a018b17 100644 --- a/src/jsonconf.c +++ b/src/jsonconf.c @@ -18,6 +18,7 @@ /* util includes */ #include "kernel/attrib.h" +#include "util/aliases.h" #include "util/crmessage.h" #include "util/functions.h" #include "util/keyword.h" @@ -696,6 +697,41 @@ static void json_strings(cJSON *json) { } } +static void json_add_aliases(cJSON *json, const struct locale *lang) { + cJSON *child; + str_aliases *aliases = get_aliases(lang); + for (child = json->child; child; child = child->next) { + if (child->type == cJSON_String) { + alias_add(aliases, child->string, child->valuestring); + } + else if (child->type == cJSON_Array) { + cJSON *entry; + for (entry = child->child; entry; entry = entry->next) { + if (entry->type == cJSON_String) { + alias_add(aliases, child->string, entry->valuestring); + } + } + } + } +} + +static void json_aliases(cJSON *json) { + cJSON *child; + if (json->type != cJSON_Object) { + log_error("aliases is not a json array: %d", json->type); + return; + } + for (child = json->child; child; child = child->next) { + if (child->type == cJSON_Object) { + struct locale *lang = get_locale(child->string); + json_add_aliases(child, lang); + } + else { + log_error("strings for locale `%s` are not a json object: %d", child->string, child->type); + } + } +} + static void json_direction(cJSON *json, struct locale *lang) { cJSON *child; if (json->type != cJSON_Object) { @@ -1108,6 +1144,9 @@ void json_config(cJSON *json) { else if (strcmp(child->string, "strings") == 0) { json_strings(child); } + else if (strcmp(child->string, "aliases") == 0) { + json_aliases(child); + } else if (strcmp(child->string, "calendar") == 0) { json_calendar(child); } diff --git a/src/kernel/region.c b/src/kernel/region.c index b3bc9a0e9..07916fda0 100644 --- a/src/kernel/region.c +++ b/src/kernel/region.c @@ -1396,7 +1396,6 @@ void region_set_owner(struct region *r, struct faction *owner, int turn) faction *update_owners(region * r) { faction *f = NULL; - assert(rule_region_owners()); if (r->land) { building *bowner = largestbuilding(r, cmp_current_owner, false); building *blargest = largestbuilding(r, cmp_taxes, false); diff --git a/src/magic.c b/src/magic.c index 5300101d3..578557b23 100644 --- a/src/magic.c +++ b/src/magic.c @@ -22,12 +22,15 @@ #include #include +#include #include #include #include #include #include +#include #include +#include #include #include #include @@ -44,10 +47,8 @@ #include /* util includes */ -#include +#include #include -#include -#include #include #include #include @@ -2927,10 +2928,14 @@ const char *spell_info(const spell * sp, const struct locale *lang) return LOC(lang, mkname("spellinfo", sp->sname)); } -/* TODO: should take the name, not the spell (spellref optimizations) */ -const char *spell_name(const spell * sp, const struct locale *lang) +const char *mkname_spell(const spell *sp) { - return LOC(lang, mkname("spell", sp->sname)); + return mkname("spell", sp->sname); +} + +const char *spell_name(const char *spname, const struct locale *lang) +{ + return LOC(lang, spname); } const char *curse_name(const curse_type * ctype, const struct locale *lang) @@ -2948,14 +2953,22 @@ static void select_spellbook(void **tokens, spellbook *sb, const struct locale * for (qi = 0, ql = sb->spells; ql; selist_advance(&ql, &qi, 1)) { spellbook_entry *sbe = (spellbook_entry *)selist_get(ql, qi); const spell *sp = spellref_get(&sbe->spref); - const char *n = spell_name(sp, lang); + const char *spname = mkname("spell", sp->sname); + const char *n = spell_name(spname, lang); if (!n) { log_error("no translation in locale %s for spell %s\n", locale_name(lang), sp->sname); } else { variant token; + const str_alias *aliases = alias_get(lang, spname); token.v = (void *)sp; addtoken((struct tnode **)tokens, n, token); + if (aliases) { + int i; + for (i = 0; i != MAXSTRINGS && aliases->strings[i]; ++i) { + addtoken((struct tnode **)tokens, aliases->strings[i], token); + } + } } } } diff --git a/src/magic.h b/src/magic.h index c373d2ecf..068ab223e 100644 --- a/src/magic.h +++ b/src/magic.h @@ -315,10 +315,10 @@ extern "C" { void fix_fam_spells(struct unit *u); void fix_fam_migrant(struct unit *u); + const char *mkname_spell(const struct spell *sp); + const char *spell_name(const char *spname, const struct locale *lang); const char *spell_info(const struct spell *sp, const struct locale *lang); - const char *spell_name(const struct spell *sp, - const struct locale *lang); const char *curse_name(const struct curse_type *ctype, const struct locale *lang); diff --git a/src/magic.test.c b/src/magic.test.c index 068efdfa4..9c4b428a8 100644 --- a/src/magic.test.c +++ b/src/magic.test.c @@ -189,7 +189,7 @@ void test_getspell_unit(CuTest * tc) lang = test_create_locale(); sp = create_spell("testspell"); - locale_setstring(lang, mkname("spell", sp->sname), "Herp-a-derp"); + locale_setstring(lang, mkname_spell(sp), "Herp-a-derp"); CuAssertPtrEquals(tc, NULL, unit_getspell(u, "Herp-a-derp", lang)); @@ -248,7 +248,7 @@ void test_getspell_school(CuTest * tc) lang = test_create_locale(); sp = create_spell("testspell"); - locale_setstring(lang, mkname("spell", sp->sname), "Herp-a-derp"); + locale_setstring(lang, mkname_spell(sp), "Herp-a-derp"); CuAssertPtrEquals(tc, NULL, unit_getspell(u, "Herp-a-derp", lang)); @@ -402,8 +402,8 @@ void test_multi_cast(CuTest *tc) { CuAssertPtrEquals(tc, sp, find_spell("fireball")); lang = test_create_locale(); - locale_setstring(lang, mkname("spell", sp->sname), "Feuerball"); - CuAssertStrEquals(tc, "Feuerball", spell_name(sp, lang)); + locale_setstring(lang, mkname_spell(sp), "Feuerball"); + CuAssertStrEquals(tc, "Feuerball", spell_name(mkname_spell(sp), lang)); u = test_create_unit(test_create_faction(NULL), test_create_plain(0, 0)); set_level(u, SK_MAGIC, 10); diff --git a/src/report.c b/src/report.c index b8b65a885..ab25b79b2 100644 --- a/src/report.c +++ b/src/report.c @@ -256,7 +256,7 @@ void nr_spell_syntax(char *buf, size_t size, spellbook_entry * sbe, const struct sbs_strcat(&sbs, " n]"); } - spname = spell_name(sp, lang); + spname = spell_name(mkname_spell(sp), lang); if (strchr(spname, ' ') != NULL) { /* contains spaces, needs quotes */ sbs_strcat(&sbs, " '"); @@ -422,7 +422,7 @@ void nr_spell(struct stream *out, spellbook_entry * sbe, const struct locale *la sbstring sbs; newline(out); - centre(out, spell_name(sp, lang), true); + centre(out, spell_name(mkname_spell(sp), lang), true); newline(out); paragraph(out, LOC(lang, "nr_spell_description"), 0, 0, 0); paragraph(out, spell_info(sp, lang), 2, 0, 0); diff --git a/src/reports.c b/src/reports.c index 2553920d1..4c3242df8 100644 --- a/src/reports.c +++ b/src/reports.c @@ -808,7 +808,7 @@ void bufunit(const faction * f, const unit * u, const faction *fv, sbs_strcat(sbp, ", "); } /* TODO: no need to deref the spellref here (spref->name is good) */ - sbs_strcat(sbp, spell_name(sp, lang)); + sbs_strcat(sbp, spell_name(mkname_spell(sp), lang)); } } @@ -830,7 +830,7 @@ void bufunit(const faction * f, const unit * u, const faction *fv, sp = get_combatspell(u, i); if (sp) { int sl = get_combatspelllevel(u, i); - sbs_strcat(sbp, spell_name(sp, lang)); + sbs_strcat(sbp, spell_name(mkname_spell(sp), lang)); if (sl > 0) { sbs_printf(sbp, "(%d)", sl); } @@ -1877,7 +1877,7 @@ static void eval_spell(struct opstack **stack, const void *userdata) 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"); + sp ? spell_name(mkname_spell(sp), f->locale) : LOC(f->locale, "an_unknown_spell"); variant var; assert(c || !"spell without description!"); diff --git a/src/tests.c b/src/tests.c index 699c093d7..9a6ac156d 100644 --- a/src/tests.c +++ b/src/tests.c @@ -27,13 +27,14 @@ #include #include +#include #include -#include "util/keyword.h" +#include #include #include #include #include -#include "util/param.h" +#include #include #include @@ -249,6 +250,7 @@ static void test_reset(void) { free_shiptypes(); free_races(); free_spellbooks(); + free_aliases(); free_prefixes(); mt_clear(); diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index eeb16df1a..5a0b57e6b 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -3,6 +3,7 @@ project(util C) add_subdirectory (crypto) SET(_TEST_FILES +# aliases.test.c base36.test.c # crmessage.test.c # dice.test.c @@ -31,6 +32,7 @@ variant.test.c ) SET(_FILES +aliases.c base36.c crmessage.c dice.c diff --git a/src/util/aliases.c b/src/util/aliases.c new file mode 100644 index 000000000..bec4dabcd --- /dev/null +++ b/src/util/aliases.c @@ -0,0 +1,79 @@ +#include "aliases.h" + +#include + +#include +#include +#include + +static struct str_aliases *g_aliases; + +void free_aliases(void) +{ + while (g_aliases) { + int i; + struct str_aliases * anext = g_aliases->next; + for (i = 0; i != MAXALIASES && g_aliases->alternatives[i].key; ++i) { + int j; + struct str_alias *alias = g_aliases->alternatives + i; + free(alias->key); + for (j = 0; j != MAXSTRINGS && alias->strings[j]; ++j) { + free(alias->strings[j]); + } + } + free(g_aliases); + g_aliases = anext; + } +} + +str_aliases *get_aliases(const struct locale *lang) { + str_aliases *aliases = g_aliases; + while (aliases && aliases->lang != lang) { + aliases = aliases->next; + } + if (aliases == NULL) { + aliases = calloc(1, sizeof(struct str_aliases)); + aliases->next = g_aliases; + aliases->lang = lang; + g_aliases = aliases; + } + return aliases; +} + +void alias_add(struct str_aliases *aliases, const char *key, const char *text) { + int i, j; + struct str_alias *alias; + + for (i = 0; i != MAXALIASES && aliases->alternatives[i].key; ++i) { + if (0 == strcmp(key, aliases->alternatives[i].key)) { + break; + } + } + assert(i != MAXALIASES); + alias = aliases->alternatives + i; + alias->key = str_strdup(key); + for (j = 0; j != MAXSTRINGS; ++j) { + if (alias->strings[j] == NULL) { + alias->strings[j] = str_strdup(text); + break; + } + } + assert(j != MAXSTRINGS); +} + +const struct str_alias *alias_get(const struct locale *lang, const char *key) +{ + str_aliases *aliases = g_aliases; + while (aliases && aliases->lang != lang) { + aliases = aliases->next; + } + if (aliases) { + int i; + for (i = 0; i != MAXALIASES && aliases->alternatives[i].key; ++i) { + if (0 == strcmp(key, aliases->alternatives[i].key)) { + return aliases->alternatives + i; + } + } + } + return NULL; +} diff --git a/src/util/aliases.h b/src/util/aliases.h new file mode 100644 index 000000000..634aeab3d --- /dev/null +++ b/src/util/aliases.h @@ -0,0 +1,23 @@ +#pragma once + +struct locale; + +#define MAXSTRINGS 2 +#define MAXALIASES 16 + +typedef struct str_alias { + char *key; + char *strings[MAXSTRINGS]; +} str_alias; + +typedef struct str_aliases { + struct str_aliases *next; + const struct locale *lang; + str_alias alternatives[MAXALIASES]; +} str_aliases; + +void free_aliases(void); + +str_aliases *get_aliases(const struct locale *lang); +void alias_add(str_aliases *aliases, const char *key, const char *text); +const str_alias *alias_get(const struct locale *lang, const char *key); From 08a4a80d376c12ce4d50d0ec99fe82a3dd26a77e Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 18 Jul 2020 19:49:30 +0200 Subject: [PATCH 25/52] Finish spell renaming, remove wdw leftovers. Fixing the E3 rules.lua test We no longer need pyramid, scarab, sphinx, pharao. --- conf/e2/locales.json | 12 +-- res/core/messages.xml | 17 ---- res/translations/messages.de.po | 6 -- res/translations/messages.en.po | 6 -- res/translations/strings.de.po | 150 ++------------------------------ res/translations/strings.en.po | 103 +--------------------- scripts/tests/e3/rules.lua | 4 +- 7 files changed, 15 insertions(+), 283 deletions(-) diff --git a/conf/e2/locales.json b/conf/e2/locales.json index 6532cae3b..02967d134 100644 --- a/conf/e2/locales.json +++ b/conf/e2/locales.json @@ -11,24 +11,24 @@ "aliases": { "de": { "spell::earthquake": [ - "Erdelementar", - "Beschwörung eines Erdelemntares" + "Beschwöre einen Erdelementar", + "Beschwörung eines Erdelementares" ], "spell::goodwinds": [ - "Wasserelementar", + "Beschwörung eines Wasserelementares", "Beschwöre einen Wasserelementar" ], "spell::stormwinds": [ - "Sturmelementar", + "Beschwöre einen Sturmelementar", "Beschwörung eines Sturmelementares" ], "spell::summonfireelemental": [ - "Hitzeelementar", + "Beschwöre einen Hitzeelementar", "Beschwörung eines Hitzeelementares" ] }, "en": { - "spell::migration": "Rite of Acceptance" + "spell::migration": "Rit of Acceptance" } } } diff --git a/res/core/messages.xml b/res/core/messages.xml index 13383cd0a..4886ea88d 100644 --- a/res/core/messages.xml +++ b/res/core/messages.xml @@ -5459,13 +5459,6 @@ - - - - - - - @@ -5485,16 +5478,6 @@ - - - - - - - - - - diff --git a/res/translations/messages.de.po b/res/translations/messages.de.po index f410bcd2c..7db486687 100644 --- a/res/translations/messages.de.po +++ b/res/translations/messages.de.po @@ -1637,9 +1637,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - $unit($unit) ge msgid "curseinfo::sparkle_11" msgstr "\"Vogelzwitschern begleitet $unit($unit) auf all seinen Wegen. ($int36($id))\"" -msgid "wdw_pyramidspell_notfound" -msgstr "\"$unit($unit) in $region($region): '$order($command)' - In dieser Region können keine Pyramiden gebaut werden. Die nächste Pyramidenregion ist zwischen $int($mindist) und $int($maxdist) Regionen entfernt.\"" - msgid "sailforbidden" msgstr "\"Die Mannschaft der $ship($ship) weigert sich, nach $region($region) zu reisen.\"" @@ -1862,9 +1859,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Wege zwisch msgid "familiar_toofar" msgstr "\"$unit($unit) in $region($region): '$order($command)' - $unit($mage) kann nicht genug Energie aufbringen, um diesen Spruch durch $unit($unit) zu wirken.\"" -msgid "wdw_pyramidspell_found" -msgstr "\"$unit($unit) in $region($region): '$order($command)' - In dieser Regione können Pyramiden gebaut werden.\"" - msgid "deorcified" msgstr "\"Langsam kehren andere Völker nach $region($region) zurück.\"" diff --git a/res/translations/messages.en.po b/res/translations/messages.en.po index c65cb788d..b9a7ff0d9 100644 --- a/res/translations/messages.en.po +++ b/res/translations/messages.en.po @@ -1637,9 +1637,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - $unit($unit) ma msgid "curseinfo::sparkle_11" msgstr "\"Bird songs follow $unit($unit) on all his travels. ($int36($id))\"" -msgid "wdw_pyramidspell_notfound" -msgstr "\"$unit($unit) in $region($region): '$order($command)' - No pyramids may be build in this region. The closest region to build a pyramid in is between $int($mindist) and $int($maxdist) regions away.\"" - msgid "sailforbidden" msgstr "\"The crew of the $ship($ship) refuses to travel to $region($region).\"" @@ -1862,9 +1859,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - The paths to th msgid "familiar_toofar" msgstr "\"$unit($unit) in $region($region): '$order($command)' - $unit($mage) cannot raise enough energy to channel the spell through $unit($unit).\"" -msgid "wdw_pyramidspell_found" -msgstr "\"$unit($unit) in $region($region): '$order($command)' - Pyramids may be build in this region.\"" - msgid "deorcified" msgstr "\"Little by little, people return to $region($region).\"" diff --git a/res/translations/strings.de.po b/res/translations/strings.de.po index ba16c31e9..4a55e8ab7 100644 --- a/res/translations/strings.de.po +++ b/res/translations/strings.de.po @@ -106,28 +106,16 @@ msgctxt "keyword" msgid "entertain" msgstr "UNTERHALTE" -msgid "spinx00" -msgstr "Das Schiff des Elfen hat ein rotes Segel" - msgid "greatbow_p" msgstr "Elfenbögen" -msgid "spinx01" -msgstr "Der Zwerg hat eine Nuss dabei" - msgid "person" msgstr "Person" -msgid "spinx02" -msgstr "Die Katze führt eine Hellebarde" - msgctxt "spell" msgid "eternal_walls" msgstr "Mauern der Ewigkeit" -msgid "spinx03" -msgstr "Das Schiff mit dem grünen Segel liegt links neben dem mit einem weissen Segel" - msgid "JEDEM" msgstr "JEDEM" @@ -135,31 +123,9 @@ msgctxt "skill" msgid "sailing" msgstr "Segeln" -msgid "spinx04" -msgstr "Auf dem Schiff mit grünen Segeln kam der Speerkämpfer" - msgid "h16_p" msgstr "Spaltwachse" -msgid "spinx05" -msgstr "Der Krieger mit dem Kreis im Wappen hat einen Keks" - -msgid "spinx06" -msgstr "Der Krieger des mittleren Schiffs hat ein Schwert" - -msgid "fortress_generic" -msgstr "Burg" - -msgid "spinx07" -msgstr "Auf dem gelben Segel prankt ein Kreuz als Wappen" - -msgctxt "race" -msgid "undeadpharaoh_d" -msgstr "Untoten Pharao" - -msgid "spinx08" -msgstr "Der Mensch kam mit dem ersten Schiff" - msgid "TRAENKE" msgstr "TRÄNKE" @@ -201,14 +167,6 @@ msgctxt "iteminfo" msgid "wente_dress" msgstr "Hach! Sieht der Mann beeindruckend aus in diesem Frack! Und so ordentlich! Und so ernst! Und so beeindruckend! Es fällt ein wenig schwer, sich auf den Bräutigam zu konzentrieren, weil das Brautkleid noch daneben strahlt, aber der Anzug des Bräutigams ist auf jeden Fall so, wie er sein soll und sieht toll aus und sehr geschmackvoll." -msgctxt "race" -msgid "apophis_d" -msgstr "Apophis" - -msgctxt "race" -msgid "undeadpharaoh_p" -msgstr "Untoter Pharaonen" - msgctxt "iteminfo" msgid "magicbag" msgstr "Dieser Beutel umschließt eine kleine Dimensionsfalte, in der bis zu 200 Gewichtseinheiten transportiert werden können, ohne dass sie auf das Traggewicht angerechnet werden. Pferde und andere Lebewesen sowie besonders sperrige Dinge (Wagen und Katapulte) können nicht in dem Beutel transportiert werden. Auch ist es nicht möglich, einen Zauberbeutel in einem anderen zu transportieren. Der Beutel selber wiegt 1 GE." @@ -239,10 +197,6 @@ msgctxt "calendar" msgid "age_3" msgstr "des dritten Zeitalters" -msgctxt "race" -msgid "undeadpharaoh_x" -msgstr "Untote Pharaonen" - msgctxt "race" msgid "youngdragon_d" msgstr "Jungdrachen" @@ -262,17 +216,10 @@ msgctxt "spellinfo" msgid "auraleak" msgstr "Der Schwarzmagier kann mit diesem dunklen Ritual einen Riss in das Gefüge der Magie bewirken, der alle magische Kraft aus der Region reißen wird. Alle magisch begabten in der Region werden einen Großteil ihrer Aura verlieren." -msgctxt "race" -msgid "apophis_p" -msgstr "Apophis" - msgctxt "spell" msgid "calm_monster" msgstr "Monster friedlich stimmen" -msgid "spinx10" -msgstr "Das Schiff des Kriegers, der ein Apfel hat, liegt neben dem, der ein Kreuz als Wappen hat" - msgctxt "spellinfo" msgid "sound_out" msgstr "Erliegt die Einheit dem Zauber, so wird sie dem Magier alles erzählen, was sie über die gefragte Region weiß. Ist in der Region niemand ihrer Partei, so weiß sie nichts zu berichten. Auch kann sie nur das erzählen, was sie selber sehen könnte." @@ -281,26 +228,10 @@ msgctxt "spell" msgid "readmind" msgstr "Traumdeuten" -msgid "spinx11" -msgstr "Der Krieger mit dem Turm im Wappen trägt eine Axt" - -msgid "spinx12" -msgstr "Das Schiff des Menschen liegt neben dem blauen Schiff" - -msgctxt "race" -msgid "apophis_x" -msgstr "Apophis" - -msgid "spinx13" -msgstr "Das Insekt trägt einen Baum als Wappen" - msgctxt "race" msgid "youngdragon_p" msgstr "Jungdrachen" -msgid "spinx14" -msgstr "Das Schiff mit dem Stern im Wappen liegt neben dem des Kriegers, der einen Zweihänder führt" - msgid "nr_options" msgstr "Optionen" @@ -705,10 +636,6 @@ msgstr "Dunkle" msgid "zombie_prefix_8" msgstr "Fürchterliche" -msgctxt "race" -msgid "redscarab" -msgstr "roter Scarabäus" - msgctxt "race" msgid "wolf_d" msgstr "Wölfen" @@ -893,10 +820,6 @@ msgctxt "race" msgid "shadowmaster_d" msgstr "Schattenmeistern" -msgctxt "race" -msgid "redscarab_d" -msgstr "roten Scarabäen" - msgid "aurapotion50_p" msgstr "Auratränke" @@ -919,9 +842,6 @@ msgctxt "spellinfo" msgid "disturbingdreams" msgstr "Dieser Zauber führt in der betroffenen Region für einige Wochen zu Schlaflosigkeit und Unruhe. Den Betroffenen fällt das Lernen deutlich schwerer." -msgid "wdw_pyramid" -msgstr "Pyramide" - msgctxt "race" msgid "shadowmaster_p" msgstr "Schattenmeister" @@ -932,10 +852,6 @@ msgstr "unbewaffnet" msgid "plain" msgstr "Ebene" -msgctxt "race" -msgid "redscarab_p" -msgstr "rote Scarabäen" - msgctxt "race" msgid "seaserpent_p" msgstr "Seeschlangen" @@ -968,10 +884,6 @@ msgstr "Adamantium" msgid "seashell" msgstr "Muschel" -msgctxt "race" -msgid "redscarab_x" -msgstr "rote Scarabäen" - msgid "adamantiumplate" msgstr "Adamantiumrüstung" @@ -1009,10 +921,6 @@ msgstr "Ritual der Aufnahme" msgid "SCHIFF" msgstr "SCHIFF" -msgctxt "race" -msgid "littlescarab" -msgstr "kleiner Scarabäus" - msgctxt "iteminfo" msgid "nut" msgstr "Nuß, im umgangssprachlichen Sinne alle trockenen, hartschaligen Früchte oder Samen, die eine Schale besitzen, die sich leicht vom inneren, eßbaren Kern entfernen läßt. In der botanischen Terminologie beschränkt sich die Bezeichnung Nuß auf eine einsamige Frucht, die aus einem Fruchtknoten (Ovarium) entstanden ist, dessen äußere Wände sich verholzt haben und der sich nicht öffnet, um seinen Samen zu entlassen. Solche echten Nüsse können eßbar, aber auch ungenießbar sein. Bekannte Beispiele sind Eicheln, Bucheckern, Kastanien und Haselnüsse. Beispiele für Früchte oder Samen, die vom Volksmund fälschlich als Nüsse bezeichnet werden, sind Mandeln und Walnüsse: Im botanischen Sinne sind dies Steinfrüchte, denen die fleischige äußere Schale entfernt wurde. Andere Beispiele für unechte Nüsse sind Erdnüsse - in Hülsen eingeschlossene Samen - sowie Roßkastanien und Paranüsse, bei denen es sich um von Kapseln umhüllte Samen handelt." @@ -1267,7 +1175,7 @@ msgstr "Dieser Zauber bewirkt eine schwere Störung des Astralraums. Innerhalb e msgctxt "spell" msgid "earthquake" -msgstr "Beschwöre einen Erdelementar" +msgstr "Erdelementar" msgctxt "spell" msgid "raise_mob" @@ -1646,10 +1554,6 @@ msgstr "Adamantiumrüstungen" msgid "log" msgstr "Holz" -msgctxt "race" -msgid "bluescarab" -msgstr "blauer Scarabäus" - msgctxt "school" msgid "draig" msgstr "Draig" @@ -1685,10 +1589,6 @@ msgctxt "spell" msgid "mallorntreegrow" msgstr "Segne Mallornstecken" -msgctxt "race" -msgid "littlescarab_d" -msgstr "kleinen Scarabäen" - msgctxt "keyword" msgid "hide" msgstr "TARNE" @@ -1708,10 +1608,6 @@ msgctxt "damage" msgid "plusstrong" msgstr "sehr stark" -msgctxt "race" -msgid "littlescarab_p" -msgstr "kleine Scarabäen" - msgid "an_unknown_ship" msgstr "ein unbekanntes Schiff" @@ -1746,7 +1642,7 @@ msgstr "DEBUG" msgctxt "spell" msgid "goodwinds" -msgstr "Beschwörung eines Wasserelementares" +msgstr "Wasserelementar" msgid "GEBAEUDE" msgstr "GEBÄUDE" @@ -1758,10 +1654,6 @@ msgctxt "spellinfo" msgid "rustweapon" msgstr "Mit diesem Ritual wird eine dunkle Gewitterfront beschworen, die sich unheilverkündend über der Region auftürmt. Der magische Regen wird alles Erz rosten lassen. Eisenwaffen und Rüstungen werden schartig und rostig. Die Zerstörungskraft des Regens ist von der investierten Kraft des Magiers abhängig. Für jede Stufe können bis zu 10 Eisenwaffen betroffen werden. Ein Ring der Macht verstärkt die Wirkung wie eine zusätzliche Stufe." -msgctxt "race" -msgid "littlescarab_x" -msgstr "kleine Scarabäen" - msgid "rop" msgstr "Ring der Macht" @@ -2282,10 +2174,6 @@ msgctxt "spell" msgid "wdwpyramid_illaun" msgstr "Traum von den Göttern" -msgctxt "race" -msgid "bluescarab_d" -msgstr "blauen Scarabäen" - msgctxt "race" msgid "snotling" msgstr "Snotling" @@ -2317,10 +2205,6 @@ msgctxt "prefix" msgid "Klein" msgstr "Klein" -msgctxt "race" -msgid "bluescarab_p" -msgstr "blaue Scarabäen" - msgid "citadel" msgstr "Zitadelle" @@ -2344,10 +2228,6 @@ msgstr "Wyrme" msgid "viele" msgstr "viele" -msgctxt "race" -msgid "bluescarab_x" -msgstr "blaue Scarabäen" - msgctxt "spell" msgid "wdwpyramid_draig" msgstr "Göttliche Macht" @@ -2947,7 +2827,7 @@ msgstr "aus dem Dunkel" msgctxt "spell" msgid "drought" -msgstr "Beschwörung eines Hitzeelementar" +msgstr "Tor in die Ebene der Hitze" msgctxt "describe" msgid "p6" @@ -3653,7 +3533,7 @@ msgstr "FAHRE" msgctxt "spell" msgid "stormwinds" -msgstr "Beschwöre einen Sturmelementar" +msgstr "Sturmelementar" msgctxt "race" msgid "elf" @@ -3812,10 +3692,6 @@ msgstr "Silber" msgid "jadee_ring_p" msgstr "Jadees Hochzeitsringe" -msgctxt "race" -msgid "apophis" -msgstr "Apophis" - msgctxt "shipinfo" msgid "no_info" msgstr "Keine Informationen über diesen Schiffstyp verfügbar." @@ -4086,10 +3962,6 @@ msgstr "Neue Zauber" msgid "nr_borderlist_prefix" msgstr "Im " -msgctxt "race" -msgid "greenscarab_d" -msgstr "grünen Scarabäen" - msgctxt "iteminfo" msgid "speedsail" msgstr "Setzt eine Einheit dieses Segel auf einem Schiff, so erhöht sich dessen Reichweite permanent um 1 Region." @@ -4102,10 +3974,6 @@ msgctxt "spell" msgid "flee" msgstr "Grauen der Schlacht" -msgctxt "race" -msgid "greenscarab_p" -msgstr "grüne Scarabäen" - msgid "studypotion_p" msgstr "Lerntränke" @@ -4133,10 +4001,6 @@ msgctxt "race" msgid "human_d" msgstr "Menschen" -msgctxt "race" -msgid "greenscarab_x" -msgstr "grünen Scarabäen" - msgctxt "spellinfo" msgid "unholypower" msgstr "Nur geflüstert wird dieses Ritual an den dunklen Akademien an die Adepten weitergegeben, gehört es doch zu den finstersten, die je niedergeschrieben wurden. Durch die Anrufung unheiliger Dämonen wird die Kraft der lebenden Toten verstärkt und sie verwandeln sich in untote Monster großer Kraft." @@ -4154,7 +4018,7 @@ msgstr "Kein Magiegebiet" msgctxt "spell" msgid "summonfireelemental" -msgstr "Beschwörung eines Hitzeelementar" +msgstr "Hitzeelementar" msgid "seed" msgstr "Same" @@ -6121,10 +5985,6 @@ msgctxt "race" msgid "halfling_p" msgstr "Halblinge" -msgctxt "race" -msgid "greenscarab" -msgstr "grüner Scarabäus" - msgctxt "keyword" msgid "quit" msgstr "STIRB" diff --git a/res/translations/strings.en.po b/res/translations/strings.en.po index f1e04959e..6375c5e96 100644 --- a/res/translations/strings.en.po +++ b/res/translations/strings.en.po @@ -131,10 +131,6 @@ msgstr "POTIONS" msgid "northwest" msgstr "northwest" -msgctxt "race" -msgid "undeadpharaoh_d" -msgstr "undead Pharaoh" - msgid "h19_p" msgstr "white hemlocks" @@ -162,14 +158,6 @@ msgstr "The famous bard Mirim was known for exceptionally limber play of the har msgid "rustysword_p" msgstr "rusty swords" -msgctxt "race" -msgid "apophis_d" -msgstr "apophis" - -msgctxt "race" -msgid "undeadpharaoh_p" -msgstr "undead Pharaohs" - msgctxt "iteminfo" msgid "magicbag" msgstr "This bag encloses a dimensional fold, which can store up to 200 stones of weight without any extra burden on the bearer. Large items such as horses or carts cannot be placed inside." @@ -200,10 +188,6 @@ msgctxt "calendar" msgid "age_3" msgstr "the third age" -msgctxt "race" -msgid "undeadpharaoh_x" -msgstr "undead Pharaoh" - msgctxt "race" msgid "youngdragon_d" msgstr "young dragons" @@ -223,10 +207,6 @@ msgctxt "spellinfo" msgid "auraleak" msgstr "With this dark ritual the chaos sorcerer causes a deep rift to appear in the astral balance that will tear all magical power from a region. All spellcasters in that region will lose most of their aura." -msgctxt "race" -msgid "apophis_p" -msgstr "apophis" - msgctxt "spell" msgid "calm_monster" msgstr "Calm Monster" @@ -239,10 +219,6 @@ msgctxt "spell" msgid "readmind" msgstr "Mind Probe" -msgctxt "race" -msgid "apophis_x" -msgstr "apophis" - msgid "nr_options" msgstr "Options" @@ -537,10 +513,6 @@ msgstr "waterfinders" msgid "tree" msgstr "tree" -msgctxt "race" -msgid "redscarab" -msgstr "red scarab" - msgctxt "race" msgid "wolf_d" msgstr "wolves" @@ -665,10 +637,6 @@ msgctxt "race" msgid "shadowmaster_d" msgstr "shadowmasters" -msgctxt "race" -msgid "redscarab_d" -msgstr "red scarab" - msgid "aurapotion50_p" msgstr "aura potions" @@ -691,9 +659,6 @@ msgctxt "spellinfo" msgid "disturbingdreams" msgstr "This spell causes insomnia and restlessness in a whole region for several weeks. All affected persons will learn much slower than normal." -msgid "wdw_pyramid" -msgstr "pyramid" - msgid "plain" msgstr "plain" @@ -704,10 +669,6 @@ msgstr "shadowmaster" msgid "unarmed" msgstr "unarmed" -msgctxt "race" -msgid "redscarab_p" -msgstr "red scarabs" - msgctxt "race" msgid "seaserpent_p" msgstr "sea serpents" @@ -740,10 +701,6 @@ msgstr "adamantium" msgid "seashell" msgstr "seashell" -msgctxt "race" -msgid "redscarab_x" -msgstr "red scarab" - msgid "adamantiumplate" msgstr "adamantium plate" @@ -776,15 +733,11 @@ msgstr "Astral Disruption" msgctxt "spell" msgid "migration" -msgstr "Rit of Acceptance" +msgstr "Rite of Acceptance" msgid "SCHIFF" msgstr "SHIP" -msgctxt "race" -msgid "littlescarab" -msgstr "little scarab" - msgid "spice_p" msgstr "spice" @@ -1388,10 +1341,6 @@ msgstr "wood" msgid "presspass_p" msgstr "presspasses" -msgctxt "race" -msgid "bluescarab" -msgstr "blue scarab" - msgctxt "school" msgid "draig" msgstr "Draig" @@ -1412,10 +1361,6 @@ msgctxt "skill" msgid "taxation" msgstr "taxation" -msgctxt "race" -msgid "undeadpharaoh" -msgstr "undead Pharaoh" - msgctxt "spell" msgid "heroic_song" msgstr "Epic Heroes" @@ -1424,10 +1369,6 @@ msgctxt "spell" msgid "mallorntreegrow" msgstr "Bless Mallorn Logs" -msgctxt "race" -msgid "littlescarab_d" -msgstr "little scarab" - msgctxt "keyword" msgid "hide" msgstr "HIDE" @@ -1447,10 +1388,6 @@ msgctxt "damage" msgid "plusstrong" msgstr "super strong" -msgctxt "race" -msgid "littlescarab_p" -msgstr "little scarab" - msgid "an_unknown_ship" msgstr "an unknown ship" @@ -1496,10 +1433,6 @@ msgctxt "spellinfo" msgid "rustweapon" msgstr "This ritual conjurs up a dark thunderstorm that affects a whole region. The magic rain will let rust any ore. Iron weapons and armor will get rusty. The exact number of items affected by the rain depends on the ammount of power invested by the magician. Up to ten weapons can be destroyed per level - a Ring of Power increases the effect like an additional level." -msgctxt "race" -msgid "littlescarab_x" -msgstr "little scarab" - msgid "rop" msgstr "ring of power" @@ -1924,10 +1857,6 @@ msgctxt "spell" msgid "wdwpyramid_illaun" msgstr "Dream of the gods" -msgctxt "race" -msgid "bluescarab_d" -msgstr "blue scarab" - msgctxt "race" msgid "snotling" msgstr "snotling" @@ -1962,10 +1891,6 @@ msgstr "gully " msgid "citadel" msgstr "citadel" -msgctxt "race" -msgid "bluescarab_p" -msgstr "blue scarabs" - msgctxt "spell" msgid "puttorest" msgstr "Eternal Rest" @@ -1986,10 +1911,6 @@ msgstr "wyrms" msgid "viele" msgstr "many" -msgctxt "race" -msgid "bluescarab_x" -msgstr "blue scarab" - msgctxt "spell" msgid "wdwpyramid_draig" msgstr "Power of the Gods" @@ -2551,7 +2472,7 @@ msgstr "Allow a Tangy Temerity to simmer for three hours in a litre of water, th msgctxt "spell" msgid "drought" -msgstr "Summon Fire Elemental" +msgstr "Gate to the elemental plane of fire" msgid "pegasus_p" msgstr "pegasi" @@ -3349,10 +3270,6 @@ msgstr "silver" msgid "jadee_ring_p" msgstr "Jadee's wedding rings" -msgctxt "race" -msgid "apophis" -msgstr "apophis" - msgctxt "shipinfo" msgid "no_info" msgstr "No Information available for this type of ship." @@ -3614,10 +3531,6 @@ msgstr "New Spells" msgid "nr_borderlist_prefix" msgstr "To the " -msgctxt "race" -msgid "greenscarab_d" -msgstr "green scarab" - msgctxt "iteminfo" msgid "speedsail" msgstr "A unit setting this sail on a ship temporarily will permanently increase the ship's range by 1." @@ -3630,10 +3543,6 @@ msgctxt "spell" msgid "flee" msgstr "Unspeakable Horrors" -msgctxt "race" -msgid "greenscarab_p" -msgstr "green scarab" - msgid "studypotion_p" msgstr "brain boosts" @@ -3661,10 +3570,6 @@ msgctxt "race" msgid "human_d" msgstr "humans" -msgctxt "race" -msgid "greenscarab_x" -msgstr "green scarab" - msgctxt "spellinfo" msgid "unholypower" msgstr "Only whispered the knowledge of performing this ritual is passed to the adepts of the dark academies, for it is one of the darkest that has ever been written down. By calling unholy demons the strength of the living dead is greatly increased and they are turned into undead monsters of immense power." @@ -5421,10 +5326,6 @@ msgctxt "keyword" msgid "quit" msgstr "QUIT" -msgctxt "race" -msgid "greenscarab" -msgstr "green scarab" - msgid "AURA" msgstr "AURA" diff --git a/scripts/tests/e3/rules.lua b/scripts/tests/e3/rules.lua index 8983d4584..e39bd26c4 100644 --- a/scripts/tests/e3/rules.lua +++ b/scripts/tests/e3/rules.lua @@ -192,14 +192,14 @@ function test_seacast() u2:add_spell("stormwinds") update_owners() u2:clear_orders() - u2:add_order("Zaubere stufe 2 'Beschwoere einen Sturmelementar' " .. itoa36(s1.id)) + u2:add_order("Zaubere stufe 2 Sturmelementar " .. itoa36(s1.id)) u1:clear_orders() u1:add_order("NACH O O O O") process_orders() assert_equal(4, u2.region.x) u2:clear_orders() - u2:add_order("Zaubere stufe 2 'Beschwoere einen Sturmelementar' " .. itoa36(s1.id)) + u2:add_order("Zaubere stufe 2 Sturmelementar " .. itoa36(s1.id)) u1:clear_orders() u1:add_order("NACH O O O O") process_orders() From 31e8df28a44c6a0fe313dce07abb8fb2aae0f652 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 19 Jul 2020 19:48:08 +0200 Subject: [PATCH 26/52] =?UTF-8?q?Alle=20Startregionen=20sind=20W=C3=A4lder?= =?UTF-8?q?=20mit=20festen=20Werten.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/eressea/equipment.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/scripts/eressea/equipment.lua b/scripts/eressea/equipment.lua index 3163b85d9..328f33048 100644 --- a/scripts/eressea/equipment.lua +++ b/scripts/eressea/equipment.lua @@ -4,6 +4,15 @@ local self = {} local function equip_first(u) name = 'seed_' .. u.race equip_unit(u, name, 255) + local r = u.region + r.terrain = 'plain' + r:set_flag(1, false) -- kein mallorn + r:set_resource('tree', 500) + r:set_resource('sapling', 100) + r:set_resource('seed', 50) + r:set_resource('peasant', 4000) + r:set_resource('money', 80000) + r:set_resource('horse', 50) end local mysets = { From 0fca7287cbe97cb33554af3e6256c9c47c9db928 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 25 Jul 2020 20:31:15 +0200 Subject: [PATCH 27/52] Bug 2684 Astraler Blick Old implementation was bad, using set_observer is better. Still missing a test, but improved the one for sp_viewreality, which is almost doing the same thing - adaptation should be easy. Fixes https://bugs.eressea.de/view.php?id=2684 --- src/kernel/pathfinder.c | 2 +- src/kernel/pathfinder.h | 13 +++----- src/monsters.c | 2 +- src/spells.c | 74 ++++++++++------------------------------- src/spells.test.c | 10 +++--- src/teleport.c | 45 ++++++++++++++----------- src/teleport.h | 1 + 7 files changed, 56 insertions(+), 91 deletions(-) diff --git a/src/kernel/pathfinder.c b/src/kernel/pathfinder.c index f2103f63f..15ac0e966 100644 --- a/src/kernel/pathfinder.c +++ b/src/kernel/pathfinder.c @@ -79,7 +79,7 @@ static void free_nodes(node * root) } } -struct selist *regions_in_range(struct region *handle_start, int maxdist, +struct selist *path_regions_in_range(struct region *handle_start, int maxdist, bool(*allowed) (const struct region *, const struct region *)) { selist * rlist = NULL; diff --git a/src/kernel/pathfinder.h b/src/kernel/pathfinder.h index 9557fe735..df4bfbe80 100644 --- a/src/kernel/pathfinder.h +++ b/src/kernel/pathfinder.h @@ -8,15 +8,10 @@ extern "C" { const struct region *target, int maxlen, bool(*allowed) (const struct region *, const struct region *)); extern bool path_exists(struct region *handle_start, const struct region *target, - int maxlen, bool(*allowed) (const struct region *, - const struct region *)); - extern bool allowed_swim(const struct region *src, - const struct region *target); - extern bool allowed_fly(const struct region *src, - const struct region *target); - extern bool allowed_walk(const struct region *src, - const struct region *target); - extern struct selist *regions_in_range(struct region *src, int maxdist, + int maxlen, bool(*allowed) (const struct region *, const struct region *)); + extern bool allowed_fly(const struct region *src, const struct region *target); + extern bool allowed_walk(const struct region *src, const struct region *target); + extern struct selist *path_regions_in_range(struct region *src, int maxdist, bool(*allowed) (const struct region *, const struct region *)); extern void pathfinder_cleanup(void); diff --git a/src/monsters.c b/src/monsters.c index 34214a342..129c8f76c 100644 --- a/src/monsters.c +++ b/src/monsters.c @@ -497,7 +497,7 @@ static attrib *set_new_dragon_target(unit * u, region * r, int range) { int max_affinity = 0; region *max_region = NULL; - selist *ql, *rlist = regions_in_range(r, range, allowed_dragon); + selist *ql, *rlist = path_regions_in_range(r, range, allowed_dragon); int qi; for (qi = 0, ql = rlist; ql; selist_advance(&ql, &qi, 1)) { diff --git a/src/spells.c b/src/spells.c index fbd4d71d5..0b17578fe 100644 --- a/src/spells.c +++ b/src/spells.c @@ -5423,19 +5423,22 @@ int sp_fetchastral(castorder * co) return cast_level; } -#define SHOWASTRAL_IS_BORKED +static bool cb_not_astral_blocked(const struct region *rt) { + return !is_cursed(rt->attribs, &ct_astralblock); +} + +#undef SHOWASTRAL_IS_BORKED #ifndef SHOWASTRAL_IS_BORKED +#define SHOWASTRAL_MAX_RADIUS 5 int sp_showastral(castorder * co) { - unit *u; region *rt; int n = 0; - int c = 0; - region_list *rl, *rl2; region *r = co_get_region(co); unit *mage = co_get_caster(co); - int cast_level = co->level; - double power = co->force; + int force = (int) co->force; + int radius = (force < SHOWASTRAL_MAX_RADIUS) ? force : SHOWASTRAL_MAX_RADIUS; + region *targets[4 * SHOWASTRAL_MAX_RADIUS * SHOWASTRAL_MAX_RADIUS]; switch (getplaneid(r)) { case 0: @@ -5455,62 +5458,21 @@ int sp_showastral(castorder * co) return 0; } - rl = all_in_range(rt, power / 5); - - /* Erst Einheiten zaehlen, fuer die Grammatik. */ - - for (rl2 = rl; rl2; rl2 = rl2->next) { - region *r2 = rl2->data; - if (!is_cursed(r2->attribs, &ct_astralblock)) { - for (u = r2->units; u; u = u->next) { - n++; - } - } - } - + n = regions_in_range(rt, radius, cb_not_astral_blocked, targets); if (n == 0) { /* sprintf(buf, "%s kann niemanden im astralen Nebel entdecken.", unitname(mage)); */ cmistake(mage, co->order, 220, MSG_MAGIC); } else { - - /* Ausgeben */ - - sprintf(buf, "%s hat eine Vision der astralen Ebene. Im astralen " - "Nebel zu erkennen sind ", unitname(mage)); - - for (rl2 = rl; rl2; rl2 = rl2->next) { - if (!is_cursed(rl2->data->attribs, &ct_astralblock)) { - for (u = rl2->data->units; u; u = u->next) { - c++; - scat(unitname(u)); - scat(" ("); - if (!fval(u, UFL_ANON_FACTION)) { - scat(factionname(u->faction)); - scat(", "); - } - icat(u->number); - scat(" "); - scat(LOC(mage->faction->locale, rc_name_s(u_race(u), (u->number == 1) ? NAME_SINGULAR : NAME_PLURAL))); - scat(", Entfernung "); - icat(distance(rl2->data, rt)); - scat(")"); - if (c == n - 1) { - scat(" und "); - } - else if (c < n - 1) { - scat(", "); - } - } - } + int i; + for (i = 0; i != n; ++i) { + region *rt = targets[i]; + set_observer(rt, mage->faction, (int)(co->force / 2), 2); } - scat("."); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); } - free_regionlist(rl); - return cast_level; + return co->level; } #endif @@ -5519,7 +5481,7 @@ int sp_viewreality(castorder * co) { region *r = co_get_region(co); unit *mage = co_get_caster(co); - int cast_level = co->level; + int force = (int)co->force; message *m; region *rl[MAX_SCHEMES]; int num; @@ -5537,7 +5499,7 @@ int sp_viewreality(castorder * co) for (i = 0; i != num; ++i) { region *rt = rl[i]; if (!is_cursed(rt->attribs, &ct_astralblock)) { - set_observer(rt, mage->faction, co->level / 2, 2); + set_observer(rt, mage->faction, force / 2, 2); } } } @@ -5546,7 +5508,7 @@ int sp_viewreality(castorder * co) r_addmessage(r, mage->faction, m); msg_release(m); - return cast_level; + return co->level; } static void cb_disrupt_astral(region *r2, void *cbdata) { diff --git a/src/spells.test.c b/src/spells.test.c index 6ee4a305c..6261bcacd 100644 --- a/src/spells.test.c +++ b/src/spells.test.c @@ -116,7 +116,7 @@ static void test_bad_dreams(CuTest *tc) { } static void test_view_reality(CuTest *tc) { - region *r, *ra; + region *r, *ra, *rx; faction *f; unit *u; castorder co; @@ -127,12 +127,13 @@ static void test_view_reality(CuTest *tc) { mt_create_va(mt_new("viewreality_effect", NULL), "unit:unit", MT_NEW_END); r = test_create_region(0, 0, NULL); + rx = test_create_region(0, TP_RADIUS+1, NULL); ra = test_create_region(real2tp(r->x), real2tp(r->y), NULL); ra->_plane = get_astralplane(); f = test_create_faction(NULL); u = test_create_unit(f, r); - test_create_castorder(&co, u, 10, 10., 0, NULL); + test_create_castorder(&co, u, 10, 10.0, 0, NULL); CuAssertIntEquals(tc, -1, get_observer(r, f)); CuAssertIntEquals(tc, 0, sp_viewreality(&co)); CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "spell_astral_only")); @@ -141,11 +142,12 @@ static void test_view_reality(CuTest *tc) { test_clear_messagelist(&f->msgs); move_unit(u, ra, NULL); - test_create_castorder(&co, u, 9, 10., 0, NULL); + test_create_castorder(&co, u, 9, 10.0, 0, NULL); CuAssertIntEquals(tc, -1, get_observer(r, f)); CuAssertIntEquals(tc, 9, sp_viewreality(&co)); CuAssertPtrEquals(tc, NULL, test_find_messagetype(f->msgs, "spell_astral_only")); - CuAssertIntEquals(tc, 4, get_observer(r, f)); + CuAssertIntEquals(tc, 5, get_observer(r, f)); + CuAssertIntEquals(tc, -1, get_observer(rx, f)); CuAssertPtrEquals(tc, f, (void *)ra->individual_messages->viewer); CuAssertPtrNotNull(tc, test_find_messagetype(ra->individual_messages->msgs, "viewreality_effect")); free_castorder(&co); diff --git a/src/teleport.c b/src/teleport.c index ecd3834bf..faa2f7103 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -43,32 +43,37 @@ static region *tpregion(const region * r) return rt; } - -int get_astralregions(const region * r, bool(*valid) (const region *), region *result[]) +int regions_in_range(const region * r, int radius, bool(*valid) (const region *), region *result[]) { - assert(is_astral(r)); - r = r_astral_to_standard(r); - if (r) { - int x, y, num = 0; - for (x = -TP_RADIUS; x <= +TP_RADIUS; ++x) { - for (y = -TP_RADIUS; y <= +TP_RADIUS; ++y) { + int x, y, num = 0; + const struct plane *pl = rplane(r); + for (x = -radius; x <= +radius; ++x) { + for (y = -radius; y <= radius; ++y) { + int dist = koor_distance(0, 0, x, y); + + if (dist <= radius) { region *rn; - int dist = koor_distance(0, 0, x, y); - - if (dist <= TP_RADIUS) { - int nx = r->x + x, ny = r->y + y; - pnormalize(&nx, &ny, rplane(r)); - rn = findregion(nx, ny); - if (rn != NULL && (valid == NULL || valid(rn))) { - if (result) { - result[num] = rn; - } - ++num; + int nx = r->x + x, ny = r->y + y; + pnormalize(&nx, &ny, pl); + rn = findregion(nx, ny); + if (rn != NULL && (valid == NULL || valid(rn))) { + if (result) { + result[num] = rn; } + ++num; } } } - return num; + } + return num; +} + +int get_astralregions(const region * r, bool(*valid) (const region *), region *result[]) +{ + assert(is_astral(r)); + r = r_astral_to_standard(r); + if (r) { + return regions_in_range(r, TP_RADIUS, valid, result); } return 0; } diff --git a/src/teleport.h b/src/teleport.h index d91bed3df..340e38667 100644 --- a/src/teleport.h +++ b/src/teleport.h @@ -20,6 +20,7 @@ extern "C" { bool is_astral(const struct region *r); struct plane *get_astralplane(void); int get_astralregions(const struct region * r, bool(*valid) (const struct region *), struct region *result[]); + int regions_in_range(const struct region * r, int radius, bool(*valid) (const struct region *), struct region *result[]); void create_teleport_plane(void); void spawn_braineaters(float chance); From a4837c9785861599c878d869e1cd5aee61d64bce Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 26 Jul 2020 14:03:46 +0200 Subject: [PATCH 28/52] New message for spells that cannot be cast in astral space. English Style: can't or can not => cannot --- res/core/messages.xml | 7 +++++++ res/translations/messages.de.po | 3 +++ res/translations/messages.en.po | 13 ++++++++----- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/res/core/messages.xml b/res/core/messages.xml index 4886ea88d..966641db3 100644 --- a/res/core/messages.xml +++ b/res/core/messages.xml @@ -3485,6 +3485,13 @@ + + + + + + + diff --git a/res/translations/messages.de.po b/res/translations/messages.de.po index 7db486687..c82d62087 100644 --- a/res/translations/messages.de.po +++ b/res/translations/messages.de.po @@ -641,6 +641,9 @@ msgstr "\"$unit($unit) vergisst $skill($skill).\"" msgid "spell_astral_only" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Dieser Zauber kann nur im Astralraum gezaubert werden.\"" +msgid "spell_astral_forbidden" +msgstr "\"$unit($unit) in $region($region): '$order($command)' - Dieser Zauber kann nicht im Astralraum gezaubert werden.\"" + msgid "sp_movecastle_fail_0" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Der Elementar ist zu klein, um das Gebäude zu tragen.\"" diff --git a/res/translations/messages.en.po b/res/translations/messages.en.po index b9a7ff0d9..63ee95ea1 100644 --- a/res/translations/messages.en.po +++ b/res/translations/messages.en.po @@ -594,7 +594,7 @@ msgid "error206" msgstr "\"$unit($unit) in $region($region): '$order($command)' - There is alrady a spell on that building.\"" msgid "error316" -msgstr "\"$unit($unit) in $region($region): '$order($command)' - Without ingredients an alchemist can not produce anything.\"" +msgstr "\"$unit($unit) in $region($region): '$order($command)' - Without ingredients an alchemist cannot produce anything.\"" msgid "error312" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Lycantropes may not be mixed with normal people.\"" @@ -638,6 +638,9 @@ msgstr "\"$unit($unit) in $region($region) damages the $ship($ship).\"" msgid "forget" msgstr "\"$unit($unit) forgets $skill($skill).\"" +msgid "spell_astral_forbidden" +msgstr "\"$unit($unit) in $region($region): '$order($command)' - This spell cannot be cast on the astral plane.\"" + msgid "spell_astral_only" msgstr "\"$unit($unit) in $region($region): '$order($command)' - This spell can only be cast on the astral plane.\"" @@ -864,7 +867,7 @@ msgid "curseinfo::shipnodrift_1" msgstr "\"The $ship($ship) is blessed with favourable winds$if($lt($duration,3),\", but the spell is starting to wear thin\",\"\"). ($int36($id))\"" msgid "error105" -msgstr "\"$unit($unit) in $region($region): '$order($command)' - Empty units can not be handed over.\"" +msgstr "\"$unit($unit) in $region($region): '$order($command)' - Empty units cannot be handed over.\"" msgid "curseinfo::sparkle_7" msgstr "\"The women of the nearby village cast furtive looks at $unit($unit). ($int36($id))\"" @@ -1164,7 +1167,7 @@ msgid "error_max_magicians" msgstr "\"$unit($unit) in $region($region): '$order($command)' - There may not be more than $int($amount) magicians in your faction.\"" msgid "error304" -msgstr "\"$unit($unit) in $region($region): '$order($command)' - Units of a faction that can't be attacked may not guard.\"" +msgstr "\"$unit($unit) in $region($region): '$order($command)' - Units of a faction that cannot be attacked may not guard.\"" msgid "error300" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Invalid synonym.\"" @@ -2301,7 +2304,7 @@ msgid "sailnolanding" msgstr "\"The $ship($ship) could not berth in $region($region). The coast is too dangerous for the vessel.\"" msgid "error116" -msgstr "\"$unit($unit) in $region($region): '$order($command)' - Number can not be assigned.\"" +msgstr "\"$unit($unit) in $region($region): '$order($command)' - Number cannot be assigned.\"" msgid "error112" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Names may not contain parenthesis.\"" @@ -2634,7 +2637,7 @@ msgid "seduce_effect_0" msgstr "\"$unit($unit) gives $unit($mage) $resources($items).\"" msgid "error311" -msgstr "\"$unit($unit) in $region($region): '$order($command)' - This unit can not change shape.\"" +msgstr "\"$unit($unit) in $region($region): '$order($command)' - This unit cannot change shape.\"" msgid "error201" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Race and target unit have not been supplied.\"" From f90e4f5351ab81fd4561f05b54305117bf6c4499 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 26 Jul 2020 16:58:57 +0200 Subject: [PATCH 29/52] bug 2684: update sp_showastral and sp_viewreality. add more messages add unit tests closes https://bugs.eressea.de/view.php?id=2684 --- res/core/messages.xml | 17 ++--- res/translations/messages.de.po | 10 +-- res/translations/messages.en.po | 10 +-- src/attributes/attributes.c | 2 +- src/spells.c | 68 ++++++++--------- src/spells.h | 3 +- src/spells.test.c | 130 +++++++++++++++++++++++++++++--- src/spells/unitcurse.c | 4 +- src/teleport.c | 1 - src/teleport.h | 3 +- 10 files changed, 178 insertions(+), 70 deletions(-) diff --git a/res/core/messages.xml b/res/core/messages.xml index 966641db3..f505d2307 100644 --- a/res/core/messages.xml +++ b/res/core/messages.xml @@ -247,14 +247,14 @@ - + - + @@ -1124,6 +1124,12 @@ + + + + + + @@ -5798,13 +5804,6 @@ - - - - - - - diff --git a/res/translations/messages.de.po b/res/translations/messages.de.po index c82d62087..abbf79811 100644 --- a/res/translations/messages.de.po +++ b/res/translations/messages.de.po @@ -656,7 +656,7 @@ msgstr "\"$unit($unit) ertrinkt in $region($region).\"" msgid "travel" msgstr "\"$unit($unit) $if($eq($mode,1),\"reitet\", \"wandert\") von $region($start) nach $region($end).$if($isnull($regions),\"\",\" Dabei wurde $trail($regions) durchquert.\")\"" -msgid "curseinfo::skill_1" +msgid "curseinfo_skill_1" msgstr "\"$unit($unit) ist ungewöhnlich geschickt in $skill($skill). ($int36($id))\"" msgid "error11" @@ -1487,12 +1487,9 @@ msgstr "\"Ein magischer Schimmer liegt auf diesen Mauern. ($int36($id))\"" msgid "changebanner" msgstr "\"Das Banner wurde auf '$value' geändert.\"" -msgid "curseinfo::skill_2" +msgid "curseinfo_skill_2" msgstr "\"$unit($unit) ist ungewöhnlich ungeschickt in $skill($skill). ($int36($id))\"" -msgid "spellfail::noway" -msgstr "\"$unit($unit) in $region($region): '$order($command)' - Dorthin führt kein Weg.\"" - msgid "spellbuildingresists" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Gebäude $int36($id) konnte nicht verzaubert werden.\"" @@ -2522,6 +2519,9 @@ msgstr "\"$unit($mage) meint, dass auf $region($region) ein Zauber liegt, konnte msgid "viewreality_effect" msgstr "\"$unit($unit) gelingt es, durch die Nebel auf die Realität zu blicken.\"" +msgid "showastral_effect" +msgstr "\"$unit($unit) gelingt es, in die Nebel des Astralraums zu blicken.\"" + msgid "use_speedsail" msgstr "\"$unit($unit) setzt ein Sonnensegel. Die Geschwindigkeit des Schiffes erhöht um $int($speed).\"" diff --git a/res/translations/messages.en.po b/res/translations/messages.en.po index 63ee95ea1..013986fbc 100644 --- a/res/translations/messages.en.po +++ b/res/translations/messages.en.po @@ -656,7 +656,7 @@ msgstr "\"$unit($unit) drowns in $region($region).\"" msgid "travel" msgstr "\"$unit($unit) $if($eq($mode,1),\"rides\", \"walks\") from $region($start) to $region($end)$if($isnull($regions),\"\",\" by way of $trail($regions)\").\"" -msgid "curseinfo::skill_1" +msgid "curseinfo_skill_1" msgstr "\"$unit($unit) is incredibly skilled at $skill($skill). ($int36($id))\"" msgid "error11" @@ -1487,12 +1487,9 @@ msgstr "\"A magical shimmer lies on these walls. ($int36($id))\"" msgid "changebanner" msgstr "\"Banner has been changed to '$value'.\"" -msgid "curseinfo::skill_2" +msgid "curseinfo_skill_2" msgstr "\"$unit($unit) has some troubles with $skill($skill). ($int36($id))\"" -msgid "spellfail::noway" -msgstr "\"$unit($unit) in $region($region): '$order($command)' - There is no route leading there.\"" - msgid "spellbuildingresists" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Building $int36($id) could not be charmed.\"" @@ -2519,6 +2516,9 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - The magician de msgid "analyse_region_fail" msgstr "\"It appears to $unit($mage) that $region($region) is charmed, but no details have been revealed.\"" +msgid "showastral_effect" +msgstr "\"$unit($unit) has a vision of the astral plane.\"" + msgid "viewreality_effect" msgstr "\"$unit($unit) manages to catch a glimpse of reality through the fog.\"" diff --git a/src/attributes/attributes.c b/src/attributes/attributes.c index 4b1af76b0..54da3c6fd 100644 --- a/src/attributes/attributes.c +++ b/src/attributes/attributes.c @@ -121,7 +121,7 @@ void set_observer(region *r, faction *f, int skill, int turns) attrib *a = a_find(r->attribs, &at_observer); while (a && a->type == &at_observer) { obs_data *od = (obs_data *)a->data.v; - if (od->f == f && od->skill < skill) { + if (od->f == f) { od->skill = skill; od->timer = turns; return; diff --git a/src/spells.c b/src/spells.c index 0b17578fe..604810d1c 100644 --- a/src/spells.c +++ b/src/spells.c @@ -5203,22 +5203,21 @@ int sp_leaveastral(castorder * co) case 1: rt = pa->param[0]->data.r; if (!rt || r_standard_to_astral(rt) != r || !inhabitable(rt)) { - ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, - "spellfail::noway", "")); + cmistake(mage, co->order, 216, MSG_MAGIC); return 0; } ro = r; break; default: ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, - "spell_astral_only", "")); + "spell_astral_only", NULL)); return 0; } if (ro == NULL || is_cursed(ro->attribs, &ct_astralblock) || is_cursed(rt->attribs, &ct_astralblock)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, - "spellfail_astralblock", "")); + "spellfail_astralblock", NULL)); return 0; } @@ -5423,13 +5422,10 @@ int sp_fetchastral(castorder * co) return cast_level; } -static bool cb_not_astral_blocked(const struct region *rt) { - return !is_cursed(rt->attribs, &ct_astralblock); +static bool cb_show_astral(const struct region *r) { + return r->units && !is_cursed(r->attribs, &ct_astralblock); } -#undef SHOWASTRAL_IS_BORKED -#ifndef SHOWASTRAL_IS_BORKED -#define SHOWASTRAL_MAX_RADIUS 5 int sp_showastral(castorder * co) { region *rt; @@ -5440,29 +5436,24 @@ int sp_showastral(castorder * co) int radius = (force < SHOWASTRAL_MAX_RADIUS) ? force : SHOWASTRAL_MAX_RADIUS; region *targets[4 * SHOWASTRAL_MAX_RADIUS * SHOWASTRAL_MAX_RADIUS]; - switch (getplaneid(r)) { - case 0: - rt = r_standard_to_astral(r); - if (!rt || fval(rt->terrain, FORBIDDEN_REGION)) { - /* Hier gibt es keine Verbindung zur astralen Welt */ - cmistake(mage, co->order, 216, MSG_MAGIC); - return 0; - } - break; - case 1: - rt = r; - break; - default: + if (getplaneid(r) == 1) { + ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, + "spell_astral_forbidden", NULL)); + return 0; + } + rt = r_standard_to_astral(r); + if (rt == NULL || fval(rt->terrain, FORBIDDEN_REGION) || is_cursed(r->attribs, &ct_astralblock)) { /* Hier gibt es keine Verbindung zur astralen Welt */ cmistake(mage, co->order, 216, MSG_MAGIC); return 0; } - n = regions_in_range(rt, radius, cb_not_astral_blocked, targets); + n = regions_in_range(rt, radius, cb_show_astral, targets); if (n == 0) { /* sprintf(buf, "%s kann niemanden im astralen Nebel entdecken.", unitname(mage)); */ cmistake(mage, co->order, 220, MSG_MAGIC); + return 0; } else { int i; @@ -5470,43 +5461,52 @@ int sp_showastral(castorder * co) region *rt = targets[i]; set_observer(rt, mage->faction, (int)(co->force / 2), 2); } + ADDMSG(&mage->faction->msgs, msg_message("showastral_effect", "unit", mage)); } return co->level; } -#endif /* ------------------------------------------------------------- */ + +static bool cb_view_reality(const struct region *r) { + return !is_cursed(r->attribs, &ct_astralblock); +} + int sp_viewreality(castorder * co) { region *r = co_get_region(co); unit *mage = co_get_caster(co); int force = (int)co->force; - message *m; region *rl[MAX_SCHEMES]; int num; if (getplaneid(r) != 1) { /* sprintf(buf, "Dieser Zauber kann nur im Astralraum gezaubert werden."); */ ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, - "spell_astral_only", "")); + "spell_astral_only", NULL)); + return 0; + } + + if (is_cursed(r->attribs, &ct_astralblock)) { + cmistake(mage, co->order, 216, MSG_MAGIC); return 0; } - num = get_astralregions(r, NULL, rl); + num = get_astralregions(r, cb_view_reality, rl); if (num > 0) { int i; for (i = 0; i != num; ++i) { region *rt = rl[i]; - if (!is_cursed(rt->attribs, &ct_astralblock)) { - set_observer(rt, mage->faction, force / 2, 2); - } + set_observer(rt, mage->faction, force / 2, 2); } } + else { + cmistake(mage, co->order, 216, MSG_MAGIC); + return 0; + } - m = msg_message("viewreality_effect", "unit", mage); - r_addmessage(r, mage->faction, m); - msg_release(m); + ADDMSG(&mage->faction->msgs, msg_message("viewreality_effect", "unit", mage)); return co->level; } @@ -6421,9 +6421,7 @@ static spelldata spell_functions[] = { { "analyze_magic", sp_analysemagic, 0 }, { "concealing_aura", sp_itemcloak, 0 }, { "tybiedfumbleshield", sp_fumbleshield, 0 }, -#ifndef SHOWASTRAL_IS_BORKED { "show_astral", sp_showastral, 0 }, -#endif { "resist_magic", sp_resist_magic_bonus, 0 }, { "keeploot", sp_keeploot, 0 }, { "enterastral", sp_enterastral, 0 }, diff --git a/src/spells.h b/src/spells.h index 5a82d5b6f..86e15bd93 100644 --- a/src/spells.h +++ b/src/spells.h @@ -16,10 +16,11 @@ extern "C" { void register_magicresistance(void); void register_spells(void); +#define SHOWASTRAL_MAX_RADIUS 5 int sp_baddreams(struct castorder * co); int sp_gooddreams(struct castorder * co); int sp_viewreality(struct castorder * co); - + int sp_showastral(struct castorder * co); #define ACTION_RESET 0x01 /* reset the one-time-flag FFL_SELECT (on first pass) */ #define ACTION_CANSEE 0x02 /* to people who can see the actor */ #define ACTION_CANNOTSEE 0x04 /* to people who can not see the actor */ diff --git a/src/spells.test.c b/src/spells.test.c index 6261bcacd..06572229c 100644 --- a/src/spells.test.c +++ b/src/spells.test.c @@ -120,38 +120,147 @@ static void test_view_reality(CuTest *tc) { faction *f; unit *u; castorder co; + curse *c; test_setup(); + mt_create_error(216); + mt_create_error(220); mt_create_va(mt_new("spell_astral_only", NULL), "unit:unit", "region:region", "command:order", MT_NEW_END); mt_create_va(mt_new("viewreality_effect", NULL), "unit:unit", MT_NEW_END); - r = test_create_region(0, 0, NULL); - rx = test_create_region(0, TP_RADIUS+1, NULL); - ra = test_create_region(real2tp(r->x), real2tp(r->y), NULL); - ra->_plane = get_astralplane(); + rx = test_create_region(0, TP_RADIUS + 1, NULL); f = test_create_faction(NULL); - u = test_create_unit(f, r); + u = test_create_unit(f, rx); + /* can only cast in astral space */ test_create_castorder(&co, u, 10, 10.0, 0, NULL); - CuAssertIntEquals(tc, -1, get_observer(r, f)); CuAssertIntEquals(tc, 0, sp_viewreality(&co)); CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "spell_astral_only")); free_castorder(&co); test_clear_messagelist(&f->msgs); + ra = test_create_region(real2tp(0), real2tp(0), NULL); + ra->_plane = get_astralplane(); move_unit(u, ra, NULL); + /* there is no connection from ra to rx */ + test_create_castorder(&co, u, 10, 10.0, 0, NULL); + CuAssertIntEquals(tc, 0, sp_viewreality(&co)); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error216")); + CuAssertIntEquals(tc, -1, get_observer(rx, f)); + free_castorder(&co); + + test_clear_messagelist(&f->msgs); + r = test_create_region(0, 0, NULL); + + test_clear_messagelist(&f->msgs); + + /* units exist, r can be seen, but rx is out of range */ test_create_castorder(&co, u, 9, 10.0, 0, NULL); - CuAssertIntEquals(tc, -1, get_observer(r, f)); CuAssertIntEquals(tc, 9, sp_viewreality(&co)); - CuAssertPtrEquals(tc, NULL, test_find_messagetype(f->msgs, "spell_astral_only")); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "viewreality_effect")); CuAssertIntEquals(tc, 5, get_observer(r, f)); CuAssertIntEquals(tc, -1, get_observer(rx, f)); - CuAssertPtrEquals(tc, f, (void *)ra->individual_messages->viewer); - CuAssertPtrNotNull(tc, test_find_messagetype(ra->individual_messages->msgs, "viewreality_effect")); free_castorder(&co); + set_observer(r, f, -1, -1); + CuAssertIntEquals(tc, -1, get_observer(r, f)); + + /* target region r exists, but astral space is blocked */ + c = create_curse(NULL, &ra->attribs, &ct_astralblock, 50.0, 1, 50, 0); + test_create_castorder(&co, u, 10, 10.0, 0, NULL); + CuAssertIntEquals(tc, 0, sp_viewreality(&co)); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error216")); + CuAssertIntEquals(tc, -1, get_observer(r, f)); + free_castorder(&co); + remove_curse(&ra->attribs, c); + + /* target region r exists, but astral interference is blocked */ + c = create_curse(NULL, &r->attribs, &ct_astralblock, 50.0, 1, 50, 0); + test_create_castorder(&co, u, 10, 10.0, 0, NULL); + CuAssertIntEquals(tc, 0, sp_viewreality(&co)); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error216")); + CuAssertIntEquals(tc, -1, get_observer(r, f)); + free_castorder(&co); + + test_teardown(); +} + +static void test_show_astral(CuTest *tc) { + region *r, *ra, *rx; + faction *f; + unit *u; + castorder co; + curse * c; + + test_setup(); + mt_create_error(216); + mt_create_error(220); + mt_create_va(mt_new("spell_astral_forbidden", NULL), + "unit:unit", "region:region", "command:order", MT_NEW_END); + mt_create_va(mt_new("showastral_effect", NULL), + "unit:unit", MT_NEW_END); + ra = test_create_region(real2tp(0), real2tp(0) + 1 + SHOWASTRAL_MAX_RADIUS, NULL); + ra->_plane = get_astralplane(); + f = test_create_faction(NULL); + u = test_create_unit(f, ra); + + /* error: unit is in astral space */ + test_create_castorder(&co, u, 10, 10.0, 0, NULL); + CuAssertIntEquals(tc, 0, sp_showastral(&co)); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "spell_astral_forbidden")); + free_castorder(&co); + + test_clear_messagelist(&f->msgs); + r = test_create_region(0, 0, NULL); + move_unit(u, r, NULL); + + /* error: no target region */ + test_create_castorder(&co, u, 9, 10.0, 0, NULL); + CuAssertIntEquals(tc, 0, sp_showastral(&co)); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error216")); + CuAssertIntEquals(tc, -1, get_observer(ra, f)); + free_castorder(&co); + + rx = test_create_region(real2tp(r->x), real2tp(r->y), NULL); + rx->_plane = ra->_plane; + + /* rx is in range, but empty */ + test_create_castorder(&co, u, 9, 10.0, 0, NULL); + CuAssertIntEquals(tc, 0, sp_showastral(&co)); + CuAssertIntEquals(tc, -1, get_observer(rx, f)); + CuAssertIntEquals(tc, -1, get_observer(ra, f)); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error220")); + CuAssertPtrEquals(tc, NULL, test_find_messagetype(f->msgs, "showastral_effect")); + free_castorder(&co); + + test_create_unit(f, ra); + test_create_unit(f, rx); + /* rx is in range, but ra is not */ + test_create_castorder(&co, u, 9, 10.0, 0, NULL); + CuAssertIntEquals(tc, 9, sp_showastral(&co)); + CuAssertIntEquals(tc, 5, get_observer(rx, f)); + CuAssertIntEquals(tc, -1, get_observer(ra, f)); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "showastral_effect")); + free_castorder(&co); + + /* astral block on r */ + c = create_curse(NULL, &r->attribs, &ct_astralblock, 50.0, 1, 50, 0); + test_create_castorder(&co, u, 9, 10.0, 0, NULL); + CuAssertIntEquals(tc, 0, sp_showastral(&co)); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error216")); + free_castorder(&co); + remove_curse(&r->attribs, c); + + /* astral block on rx */ + c = create_curse(NULL, &rx->attribs, &ct_astralblock, 50.0, 1, 50, 0); + test_create_castorder(&co, u, 9, 10.0, 0, NULL); + CuAssertIntEquals(tc, 0, sp_showastral(&co)); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error220")); + free_castorder(&co); + remove_curse(&rx->attribs, c); + test_teardown(); } @@ -176,6 +285,7 @@ CuSuite *get_spells_suite(void) CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_watch_region); SUITE_ADD_TEST(suite, test_view_reality); + SUITE_ADD_TEST(suite, test_show_astral); SUITE_ADD_TEST(suite, test_good_dreams); SUITE_ADD_TEST(suite, test_bad_dreams); SUITE_ADD_TEST(suite, test_dreams); diff --git a/src/spells/unitcurse.c b/src/spells/unitcurse.c index 095649562..345f75109 100644 --- a/src/spells/unitcurse.c +++ b/src/spells/unitcurse.c @@ -318,10 +318,10 @@ static message *cinfo_skillmod(const void *obj, objtype_t typ, const curse * c, unit *u = (unit *)obj; int sk = c->data.i; if (c->effect > 0) { - return msg_message("curseinfo::skill_1", "unit skill id", u, sk, c->no); + return msg_message("curseinfo_skill_1", "unit skill id", u, sk, c->no); } else if (c->effect < 0) { - return msg_message("curseinfo::skill_2", "unit skill id", u, sk, c->no); + return msg_message("curseinfo_skill_2", "unit skill id", u, sk, c->no); } } return NULL; diff --git a/src/teleport.c b/src/teleport.c index faa2f7103..067764946 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -22,7 +22,6 @@ #include #define TE_CENTER 1000 -#define TP_DISTANCE 4 int real2tp(int rk) { diff --git a/src/teleport.h b/src/teleport.h index 340e38667..9930a010c 100644 --- a/src/teleport.h +++ b/src/teleport.h @@ -7,7 +7,8 @@ extern "C" { #endif -#define TP_RADIUS 2 /* Radius von Schemen */ +#define TP_DISTANCE 4 +#define TP_RADIUS (TP_DISTANCE/2) /* Radius von Schemen */ #define MAX_SCHEMES ((TP_RADIUS * 2 + 1) * (TP_RADIUS * 2 + 1) - 4) struct region; From 6df7bb2cc98eec8a365b9466ad27abab4e1a2261 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 26 Jul 2020 18:35:12 +0200 Subject: [PATCH 30/52] Bug 2685: magic resistance goblins and insects Magic resistance is not a fraction, but given in percent. Closes https://bugs.eressea.de/view.php?id=2685 --- res/eressea/races.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/eressea/races.xml b/res/eressea/races.xml index 106ff1cce..b8d8c5139 100644 --- a/res/eressea/races.xml +++ b/res/eressea/races.xml @@ -812,7 +812,7 @@ - + @@ -903,7 +903,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes"> - + From 27653a4094f8265a0882e7bfe3591a893e26c28d Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 26 Jul 2020 12:59:49 +0200 Subject: [PATCH 31/52] fix parser crash from turn 1179 --- src/util/parser.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/util/parser.c b/src/util/parser.c index e0f6aeb67..7e2ac665e 100644 --- a/src/util/parser.c +++ b/src/util/parser.c @@ -147,7 +147,7 @@ char *parse_token(const char **str, char *lbuf, size_t buflen) char *cursor = lbuf; char quotechar = 0; bool escape = false; - const char *ctoken = *str; + const char *ctoken = *str, *cstart; if (!ctoken) { return 0; @@ -159,6 +159,7 @@ char *parse_token(const char **str, char *lbuf, size_t buflen) } return 0; } + cstart = ctoken; while (*ctoken) { wint_t wc; size_t len; @@ -190,7 +191,7 @@ char *parse_token(const char **str, char *lbuf, size_t buflen) ++ctoken; break; } - else if (quotechar == 0) { + else if (quotechar == 0 && cstart == ctoken) { quotechar = utf8_character; ++ctoken; } From ad96016f69884307692ed65b0e654e8a01e3a4d1 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 26 Jul 2020 18:49:32 +0200 Subject: [PATCH 32/52] test for new parser behavior. --- src/util/parser.test.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/util/parser.test.c b/src/util/parser.test.c index 7690a01b4..27ce4e918 100644 --- a/src/util/parser.test.c +++ b/src/util/parser.test.c @@ -33,10 +33,48 @@ static void test_parse_token_bug_2381(CuTest *tc) { char token[64]; stok = s; - stok = parse_token(&stok, token, sizeof(token)); + parse_token(&stok, token, sizeof(token)); CuAssertTrue(tc, strlen(token) < sizeof(token)); } +static void test_parse_token_quotes(CuTest *tc) { + const char *stok, *s = "There are 'exactly four' \"tokens\""; + char token[64], *tok; + + stok = s; + tok = parse_token(&stok, token, sizeof(token)); + CuAssertStrEquals(tc, "There", token); + tok = parse_token(&stok, token, sizeof(token)); + CuAssertStrEquals(tc, "are", tok); + tok = parse_token(&stok, token, sizeof(token)); + CuAssertStrEquals(tc, "exactly four", tok); + tok = parse_token(&stok, token, sizeof(token)); + CuAssertStrEquals(tc, "tokens", tok); + tok = parse_token(&stok, token, sizeof(token)); + CuAssertPtrEquals(tc, NULL, (void *)tok); + CuAssertStrEquals(tc, "", token); +} + +static void test_parse_token_quote_bug_turn_1179(CuTest *tc) { + const char *stok, *s = "O'Leary and \"O'Hara\" \"'nuff\" said'"; + char token[64], *tok; + + stok = s; + tok = parse_token(&stok, token, sizeof(token)); + CuAssertStrEquals(tc, "O'Leary", tok); + tok = parse_token(&stok, token, sizeof(token)); + CuAssertStrEquals(tc, "and", tok); + tok = parse_token(&stok, token, sizeof(token)); + CuAssertStrEquals(tc, "O'Hara", tok); + tok = parse_token(&stok, token, sizeof(token)); + CuAssertStrEquals(tc, "'nuff", tok); + tok = parse_token(&stok, token, sizeof(token)); + CuAssertStrEquals(tc, "said'", tok); + tok = parse_token(&stok, token, sizeof(token)); + CuAssertPtrEquals(tc, NULL, (void *)tok); + CuAssertStrEquals(tc, "", token); +} + static void test_parse_token_limit(CuTest *tc) { char lbuf[8]; const char *tok; @@ -126,6 +164,8 @@ CuSuite *get_parser_suite(void) SUITE_ADD_TEST(suite, test_atoip); SUITE_ADD_TEST(suite, test_skip_token); SUITE_ADD_TEST(suite, test_parse_token); + SUITE_ADD_TEST(suite, test_parse_token_quotes); + SUITE_ADD_TEST(suite, test_parse_token_quote_bug_turn_1179); SUITE_ADD_TEST(suite, test_parse_token_bug_2381); SUITE_ADD_TEST(suite, test_parse_token_limit); SUITE_ADD_TEST(suite, test_parse_token_limit_utf8); From ac3060ef1ae8d5ed2cc4bf50ebaf8d6b37fc41f7 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 26 Jul 2020 21:24:58 +0200 Subject: [PATCH 33/52] bug 2660 delayed canibalism monsters now eat each other in randenc. --- src/monsters.c | 17 +++++++++-------- src/monsters.h | 3 ++- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/monsters.c b/src/monsters.c index 129c8f76c..bd156238b 100644 --- a/src/monsters.c +++ b/src/monsters.c @@ -205,8 +205,13 @@ void monsters_desert(struct faction *monsters) unit *u; for (u = r->units; u; u = u->next) { - if (u->faction != monsters - && (u_race(u)->flags & RCF_DESERT)) { + if (u->faction == monsters) { + const struct race * rc = u_race(u); + if (rc->splitsize < 10) { + /* hermit-type monsters eat each other */ + monster_cannibalism(u); + } + } else if (u_race(u)->flags & RCF_DESERT) { if (fval(u, UFL_ISNEW)) continue; if (rng_int() % 100 < 5) { @@ -752,7 +757,8 @@ static order *plan_dragon(unit * u) return long_order; } -static void monster_cannibalism(unit *u) { +void monster_cannibalism(unit *u) +{ unit *u2; for (u2 = u->next; u2; u2 = u2->next) { @@ -792,11 +798,6 @@ void plan_monsters(faction * f) } a_removeall(&u->attribs, &at_otherfaction); - if (rc->splitsize < 10) { - /* hermit-type monsters eat each other */ - monster_cannibalism(u); - } - if (skill_enabled(SK_PERCEPTION)) { /* Monster bekommen jede Runde ein paar Tage Wahrnehmung dazu */ produceexp(u, SK_PERCEPTION, u->number); diff --git a/src/monsters.h b/src/monsters.h index d87398c2c..7b3cda142 100644 --- a/src/monsters.h +++ b/src/monsters.h @@ -15,7 +15,8 @@ extern "C" { struct unit *spawn_seaserpent(struct region *r, struct faction *f); void spawn_dragons(void); - void monsters_desert(struct faction *monsters); + void monsters_desert(const struct faction *monsters); + void monster_cannibalism(struct unit *u); void monster_kills_peasants(struct unit *u); void make_zombie(struct unit * u); From 5615dda22213e06f36035423d1d8acbf69eb5f3d Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 26 Jul 2020 21:30:27 +0200 Subject: [PATCH 34/52] small bug: argument is not const. --- src/monsters.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/monsters.h b/src/monsters.h index 7b3cda142..c44ef1a00 100644 --- a/src/monsters.h +++ b/src/monsters.h @@ -15,7 +15,7 @@ extern "C" { struct unit *spawn_seaserpent(struct region *r, struct faction *f); void spawn_dragons(void); - void monsters_desert(const struct faction *monsters); + void monsters_desert(struct faction *monsters); void monster_cannibalism(struct unit *u); void monster_kills_peasants(struct unit *u); From df036d80547cad6c5f3063d8c19c6b16296b52f4 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 26 Jul 2020 22:45:03 +0200 Subject: [PATCH 35/52] Bug 2621: Fehlermeldung bei fehlender Anzahl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bei Personen- und Schiffsübergaben. closing https://bugs.eressea.de/view.php?id=2621 --- res/core/messages.xml | 8 ++++++++ res/translations/messages.de.po | 5 ++++- res/translations/messages.en.po | 3 +++ res/translations/strings.de.po | 6 ++++++ res/translations/strings.en.po | 6 ++++++ src/give.c | 10 ++++++++++ 6 files changed, 37 insertions(+), 1 deletion(-) diff --git a/res/core/messages.xml b/res/core/messages.xml index f505d2307..4a0212557 100644 --- a/res/core/messages.xml +++ b/res/core/messages.xml @@ -4982,6 +4982,14 @@ + + + + + + + + diff --git a/res/translations/messages.de.po b/res/translations/messages.de.po index abbf79811..3e4725974 100644 --- a/res/translations/messages.de.po +++ b/res/translations/messages.de.po @@ -1389,7 +1389,7 @@ msgid "earthquake_effect" msgstr "\"$unit($mage) läßt die Erde in $region($region) erzittern.\"" msgid "error56" -msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Einheit kann soviele Pferde nicht bändigen.\"" +msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Einheit kann so viele Pferde nicht bändigen.\"" msgid "error78" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Ein Fluch verhindert die Übergabe.\"" @@ -2777,6 +2777,9 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - So viele Schiff msgid "error328" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Dafür müssen die Schiffe an derselben Küste liegen.\"" +msgid "give_number_missing" +msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Anzahl der $localize($resource) fehlt.\"" + msgid "error326" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Diese Schiffe können keinen Konvoi bilden.\"" diff --git a/res/translations/messages.en.po b/res/translations/messages.en.po index 013986fbc..49fbdcb3d 100644 --- a/res/translations/messages.en.po +++ b/res/translations/messages.en.po @@ -2780,6 +2780,9 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - The ship is und msgid "error328" msgstr "\"$unit($unit) in $region($region): '$order($command)' - All ships of a convoy must be on the same coast.\"" +msgid "give_number_missing" +msgstr "\"$unit($unit) in $region($region): '$order($command)' - Missign number of $localize($resource,0).\"" + msgid "dissolve_units_2" msgstr "\"$unit($unit) in $region($region): $int($number) $race($race,$number) turned into $if($eq($number,1),\"a tree\", \"trees\").\"" diff --git a/res/translations/strings.de.po b/res/translations/strings.de.po index 4a55e8ab7..70d90a863 100644 --- a/res/translations/strings.de.po +++ b/res/translations/strings.de.po @@ -1029,6 +1029,12 @@ msgstr "ein Boot" msgid "boat_p" msgstr "Boote" +msgid "ship" +msgstr "Schiff" + +msgid "ship_p" +msgstr "Schiffe" + msgctxt "race" msgid "nymph" msgstr "Nymphe" diff --git a/res/translations/strings.en.po b/res/translations/strings.en.po index 6375c5e96..ff7ecb01f 100644 --- a/res/translations/strings.en.po +++ b/res/translations/strings.en.po @@ -838,6 +838,12 @@ msgstr "a boat" msgid "boat_p" msgstr "boats" +msgid "ship" +msgstr "ship" + +msgid "ship_p" +msgstr "ships" + msgctxt "race" msgid "orc_p" msgstr "orcs" diff --git a/src/give.c b/src/give.c index d6813abd3..0338fa590 100644 --- a/src/give.c +++ b/src/give.c @@ -836,6 +836,16 @@ void give_cmd(unit * u, order * ord) /* handled in give_control_cmd */ return; } + else if (p == P_SHIP) { + ADDMSG(&u->faction->msgs, + msg_feedback(u, ord, "give_number_missing", "resource", "ship_p")); + return; + } + else if (p == P_PERSON) { + ADDMSG(&u->faction->msgs, + msg_feedback(u, ord, "give_number_missing", "resource", "person_p")); + return; + } if (err == GET_NOTFOUND || (err != GET_PEASANTS && !can_give_to(u, u2))) { ADDMSG(&u->faction->msgs, From c0babd46780264fe00038c3ee20cf3c19aa79db0 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 29 Jul 2020 19:00:52 +0200 Subject: [PATCH 36/52] report 2682: simplify tree growth each spring, 2/3 of all saplings become trees over a 6 week period (1/9 per week). --- src/laws.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/laws.c b/src/laws.c index 458d7b660..731e46cea 100644 --- a/src/laws.c +++ b/src/laws.c @@ -681,8 +681,8 @@ growing_trees(region * r, const season_t current_season, const season_t last_wee a = a_find(r->attribs, &at_germs); if (!a) { a = a_add(&r->attribs, a_new(&at_germs)); - a->data.sa[0] = (short)cap_int(rtrees(r, 0), 0, SHRT_MAX); - a->data.sa[1] = (short)cap_int(rtrees(r, 1), 0, SHRT_MAX); + a->data.sa[0] = (short)cap_int(rtrees(r, 0) / 9, 0, SHRT_MAX); + a->data.sa[1] = (short)cap_int(rtrees(r, 1) / 9, 0, SHRT_MAX); } else if (a->data.sa[0] < 0 || a->data.sa[1] < 0) { a->data.sa[0] = (short)cap_int(a->data.sa[0], 0, SHRT_MAX); @@ -692,9 +692,7 @@ growing_trees(region * r, const season_t current_season, const season_t last_wee /* Baumwachstum */ sprout = rtrees(r, 1); if (sprout > a->data.sa[1]) sprout = a->data.sa[1]; - grownup_trees = sprout / 6; - /* aus dem Sproesslingepool dieses Jahres abziehen */ - a->data.sa[1] = (short)(sprout - grownup_trees); + grownup_trees = sprout; /* aus dem gesamt Sproesslingepool abziehen */ rsettrees(r, 1, rtrees(r, 1) - grownup_trees); /* zu den Baeumen hinzufuegen */ @@ -703,13 +701,10 @@ growing_trees(region * r, const season_t current_season, const season_t last_wee /* Samenwachstum */ seeds = rtrees(r, 0); if (seeds > a->data.sa[0]) seeds = a->data.sa[0]; - sprout = seeds / 6; - /* aus dem Samenpool dieses Jahres abziehen */ - a->data.sa[0] = (short)(seeds - sprout); /* aus dem gesamt Samenpool abziehen */ - rsettrees(r, 0, rtrees(r, 0) - sprout); + rsettrees(r, 0, rtrees(r, 0) - seeds); /* zu den Sproesslinge hinzufuegen */ - rsettrees(r, 1, rtrees(r, 1) + sprout); + rsettrees(r, 1, rtrees(r, 1) + seeds); } } From a5f457a62d36a12b1f7bec43635a1b687b621fc0 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 30 Jul 2020 13:25:55 +0200 Subject: [PATCH 37/52] tree growth is rounded up. --- src/laws.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/laws.c b/src/laws.c index 731e46cea..99a61781e 100644 --- a/src/laws.c +++ b/src/laws.c @@ -681,8 +681,8 @@ growing_trees(region * r, const season_t current_season, const season_t last_wee a = a_find(r->attribs, &at_germs); if (!a) { a = a_add(&r->attribs, a_new(&at_germs)); - a->data.sa[0] = (short)cap_int(rtrees(r, 0) / 9, 0, SHRT_MAX); - a->data.sa[1] = (short)cap_int(rtrees(r, 1) / 9, 0, SHRT_MAX); + a->data.sa[0] = (short)cap_int((8 + rtrees(r, 0)) / 9, 0, SHRT_MAX); + a->data.sa[1] = (short)cap_int((8 + rtrees(r, 1)) / 9, 0, SHRT_MAX); } else if (a->data.sa[0] < 0 || a->data.sa[1] < 0) { a->data.sa[0] = (short)cap_int(a->data.sa[0], 0, SHRT_MAX); From 83ecf3902946cc7e9ed065dc2bd4b5d44797a12f Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 4 Aug 2020 19:00:04 +0200 Subject: [PATCH 38/52] spotting monsters from lighthouses --- src/reports.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/reports.c b/src/reports.c index 4c3242df8..c362d2b0e 100644 --- a/src/reports.c +++ b/src/reports.c @@ -2369,8 +2369,10 @@ bool visible_unit(const unit *u, const faction *f, int stealthmod, seen_mode mod return true; } else { - if (stealthmod > INT_MIN && mode >= seen_lighthouse) { - if (mode != seen_travel || u->building || u->ship || is_guard(u)) { + if (stealthmod > INT_MIN) { + if (mode == seen_lighthouse) { + return true; + } else if (mode > seen_travel || u->building || u->ship || is_guard(u)) { return cansee(f, u->region, u, stealthmod); } } From dff03641875f087592420ed3c2a2804125078dea Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 4 Aug 2020 19:07:25 +0200 Subject: [PATCH 39/52] quick performance improvement for scale_number --- src/kernel/unit.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/kernel/unit.c b/src/kernel/unit.c index 6b4cbe2b0..e011d6ec5 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -974,6 +974,12 @@ void remove_skill(unit * u, skill_t sk) } } +static void remove_skills(unit * u) { + free(u->skills); + u->skills = NULL; + u->skill_size = 0; +} + skill *add_skill(unit * u, skill_t sk) { skill *sv; @@ -1587,10 +1593,7 @@ void scale_number(unit * u, int n) } } if (u->number == 0 || n == 0) { - skill_t sk; - for (sk = 0; sk < MAXSKILLS; sk++) { - remove_skill(u, sk); - } + remove_skills(u); } set_number(u, n); From 5cdabd3b8cc7efed00bb7153c8af74862f709791 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 4 Aug 2020 19:10:05 +0200 Subject: [PATCH 40/52] update test for lighthouse visibility --- src/reports.test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reports.test.c b/src/reports.test.c index 0a85b75d2..68a37e769 100644 --- a/src/reports.test.c +++ b/src/reports.test.c @@ -871,7 +871,7 @@ static void test_visible_unit(CuTest *tc) { CuAssertTrue(tc, !visible_unit(u, f, 0, seen_lighthouse_land)); CuAssertTrue(tc, visible_unit(u, f, 0, seen_lighthouse)); - CuAssertTrue(tc, !visible_unit(u, f, -2, seen_lighthouse)); + CuAssertTrue(tc, visible_unit(u, f, -2, seen_lighthouse)); u->ship = sh = test_create_ship(u->region, NULL); CuAssertTrue(tc, visible_unit(u, f, -2, seen_travel)); From 99998472b043f4ff69e5df761ad3ed88e2333dde Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 5 Aug 2020 16:29:20 +0200 Subject: [PATCH 41/52] Lighthouse: only show large swimming units (more than 10 weight). Eliminate RCF_INVISIBLE and the spell race. --- res/e3a/races.xml | 6 +----- res/eressea/races.xml | 4 ---- src/creport.c | 3 --- src/kernel/race.h | 3 +-- src/kernel/unit.c | 6 ++---- src/kernel/unit.test.c | 10 ---------- src/laws.c | 23 +++++++++++------------ src/report.c | 7 ++----- src/reports.c | 2 +- src/reports.test.c | 3 ++- 10 files changed, 20 insertions(+), 47 deletions(-) diff --git a/res/e3a/races.xml b/res/e3a/races.xml index 197a2f1d1..d039b067d 100644 --- a/res/e3a/races.xml +++ b/res/e3a/races.xml @@ -400,7 +400,7 @@ - + @@ -678,10 +678,6 @@ - - - - diff --git a/res/eressea/races.xml b/res/eressea/races.xml index b8d8c5139..ac182a2f8 100644 --- a/res/eressea/races.xml +++ b/res/eressea/races.xml @@ -675,10 +675,6 @@ - - - - diff --git a/src/creport.c b/src/creport.c index eaa19e186..18957447b 100644 --- a/src/creport.c +++ b/src/creport.c @@ -745,9 +745,6 @@ void cr_output_unit(stream *out, const faction * f, assert(u && u->number); - if (fval(u_race(u), RCF_INVISIBLE)) - return; - stream_printf(out, "EINHEIT %d\n", u->no); stream_printf(out, "\"%s\";Name\n", unit_getname(u)); str = u_description(u, lang); diff --git a/src/kernel/race.h b/src/kernel/race.h index 8918437e1..a034fd666 100644 --- a/src/kernel/race.h +++ b/src/kernel/race.h @@ -212,11 +212,10 @@ extern "C" { #define RCF_COASTAL (1<<22) /* kann in Landregionen an der Kueste sein */ #define RCF_UNARMEDGUARD (1<<23) /* kann ohne Waffen bewachen */ #define RCF_CANSAIL (1<<24) /* Einheit darf Schiffe betreten */ -#define RCF_INVISIBLE (1<<25) /* not visible in any report */ +#define RCF_FAMILIAR (1<<25) /* may be a familiar */ #define RCF_SHIPSPEED (1<<26) /* race gets +1 on shipspeed */ #define RCF_ATTACK_MOVED (1<<27) /* may attack if it has moved */ #define RCF_MIGRANTS (1<<28) /* may have migrant units (human bonus) */ -#define RCF_FAMILIAR (1<<29) /* may be a familiar */ #define RCF_DEFAULT RCF_CANSAIL diff --git a/src/kernel/unit.c b/src/kernel/unit.c index e011d6ec5..3ebd243bb 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -937,9 +937,7 @@ void u_setfaction(unit * u, faction * f) bool count_unit(const unit *u) { - const race *rc = u_race(u); - /* spells are invisible. units we cannot see do not count to our limit */ - return rc == NULL || (rc->flags & RCF_INVISIBLE) == 0; + return u_race(u) != NULL; } void set_number(unit * u, int count) @@ -948,7 +946,7 @@ void set_number(unit * u, int count) assert(count <= UNIT_MAXSIZE); if (count == 0) { - u->flags &= ~(UFL_HERO); + u->flags &= ~UFL_HERO; } if (u->faction && count_unit(u)) { u->faction->num_people += count - u->number; diff --git a/src/kernel/unit.test.c b/src/kernel/unit.test.c index a5b0ec89d..75fd9ea08 100644 --- a/src/kernel/unit.test.c +++ b/src/kernel/unit.test.c @@ -520,14 +520,11 @@ static void test_heal_factor(CuTest *tc) { } static void test_unlimited_units(CuTest *tc) { - race *rc; faction *f; unit *u; test_setup(); f = test_create_faction(NULL); - rc = test_create_race("spell"); - rc->flags |= RCF_INVISIBLE; CuAssertIntEquals(tc, 0, f->num_units); CuAssertIntEquals(tc, 0, f->num_people); u = test_create_unit(f, test_create_region(0, 0, NULL)); @@ -540,14 +537,7 @@ static void test_unlimited_units(CuTest *tc) { u_setfaction(u, f); CuAssertIntEquals(tc, 1, f->num_units); CuAssertIntEquals(tc, 1, f->num_people); - u_setrace(u, rc); - CuAssertTrue(tc, !count_unit(u)); - CuAssertIntEquals(tc, 0, f->num_units); - CuAssertIntEquals(tc, 0, f->num_people); scale_number(u, 10); - CuAssertIntEquals(tc, 0, f->num_units); - CuAssertIntEquals(tc, 0, f->num_people); - u_setrace(u, f->race); CuAssertIntEquals(tc, 1, f->num_units); CuAssertIntEquals(tc, 10, f->num_people); remove_unit(&u->region->units, u); diff --git a/src/laws.c b/src/laws.c index 99a61781e..4b98d32c1 100644 --- a/src/laws.c +++ b/src/laws.c @@ -4060,31 +4060,30 @@ void turn_end(void) update_spells(); } -/** determine if unit can be seen by faction +/** + * Determine if unit can be seen by faction. + * * @param f -- the observiong faction * @param u -- the unit that is observed * @param r -- the region that u is obesrved from (see below) * @param m -- terrain modifier to stealth - * + * * r kann != u->region sein, wenn es um Durchreisen geht, * oder Zauber (sp_generous, sp_fetchastral). * Es muss auch niemand aus f in der region sein, wenn sie vom Turm - * erblickt wird */ -bool -cansee(const faction * f, const region * r, const unit * u, int modifier) + * erblickt wird. + */ +bool cansee(const faction * f, const region * r, const unit * u, int modifier) { int stealth, rings; if (u->faction == f || omniscient(f)) { return true; } - else if (fval(u_race(u), RCF_INVISIBLE)) { - return false; - } else if (u->number == 0) { attrib *a = a_find(u->attribs, &at_creator); - if (a) { /* u is an empty temporary unit. In this special case - we look at the creating unit. */ + if (a) { + /* u is an empty temporary unit. In this special case we look at the creating unit. */ u = (unit *)a->data.v; } else { @@ -4124,7 +4123,7 @@ cansee(const faction * f, const region * r, const unit * u, int modifier) bool cansee_unit(const unit * u, const unit * target, int modifier) /* target->region kann != u->region sein, wenn es um durchreisen geht */ { - if (fval(u_race(target), RCF_INVISIBLE) || target->number == 0) + if (target->number == 0) return false; else if (target->faction == u->faction) return true; @@ -4167,7 +4166,7 @@ cansee_durchgezogen(const faction * f, const region * r, const unit * u, { unit *u2; - if (fval(u_race(u), RCF_INVISIBLE) || u->number == 0) + if (u->number == 0) return false; else if (u->faction == f) return true; diff --git a/src/report.c b/src/report.c index ab25b79b2..f283b552f 100644 --- a/src/report.c +++ b/src/report.c @@ -627,9 +627,6 @@ nr_unit(struct stream *out, const faction * f, const unit * u, int indent, seen_ bool isbattle = (mode == seen_battle); char buf[8192]; - if (fval(u_race(u), RCF_INVISIBLE)) - return; - newline(out); dh = bufunit_depr(f, u, mode, buf, sizeof(buf)); @@ -1231,7 +1228,7 @@ static void report_statistics(struct stream *out, const region * r, const factio /* count */ for (number = 0, u = r->units; u; u = u->next) { - if (u->faction == f && !fval(u_race(u), RCF_INVISIBLE)) { + if (u->faction == f) { for (itm = u->items; itm; itm = itm->next) { i_change(&items, itm->type, itm->number); } @@ -1321,7 +1318,7 @@ report_template(const char *filename, report_context * ctx, const char *bom) continue; for (u = r->units; u; u = u->next) { - if (u->faction == f && !fval(u_race(u), RCF_INVISIBLE)) { + if (u->faction == f) { order *ord; if (!dh) { plane *pl = getplane(r); diff --git a/src/reports.c b/src/reports.c index c362d2b0e..547351824 100644 --- a/src/reports.c +++ b/src/reports.c @@ -2371,7 +2371,7 @@ bool visible_unit(const unit *u, const faction *f, int stealthmod, seen_mode mod else { if (stealthmod > INT_MIN) { if (mode == seen_lighthouse) { - return true; + return u_race(u)->weight > 10000; } else if (mode > seen_travel || u->building || u->ship || is_guard(u)) { return cansee(f, u->region, u, stealthmod); } diff --git a/src/reports.test.c b/src/reports.test.c index 68a37e769..50dc08704 100644 --- a/src/reports.test.c +++ b/src/reports.test.c @@ -870,7 +870,8 @@ static void test_visible_unit(CuTest *tc) { CuAssertTrue(tc, !visible_unit(u, f, 0, seen_neighbour)); CuAssertTrue(tc, !visible_unit(u, f, 0, seen_lighthouse_land)); - CuAssertTrue(tc, visible_unit(u, f, 0, seen_lighthouse)); + CuAssertTrue(tc, !visible_unit(u, f, -2, seen_lighthouse)); + rc->weight = 11000; CuAssertTrue(tc, visible_unit(u, f, -2, seen_lighthouse)); u->ship = sh = test_create_ship(u->region, NULL); From 4b960462bd38ddc762c4e65d69aa06a31b507e00 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 5 Aug 2020 16:33:00 +0200 Subject: [PATCH 42/52] Reduce weight limit, lighthouses also show all dragons. --- src/reports.c | 2 +- src/reports.test.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/reports.c b/src/reports.c index 547351824..55ecdafdd 100644 --- a/src/reports.c +++ b/src/reports.c @@ -2371,7 +2371,7 @@ bool visible_unit(const unit *u, const faction *f, int stealthmod, seen_mode mod else { if (stealthmod > INT_MIN) { if (mode == seen_lighthouse) { - return u_race(u)->weight > 10000; + return u_race(u)->weight >= 5000; } else if (mode > seen_travel || u->building || u->ship || is_guard(u)) { return cansee(f, u->region, u, stealthmod); } diff --git a/src/reports.test.c b/src/reports.test.c index 50dc08704..bcf644ebc 100644 --- a/src/reports.test.c +++ b/src/reports.test.c @@ -871,7 +871,7 @@ static void test_visible_unit(CuTest *tc) { CuAssertTrue(tc, !visible_unit(u, f, 0, seen_lighthouse_land)); CuAssertTrue(tc, !visible_unit(u, f, -2, seen_lighthouse)); - rc->weight = 11000; + rc->weight = 5000; CuAssertTrue(tc, visible_unit(u, f, -2, seen_lighthouse)); u->ship = sh = test_create_ship(u->region, NULL); From aeca118eae68a1fb479a355ebad5ac3016ec4d3f Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 6 Aug 2020 14:48:30 +0200 Subject: [PATCH 43/52] https://bugs.eressea.de/view.php?id=2689 Implement a working LOCALE command. OBS: Throws away any command that isn't easy to translate. --- conf/keywords.json | 2 ++ res/translations/strings.de.po | 4 +++ res/translations/strings.en.po | 4 +++ src/kernel/faction.c | 15 +++++++++++ src/kernel/faction.h | 1 + src/kernel/faction.test.c | 49 ++++++++++++++++++++++++++++++++++ src/kernel/order.c | 3 +-- src/kernel/unit.c | 43 +++++++++++++++++++++++++++++ src/kernel/unit.h | 2 ++ src/laws.c | 18 +++++++++++++ src/laws.h | 1 + src/util/keyword.c | 1 + src/util/keyword.h | 1 + 13 files changed, 142 insertions(+), 2 deletions(-) diff --git a/conf/keywords.json b/conf/keywords.json index 11376ea95..9cee06a6d 100644 --- a/conf/keywords.json +++ b/conf/keywords.json @@ -4,6 +4,7 @@ "plant": "PLANT", "grow": [ "GROW", "BREED" ], "promote": ["PROMOTE", "PROMOTION" ], + "locale": ["LANGUAGE", "LOCALE"], "combat": [ "COMBAT", "FIGHT" ] }, "de": { @@ -65,6 +66,7 @@ "alliance": "ALLIANZ", "claim": ["BEANSPRUCHE", "BEANSPRUCHEN"], "promote": ["BEFÖRDERE", "BEFÖRDERUNG"], + "locale": ["SPRACHE", "LOCALE"], "pay": ["BEZAHLE", "BEZAHLEN"] } } diff --git a/res/translations/strings.de.po b/res/translations/strings.de.po index 70d90a863..6a03cce5a 100644 --- a/res/translations/strings.de.po +++ b/res/translations/strings.de.po @@ -4806,6 +4806,10 @@ msgctxt "keyword" msgid "promote" msgstr "BEFÖRDERE" +msgctxt "keyword" +msgid "locale" +msgstr "SPRACHE" + msgid "skeleton_prefix_0" msgstr "Klapperige" diff --git a/res/translations/strings.en.po b/res/translations/strings.en.po index ff7ecb01f..f66058624 100644 --- a/res/translations/strings.en.po +++ b/res/translations/strings.en.po @@ -4304,6 +4304,10 @@ msgctxt "keyword" msgid "promote" msgstr "PROMOTE" +msgctxt "keyword" +msgid "locale" +msgstr "LANGUAGE" + msgid "stone" msgstr "stone" diff --git a/src/kernel/faction.c b/src/kernel/faction.c index c17f4662c..03026a7bd 100755 --- a/src/kernel/faction.c +++ b/src/kernel/faction.c @@ -835,3 +835,18 @@ faction *faction_create(int no) fhash(f); return f; } + +void change_locale(faction *f, const struct locale *lang) { + unit *ux; + for (ux = f->units; ux; ux = ux->nextF) { + translate_orders(ux, lang, &ux->orders); + if (ux->old_orders) { + translate_orders(ux, lang, &ux->old_orders); + } + if (ux->thisorder) { + translate_orders(ux, lang, &ux->thisorder); + } + } + f->locale = lang; +} + diff --git a/src/kernel/faction.h b/src/kernel/faction.h index 0fcb2ff4a..94835c33e 100644 --- a/src/kernel/faction.h +++ b/src/kernel/faction.h @@ -155,6 +155,7 @@ extern "C" { int count_migrants(const struct faction * f); int count_maxmigrants(const struct faction * f); int max_magicians(const struct faction * f); + void change_locale(struct faction *f, const struct locale *lang); #define MONSTER_ID 666 struct faction *getfaction(void); diff --git a/src/kernel/faction.test.c b/src/kernel/faction.test.c index 2b7d97a5a..21b9b5fad 100644 --- a/src/kernel/faction.test.c +++ b/src/kernel/faction.test.c @@ -135,11 +135,59 @@ static void test_addfaction(CuTest *tc) { static void test_check_passwd(CuTest *tc) { faction *f; + test_setup(); f = test_create_faction(NULL); faction_setpassword(f, password_hash("password", PASSWORD_DEFAULT)); CuAssertTrue(tc, checkpasswd(f, "password")); CuAssertTrue(tc, !checkpasswd(f, "assword")); CuAssertTrue(tc, !checkpasswd(f, "PASSWORD")); + test_teardown(); +} + +static void test_change_locale(CuTest *tc) { + faction *f; + unit *u; + order *ord; + struct locale *lang; + + test_setup(); + f = test_create_faction(NULL); + lang = get_or_create_locale("en"); + u = test_create_unit(f, test_create_plain(0, 0)); + u->thisorder = create_order(K_ENTERTAIN, f->locale, NULL); + + u->old_orders = create_order(K_WORK, f->locale, NULL); + u->old_orders->next = ord = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ALCHEMY]); + CuAssertIntEquals(tc, SK_ALCHEMY - 100, ord->id); + ord->next = create_order(K_GIVE, f->locale, "abcd 1 Schwert"); + + unit_addorder(u, create_order(K_NAME, f->locale, "EINHEIT Hodor")); + unit_addorder(u, create_order(K_ENTERTAIN, f->locale, NULL)); + unit_addorder(u, create_order(K_KOMMENTAR, f->locale, "ich bin kein Tintenfisch")); + unit_addorder(u, ord = create_order(K_STUDY, f->locale, skillnames[SK_ENTERTAINMENT])); + CuAssertIntEquals(tc, SK_ENTERTAINMENT - 100, ord->id); + + change_locale(f, lang); + CuAssertPtrEquals(tc, lang, (void *)f->locale); + CuAssertPtrNotNull(tc, u->thisorder); + + CuAssertPtrNotNull(tc, ord = u->old_orders); + CuAssertIntEquals(tc, K_WORK, ord->command); + CuAssertPtrNotNull(tc, ord = ord->next); + CuAssertIntEquals(tc, K_AUTOSTUDY, ord->command); + CuAssertIntEquals(tc, SK_ALCHEMY - 100, ord->id); + CuAssertPtrEquals(tc, NULL, ord->next); + + CuAssertPtrNotNull(tc, ord = u->orders); + CuAssertIntEquals(tc, K_ENTERTAIN, ord->command); + CuAssertPtrNotNull(tc, ord = ord->next); + CuAssertIntEquals(tc, K_KOMMENTAR, ord->command); + CuAssertPtrNotNull(tc, ord = ord->next); + CuAssertIntEquals(tc, K_STUDY, ord->command); + CuAssertIntEquals(tc, SK_ENTERTAINMENT - 100, ord->id); + CuAssertPtrEquals(tc, NULL, ord->next); + + test_teardown(); } static void test_get_monsters(CuTest *tc) { @@ -366,6 +414,7 @@ CuSuite *get_faction_suite(void) SUITE_ADD_TEST(suite, test_set_origin); SUITE_ADD_TEST(suite, test_set_origin_bug); SUITE_ADD_TEST(suite, test_check_passwd); + SUITE_ADD_TEST(suite, test_change_locale); SUITE_ADD_TEST(suite, test_valid_race); SUITE_ADD_TEST(suite, test_set_email); SUITE_ADD_TEST(suite, test_dbstrings); diff --git a/src/kernel/order.c b/src/kernel/order.c index 056722294..96626bfc0 100644 --- a/src/kernel/order.c +++ b/src/kernel/order.c @@ -213,7 +213,7 @@ int stream_order(struct stream *out, const struct order *ord, const struct local void free_order(order * ord) { if (ord != NULL) { - assert(ord->next == 0); + assert(ord->next == NULL); free(ord); } } @@ -641,4 +641,3 @@ void close_orders(void) { (void)init_order(NULL, NULL); } } - diff --git a/src/kernel/unit.c b/src/kernel/unit.c index 3ebd243bb..67a8f203b 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -1848,3 +1848,46 @@ void unit_convert_race(unit *u, const race *rc, const char *rcname) } } + +void translate_orders(unit *u, const struct locale *lang, order **list) +{ + order **po = list; + (void)lang; + while (*po) { + order *ord = *po; + if (ord->id <= 0) { + /* we can keep these, they have no problematic arguments */ + po = &ord->next; + continue; + } + switch (getkeyword(ord)) { + case K_ATTACK: + case K_BANNER: + case K_DRIVE: + case K_FOLLOW: + case K_GROUP: + case K_KOMMENTAR: + case K_MAIL: + case K_NUMBER: + case K_PASSWORD: + case K_PREFIX: + case K_RECRUIT: + case K_SPY: + case K_STEAL: + case K_TEACH: + case K_TRANSPORT: + case K_URSPRUNG: + /* we can keep these, they need no translation */ + po = &ord->next; + break; + default: + /* we don't know what to do with these, drop them */ + if (u->thisorder == ord) { + u->thisorder = NULL; + } + *po = ord->next; + ord->next = NULL; + free_order(ord); + } + } +} diff --git a/src/kernel/unit.h b/src/kernel/unit.h index 7fc6fa730..934527db1 100644 --- a/src/kernel/unit.h +++ b/src/kernel/unit.h @@ -223,6 +223,8 @@ extern "C" { bool unit_name_equals_race(const struct unit *u); void unit_convert_race(struct unit *u, const struct race *rc, const char *rcname); + void translate_orders(struct unit *u, const struct locale *lang, struct order **list); + /* getunit results: */ #define GET_UNIT 0 #define GET_NOTFOUND 1 diff --git a/src/laws.c b/src/laws.c index 4b98d32c1..14dc6df07 100644 --- a/src/laws.c +++ b/src/laws.c @@ -3990,6 +3990,7 @@ void init_processor(void) p += 10; add_proc_global(p, renumber_factions, "Neue Nummern"); } + add_proc_order(p, K_LOCALE, locale_cmd, 0, "Sprache wechseln"); } static void reset_game(void) @@ -4209,3 +4210,20 @@ seefaction(const faction * f, const region * r, const unit * u, int modifier) return true; return false; } + +int locale_cmd(unit * u, order * ord) +{ + char token[128]; + const char * name; + faction *f = u->faction; + + init_order(ord, f->locale); + name = gettoken(token, sizeof(token)); + if (name) { + const struct locale *lang = get_locale(name); + if (lang && lang != f->locale) { + change_locale(f, lang); + } + } + return 0; +} diff --git a/src/laws.h b/src/laws.h index a49848ac9..bb9ed05dc 100755 --- a/src/laws.h +++ b/src/laws.h @@ -50,6 +50,7 @@ extern "C" { bool long_order_allowed(const struct unit *u); bool password_wellformed(const char *password); + int locale_cmd(struct unit *u, struct order *ord); int password_cmd(struct unit *u, struct order *ord); int banner_cmd(struct unit *u, struct order *ord); int email_cmd(struct unit *u, struct order *ord); diff --git a/src/util/keyword.c b/src/util/keyword.c index f1a59f44a..07b557572 100644 --- a/src/util/keyword.c +++ b/src/util/keyword.c @@ -155,5 +155,6 @@ const char *keywords[MAXKEYWORDS] = { "pay", "loot", "autostudy", + "locale", }; diff --git a/src/util/keyword.h b/src/util/keyword.h index cc78fd6f7..0dcb44ce5 100644 --- a/src/util/keyword.h +++ b/src/util/keyword.h @@ -72,6 +72,7 @@ extern "C" K_PAY, K_LOOT, K_AUTOSTUDY, + K_LOCALE, MAXKEYWORDS, NOKEYWORD } keyword_t; From 7251ef553a115efa324c8d665c7f4c52b33851f5 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Fri, 7 Aug 2020 17:10:42 +0200 Subject: [PATCH 44/52] https://bugs.eressea.de/view.php?id=2658 firewall has no roads or peasants. --- conf/e2/terrains.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/conf/e2/terrains.json b/conf/e2/terrains.json index 5a8cf0280..bb0da7106 100644 --- a/conf/e2/terrains.json +++ b/conf/e2/terrains.json @@ -211,8 +211,6 @@ } }, "firewall": { - "size": 100, - "road": 250, "flags": [ "forbidden" ] }, "fog": { From 52a875da0548a065f393fe46ff27601b21306c7f Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Fri, 7 Aug 2020 23:50:17 +0200 Subject: [PATCH 45/52] https://bugs.eressea.de/view.php?id=2629 make ship_ownernot test u->number, hope that wasn't important. --- src/kernel/ship.c | 6 +++--- src/kernel/ship.test.c | 16 +++++++--------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/kernel/ship.c b/src/kernel/ship.c index 55cddbba8..dd8752b12 100644 --- a/src/kernel/ship.c +++ b/src/kernel/ship.c @@ -514,9 +514,9 @@ unit *ship_owner(const ship * sh) { if (sh->number > 0) { unit *owner = sh->_owner; - if (!owner || (owner->ship != sh || owner->number <= 0)) { - unit * heir = ship_owner_ex(sh, owner ? owner->faction : 0); - return (heir && heir->number > 0) ? heir : 0; + if (!owner || owner->ship != sh) { + unit * heir = ship_owner_ex(sh, owner ? owner->faction : NULL); + return (heir && heir->number > 0) ? heir : NULL; } return owner; } diff --git a/src/kernel/ship.test.c b/src/kernel/ship.test.c index 174762182..1952e60a7 100644 --- a/src/kernel/ship.test.c +++ b/src/kernel/ship.test.c @@ -142,7 +142,7 @@ static void test_shipowner_goes_to_next_when_empty(CuTest * tc) u_set_ship(u, sh); u_set_ship(u2, sh); CuAssertPtrEquals(tc, u, ship_owner(sh)); - u->number = 0; + leave_ship(u); CuAssertPtrEquals(tc, u2, ship_owner(sh)); test_teardown(); } @@ -177,7 +177,7 @@ static void test_shipowner_goes_to_other_when_empty(CuTest * tc) u_set_ship(u, sh); u_set_ship(u2, sh); CuAssertPtrEquals(tc, u, ship_owner(sh)); - u->number = 0; + leave_ship(u); CuAssertPtrEquals(tc, u2, ship_owner(sh)); test_teardown(); } @@ -215,9 +215,9 @@ static void test_shipowner_goes_to_same_faction_when_empty(CuTest * tc) u_set_ship(u2, sh); u_set_ship(u3, sh); CuAssertPtrEquals(tc, u, ship_owner(sh)); - u->number = 0; + leave_ship(u); CuAssertPtrEquals(tc, u3, ship_owner(sh)); - u3->number = 0; + leave_ship(u3); CuAssertPtrEquals(tc, u2, ship_owner(sh)); test_teardown(); } @@ -362,10 +362,8 @@ static void test_shipowner_resets_when_empty(CuTest * tc) CuAssertPtrNotNull(tc, u); u_set_ship(u, sh); CuAssertPtrEquals(tc, u, ship_owner(sh)); - u->number = 0; + leave_ship(u); CuAssertPtrEquals(tc, NULL, ship_owner(sh)); - u->number = 1; - CuAssertPtrEquals(tc, u, ship_owner(sh)); test_teardown(); } @@ -401,12 +399,12 @@ void test_shipowner_goes_to_empty_unit_after_leave(CuTest * tc) u_set_ship(u3, sh); CuAssertPtrEquals(tc, u1, ship_owner(sh)); - u2->number = 0; + leave_ship(u2); leave_ship(u1); CuAssertPtrEquals(tc, u3, ship_owner(sh)); leave_ship(u3); CuAssertPtrEquals(tc, NULL, ship_owner(sh)); - u2->number = 1; + u2->ship = sh; CuAssertPtrEquals(tc, u2, ship_owner(sh)); test_teardown(); } From 81656733315779e72c2b2aea0761560d40c1f200 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 8 Aug 2020 13:05:49 +0200 Subject: [PATCH 46/52] =?UTF-8?q?https://bugs.eressea.de/view.php=3Fid=3D2?= =?UTF-8?q?676=20Neue=20Meldung=20f=C3=BCr=20Konvois=20mit=20zu=20wenigen?= =?UTF-8?q?=20Kapit=C3=A4nen.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- res/core/messages.xml | 8 ++++++++ res/translations/messages.de.po | 3 +++ res/translations/messages.en.po | 3 +++ src/kernel/ship.c | 7 +++++-- src/kernel/ship.h | 2 +- src/kernel/ship.test.c | 22 +++++++++++----------- src/laws.c | 5 +++-- src/move.c | 11 ++++++++--- 8 files changed, 42 insertions(+), 19 deletions(-) diff --git a/res/core/messages.xml b/res/core/messages.xml index 4a0212557..e85787a95 100644 --- a/res/core/messages.xml +++ b/res/core/messages.xml @@ -2753,6 +2753,14 @@ + + + + + + + + diff --git a/res/translations/messages.de.po b/res/translations/messages.de.po index 3e4725974..6039f9481 100644 --- a/res/translations/messages.de.po +++ b/res/translations/messages.de.po @@ -2777,6 +2777,9 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - So viele Schiff msgid "error328" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Dafür müssen die Schiffe an derselben Küste liegen.\"" +msgid "error329" +msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Einheit ist zu klein für einen Konvoi dieser Größe.\"" + msgid "give_number_missing" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Anzahl der $localize($resource) fehlt.\"" diff --git a/res/translations/messages.en.po b/res/translations/messages.en.po index 49fbdcb3d..bd9e7b842 100644 --- a/res/translations/messages.en.po +++ b/res/translations/messages.en.po @@ -2780,6 +2780,9 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - The ship is und msgid "error328" msgstr "\"$unit($unit) in $region($region): '$order($command)' - All ships of a convoy must be on the same coast.\"" +msgid "error329" +msgstr "\"$unit($unit) in $region($region): '$order($command)' - The unit has too few members to sail this convoi.\"" + msgid "give_number_missing" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Missign number of $localize($resource,0).\"" diff --git a/src/kernel/ship.c b/src/kernel/ship.c index dd8752b12..1458953cb 100644 --- a/src/kernel/ship.c +++ b/src/kernel/ship.c @@ -408,9 +408,12 @@ int crew_skill(const ship *sh) { return n; } -bool ship_crewed(const ship *sh) { - unit *u, *cap = ship_owner(sh); +bool ship_crewed(const ship *sh, const unit *cap) { + unit *u; int capskill = -1, sumskill = 0; + if (cap == NULL) { + return false; + } for (u = sh->region->units; u; u = u->next) { if (u->ship == sh) { int es = effskill(u, SK_SAILING, NULL); diff --git a/src/kernel/ship.h b/src/kernel/ship.h index d21f9f3c3..ee45ce2c2 100644 --- a/src/kernel/ship.h +++ b/src/kernel/ship.h @@ -117,7 +117,7 @@ extern "C" { void ship_setname(struct ship *self, const char *name); int shipspeed(const struct ship *sh, const struct unit *u); - bool ship_crewed(const struct ship *sh); + bool ship_crewed(const struct ship *sh, const struct unit *cap); int crew_skill(const struct ship *sh); int ship_captain_minskill(const struct ship *sh); diff --git a/src/kernel/ship.test.c b/src/kernel/ship.test.c index 1952e60a7..9b11d6f72 100644 --- a/src/kernel/ship.test.c +++ b/src/kernel/ship.test.c @@ -45,39 +45,39 @@ static void test_ship_crewed(CuTest * tc) stype->cptskill = 2; stype->sumskill = 4; sh = test_create_ship(r, stype); - CuAssertTrue(tc, !ship_crewed(sh)); + CuAssertTrue(tc, !ship_crewed(sh, NULL)); u1 = test_create_unit(f, r); set_level(u1, SK_SAILING, 4); u_set_ship(u1, sh); - CuAssertTrue(tc, ship_crewed(sh)); + CuAssertTrue(tc, ship_crewed(sh, u1)); u2 = test_create_unit(f, r); set_level(u1, SK_SAILING, 2); set_level(u2, SK_SAILING, 2); u_set_ship(u2, sh); - CuAssertTrue(tc, ship_crewed(sh)); + CuAssertTrue(tc, ship_crewed(sh, u1)); set_level(u1, SK_SAILING, 1); set_level(u2, SK_SAILING, 2); - CuAssertTrue(tc, !ship_crewed(sh)); + CuAssertTrue(tc, !ship_crewed(sh, u1)); set_level(u1, SK_SAILING, 2); set_level(u2, SK_SAILING, 1); - CuAssertTrue(tc, !ship_crewed(sh)); + CuAssertTrue(tc, !ship_crewed(sh, u1)); set_level(u1, SK_SAILING, 3); set_level(u2, SK_SAILING, 1); - CuAssertTrue(tc, ship_crewed(sh)); + CuAssertTrue(tc, ship_crewed(sh, u1)); stype->minskill = 2; - CuAssertTrue(tc, !ship_crewed(sh)); + CuAssertTrue(tc, !ship_crewed(sh, u1)); set_level(u1, SK_SAILING, 2); set_level(u2, SK_SAILING, 2); - CuAssertTrue(tc, ship_crewed(sh)); + CuAssertTrue(tc, ship_crewed(sh, u1)); sh->number = 2; - CuAssertTrue(tc, !ship_crewed(sh)); + CuAssertTrue(tc, !ship_crewed(sh, u1)); set_level(u1, SK_SAILING, 4); set_level(u2, SK_SAILING, 4); - CuAssertTrue(tc, !ship_crewed(sh)); + CuAssertTrue(tc, !ship_crewed(sh, u1)); u1->number = 2; set_level(u1, SK_SAILING, 2); set_level(u2, SK_SAILING, 4); - CuAssertTrue(tc, ship_crewed(sh)); + CuAssertTrue(tc, ship_crewed(sh, u1)); test_teardown(); } diff --git a/src/laws.c b/src/laws.c index 14dc6df07..71ec9da52 100644 --- a/src/laws.c +++ b/src/laws.c @@ -2573,14 +2573,15 @@ void sinkships(struct region * r) if (sh->number > 0) { if (!sh->type->construction || sh->size >= sh->type->construction->maxsize) { + unit *cap = ship_owner(sh); if (fval(r->terrain, SEA_REGION)) { - if (!ship_crewed(sh)) { + if (!ship_crewed(sh, cap)) { /* ship is at sea, but not enough people to control it */ double dmg = config_get_flt("rules.ship.damage.nocrewocean", 0.3); damage_ship(sh, dmg); } } - else if (!ship_owner(sh)) { + else if (!cap) { /* any ship lying around without an owner slowly rots */ double dmg = config_get_flt("rules.ship.damage.nocrew", 0.05); damage_ship(sh, dmg); diff --git a/src/move.c b/src/move.c index 5d5513cd3..3976b79bf 100644 --- a/src/move.c +++ b/src/move.c @@ -819,7 +819,7 @@ static void drifting_ships(region * r) /* Kapitaen da? Beschaedigt? Genuegend Matrosen? * Genuegend leicht? Dann ist alles OK. */ - if (ship_finished(sh) && ship_crewed(sh) && cansail(r, sh)) { + if (ship_finished(sh) && ship_crewed(sh, ship_owner(sh)) && cansail(r, sh)) { shp = &sh->next; continue; } @@ -1630,7 +1630,8 @@ static const region_list *travel_route(unit * u, static bool ship_ready(const region * r, unit * u, order * ord) { - if (!u->ship || u != ship_owner(u->ship)) { + unit *cap = u->ship ? ship_owner(u->ship) : NULL; + if (u != cap) { cmistake(u, ord, 146, MSG_MOVE); return false; } @@ -1640,11 +1641,15 @@ static bool ship_ready(const region * r, unit * u, order * ord) u->ship)); return false; } + if (u->number < u->ship->number) { + cmistake(u, ord, 329, MSG_MOVE); + return false; + } if (!ship_finished(u->ship)) { cmistake(u, ord, 15, MSG_MOVE); return false; } - if (!ship_crewed(u->ship)) { + if (!ship_crewed(u->ship, u)) { cmistake(u, ord, 1, MSG_MOVE); return false; } From 7f148dc010319cc8fd4782ccbbc421986732b2a3 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 8 Aug 2020 15:10:44 +0200 Subject: [PATCH 47/52] https://bugs.eressea.de/view.php?id=2638 flying units may leave a ship on the ocean. --- src/laws.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/laws.c b/src/laws.c index 71ec9da52..bfe13d665 100644 --- a/src/laws.c +++ b/src/laws.c @@ -880,7 +880,7 @@ int leave_cmd(unit * u, struct order *ord) } if (fval(r->terrain, SEA_REGION) && u->ship) { - if (!fval(u_race(u), RCF_SWIM)) { + if (!fval(u_race(u), RCF_SWIM|RCF_FLY)) { cmistake(u, ord, 11, MSG_MOVE); return 0; } From a618b5899a77a233341149466a5950c42e1c454e Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 8 Aug 2020 18:22:12 +0200 Subject: [PATCH 48/52] https://bugs.eressea.de/view.php?id=2572 in e3, goblins cannot use two-handed weapons. --- res/e3a/weapons.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/res/e3a/weapons.xml b/res/e3a/weapons.xml index 3eea6dc44..f993b7ac2 100644 --- a/res/e3a/weapons.xml +++ b/res/e3a/weapons.xml @@ -3,9 +3,9 @@ - + @@ -112,7 +112,7 @@ - + From ad4cb9bea83caba72c8cbca34ddc78f5e42558dd Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 9 Aug 2020 20:56:43 +0200 Subject: [PATCH 49/52] Parser fallback to latin1 when detecting invalid utf-8 --- src/util/parser.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/util/parser.c b/src/util/parser.c index 7e2ac665e..36a4b1565 100644 --- a/src/util/parser.c +++ b/src/util/parser.c @@ -173,8 +173,15 @@ char *parse_token(const char **str, char *lbuf, size_t buflen) else { int ret = unicode_utf8_decode(&wc, ctoken, &len); if (ret != 0) { - log_warning("illegal character sequence in UTF8 string: %s\n", ctoken); - break; + log_info("falling back to ISO-8859-1: %s\n", cstart); + if (cursor - buflen < lbuf - 2) { + size_t inlen = 1; + len = 2; + unicode_latin1_to_utf8(cursor, &len, ctoken, &inlen); + cursor += len; + ctoken += inlen; + continue; + } } } if (escape) { From 31c1b070e82cce1020fe70f847c606873137ec42 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 10 Aug 2020 13:11:19 +0200 Subject: [PATCH 50/52] git pull with rebase --- s/preview | 2 +- s/pull | 5 ----- s/upgrade | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) delete mode 100755 s/pull diff --git a/s/preview b/s/preview index 2fde22cda..717737a02 100755 --- a/s/preview +++ b/s/preview @@ -25,7 +25,7 @@ cd $SOURCE rm -rf tolua git fetch || abort "failed to update source. do you have local changes?" [ -z $1 ] || git checkout $1 -git pull -q +git pull --rebase -q git submodule update s/cmake-init s/build > /dev/null || abort "build failed." diff --git a/s/pull b/s/pull deleted file mode 100755 index fa3167c5b..000000000 --- a/s/pull +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh -if [ ! -z $1 ]; then - git checkout $1 -fi -git pull && git submodule update diff --git a/s/upgrade b/s/upgrade index 9d9d5a313..e7b91196b 100755 --- a/s/upgrade +++ b/s/upgrade @@ -7,7 +7,7 @@ while [ ! -d $ROOT/.git ]; do done cd $ROOT -git pull +git pull --rebase git submodule update s/build s/runtests From ca5cdd142b00dd6b7c182b7ed063d0b3f17c652b Mon Sep 17 00:00:00 2001 From: Christian Schubert Date: Mon, 10 Aug 2020 16:51:20 +0200 Subject: [PATCH 51/52] refactor epasswd stuff - try to avoid builtin identifiers (like id, file) as variable names - try to avoid bare except - refactor the logic that deals with opening sqlite3 vs. bare passwd-like files; this now resides completely in epasswd.py - add explicit _Faction type for epasswd --- process/checkpasswd.py | 21 +++----- process/epasswd.py | 106 ++++++++++++++++++-------------------- process/getemail.py | 16 +++--- process/getfaction.py | 19 +++---- process/process-orders.py | 23 ++++----- 5 files changed, 79 insertions(+), 106 deletions(-) diff --git a/process/checkpasswd.py b/process/checkpasswd.py index 1e6198600..96aebcd2c 100755 --- a/process/checkpasswd.py +++ b/process/checkpasswd.py @@ -18,20 +18,13 @@ def log(str): if mypasswd[0] == '"': mypasswd = mypasswd.strip('"') -pw_data = EPasswd() -try: - pw_data.load_database(filename) - log("loaded from db " + filename) -except: - pw_data.load_file(filename) - log("loaded from file " + filename) +pw_data = EPasswd.load_any(filename, log=log) +faction = pw_data.get_faction(myfaction) -if pw_data.fac_exists(myfaction): - if pw_data.check(myfaction, mypasswd): - log("password match: " + myfaction) - sys.exit(0) +if not faction: + log("faction missing: " + myfaction) +elif not faction.check_passwd(mypasswd): log("password mismatch: " + myfaction) else: - log("faction missing: " + myfaction) - -sys.exit(-1) + log("password match: " + myfaction) + sys.exit(0) diff --git a/process/epasswd.py b/process/epasswd.py index 0f38fd593..24d789fb6 100755 --- a/process/epasswd.py +++ b/process/epasswd.py @@ -1,63 +1,57 @@ #!/usr/bin/python -from string import split -from string import strip -from string import lower import bcrypt +import collections import sqlite3 -class EPasswd: - def __init__(self): - self.data = {} - - def set_data(self, no, email, passwd): - lc_id = lower(no) - self.data[lc_id] = {} - self.data[lc_id]["id"] = no - self.data[lc_id]["email"] = email - self.data[lc_id]["passwd"] = passwd - - def load_database(self, file): - conn = sqlite3.connect(file) - c = conn.cursor() - for row in c.execute('SELECT `no`, `email`, `password` FROM `faction`'): - (no, email, passwd) = row - self.set_data(no, email, passwd) - conn.close() - - def load_file(self, file): - try: - fp = open(file,"r") - except: - fp = None - if fp != None: - while True: - line = fp.readline() - if not line: break - line = strip(line) - [id, email, passwd] = split(line, ":")[0:3] - self.set_data(id, email, passwd) - fp.close() - - def check(self, id, passwd): - pw = self.get_passwd(id) - if pw[0:4]=='$2a$' or pw[0:4]=='$2y$': + +class _Faction(collections.namedtuple('Faction', 'no email passwd')): + def check_passwd(self, passwd): + if self.passwd.startswith(('$2a$', '$2y$')): try: - uhash = pw.encode('utf8') - upass = passwd.encode('utf8') - return bcrypt.checkpw(upass, uhash) - except: + return bcrypt.checkpw(passwd.encode('utf8'), self.passwd.encode('utf8')) + except UnicodeEncodeError: return False - return pw == passwd - - def get_passwd(self, id): - return self.data[lower(id)]["passwd"] - - def get_email(self, id): - return self.data[lower(id)]["email"] - - def get_canon_id(self, id): - return self.data[lower(id)]["id"] - - def fac_exists(self, id): - return self.data.has_key(lower(id)) + + return passwd == self.passwd + + +class EPasswd: + def __init__(self, generator): + self._data = {row[0]: _Faction(*row) for row in generator} + + @classmethod + def load_database(cls, filename): + conn = sqlite3.connect('file:' + filename + '?mode=ro', uri=True) + try: + cls(conn.execute('SELECT no, email, password FROM faction')) + finally: + conn.close() + + @classmethod + def load_file(cls, filename): + with open(filename, 'r') as f: + return cls( + line.strip().split(':') + for line in f + if line.strip() and not line.startswith('#') + ) + + @classmethod + def load_any(cls, filename, passwd_filename=None, log=None): + try: + result = cls.load_database(filename) + type_ = "db" + except (sqlite3.OperationalError, sqlite3.DatabaseError): + if passwd_filename is not None: + filename = passwd_filename + result = cls.load_file(filename) + type_ = "file" + + if log is not None: + log("loaded from " + type_ + " " + filename) + + return result + + def get_faction(self, no): + return self._data.get(no.lower()) diff --git a/process/getemail.py b/process/getemail.py index d9951bcb0..3f5cc1d86 100755 --- a/process/getemail.py +++ b/process/getemail.py @@ -9,14 +9,10 @@ filename=sys.argv[1] myfaction=sys.argv[2] -pw_data = EPasswd() -try: - pw_data.load_database(filename) -except: - pw_data.load_file(filename) +pw_data = EPasswd.load_any(filename) +faction = pw_data.get_faction(myfaction) -if pw_data.fac_exists(myfaction): - email = pw_data.get_email(myfaction) - print(email) - sys.exit(0) -sys.exit(-1) +if not faction: + sys.exit(-1) + +print(faction.email) diff --git a/process/getfaction.py b/process/getfaction.py index 11350ec8a..92880a7b3 100755 --- a/process/getfaction.py +++ b/process/getfaction.py @@ -14,19 +14,12 @@ def log(str): if not quiet: print(str) -pw_data = EPasswd() -try: - pw_data.load_database(filename) - log("loaded from db " + filename) -except: - pw_data.load_file(filename) - log("loaded from file " + filename) +pw_data = EPasswd.load_any(filename, log=log) +faction = pw_data.get_faction(myfaction) -if pw_data.fac_exists(myfaction): - print(pw_data.get_email(myfaction)) - log("faction found: " + myfaction) - sys.exit(0) -else: +if not faction: log("faction missing: " + myfaction) + sys.exit(-1) -sys.exit(-1) +print(faction.email) +log("faction found: " + myfaction) diff --git a/process/process-orders.py b/process/process-orders.py index dade32c02..722b50ec7 100755 --- a/process/process-orders.py +++ b/process/process-orders.py @@ -82,15 +82,13 @@ def check_pwd(filename, email, pw_data): if mo != None: fact_nr = str(mo.group(2)) fact_pw = str(mo.group(3)) - if pw_data.fac_exists(fact_nr): - if not pw_data.check(fact_nr, fact_pw): - game_email = pw_data.get_email(fact_nr) - results = results + [ (fact_nr, game_email, False, fact_pw) ] - else: - game_email = pw_data.get_email(fact_nr) - results = results + [ (fact_nr, game_email, True, fact_pw) ] + faction = pw_data.get_faction(fact_nr) + + if not faction: + results.append((fact_nr, None, False, fact_pw)) else: - results = results + [ (fact_nr, None, False, fact_pw) ] + results.append((fact_nr, faction.email, faction.check_passwd(fact_pw), fact_pw)) + return results def echeck(filename, locale, rules): @@ -108,11 +106,10 @@ def echeck(filename, locale, rules): return mail #print "reading password file..." -pw_data = EPasswd() -try: - pw_data.load_database(os.path.join(game_dir, "eressea.db")) -except: - pw_data.load_file(os.path.join(game_dir, "passwd")) +pw_data = EPasswd.load_any( + os.path.join(game_dir, "eressea.db"), + os.path.join(game_dir, "passwd"), +) #print "reading orders.queue..." # move the queue file to a safe space while locking it: From f0c1eafee9223a8e2d4cd507eb73f8efe8aa93b0 Mon Sep 17 00:00:00 2001 From: Christian Schubert Date: Mon, 10 Aug 2020 20:43:36 +0200 Subject: [PATCH 52/52] b/f --- process/checkpasswd.py | 1 + 1 file changed, 1 insertion(+) diff --git a/process/checkpasswd.py b/process/checkpasswd.py index 96aebcd2c..09b7b4f27 100755 --- a/process/checkpasswd.py +++ b/process/checkpasswd.py @@ -28,3 +28,4 @@ def log(str): else: log("password match: " + myfaction) sys.exit(0) +sys.exit(-1)