diff --git a/res/messages.xml b/res/messages.xml index 443f98785..6d14bfd30 100644 --- a/res/messages.xml +++ b/res/messages.xml @@ -3800,7 +3800,7 @@ "$unit($unit) in $region($region): '$order($command)' - Das Gebäude kann nur einmal pro Runde erweitert werden." - "$unit($unit) in $region($region): '$order($command)' - Thhe building can be expanded only once per turn." + "$unit($unit) in $region($region): '$order($command)' - The building can be expanded only once per turn." @@ -6460,7 +6460,7 @@ "$unit($unit) in $region($region): '$order($command)' - Die Einheit kann keine weiteren langen Befehle ausführen." - "$unit($unit) in $region($region): '$order($command)' - The unit is exhausted from battle." + "$unit($unit) in $region($region): '$order($command)' - The unit cannot execute more long orders." diff --git a/scripts/tests/common.lua b/scripts/tests/common.lua index 86f625123..f0d987ada 100644 --- a/scripts/tests/common.lua +++ b/scripts/tests/common.lua @@ -771,7 +771,6 @@ end local function find_in_report(f, pattern, extension) extension = extension or "nr" - write_report(f) local filename = config.basepath .. "/reports/" .. get_turn() .. "-" .. itoa36(f.id) .. "." .. extension local report = io.open(filename, 'rt'); t = report:read("*all") @@ -782,19 +781,13 @@ local function find_in_report(f, pattern, extension) return start~=nil end -local function assert_in_report(f, pattern, extension) - assert_not_equal(nil, find_in_report(f, pattern, extension)) -end -local function assert_not_in_report(f, pattern, extension) - assert_equal(nil, find_in_report(f, pattern, extension)) -end - function test_coordinates_no_plane() local r = region.create(0, 0, "mountain") local f = faction.create("noreply@eressea.de", "human", "de") local u = unit.create(f, r, 1) init_reports() - assert_in_report(f, r.name .. " %(0,0%), Berg") + write_report(f) + assert_true(find_in_report(f, r.name .. " %(0,0%), Berg")) end function test_coordinates_named_plane() @@ -803,7 +796,8 @@ function test_coordinates_named_plane() local f = faction.create("noreply@eressea.de", "human", "de") local u = unit.create(f, r, 1) init_reports() - assert_in_report(f, r.name .. " %(0,0,Hell%), Berg") + write_report(f) + assert_true(find_in_report(f, r.name .. " %(0,0,Hell%), Berg")) end function test_coordinates_unnamed_plane() @@ -812,7 +806,8 @@ function test_coordinates_unnamed_plane() local f = faction.create("noreply@eressea.de", "human", "de") local u = unit.create(f, r, 1) init_reports() - assert_in_report(f, r.name .. " %(0,0%), Berg") + write_report(f) + assert_true(find_in_report(f, r.name .. " %(0,0%), Berg")) end function test_coordinates_noname_plane() @@ -821,7 +816,8 @@ function test_coordinates_noname_plane() local f = faction.create("noreply@eressea.de", "human", "de") local u = unit.create(f, r, 1) init_reports() - assert_in_report(f, r.name .. " %(0,0%), Berg") + write_report(f) + assert_true(find_in_report(f, r.name .. " %(0,0%), Berg")) end module( "parser", package.seeall, lunit.testcase ) @@ -864,5 +860,28 @@ function test_bug_1814() read_orders(filename) process_orders() init_reports() + write_report(f) assert_false(find_in_report(f, "Der Befehl wurde nicht erkannt", "cr")) end + +function test_bug_1679() + -- see http://bugs.eressea.de/view.php?id=1679 + local r = region.create(0, 0, "mountain") + local f = faction.create("noreply@eressea.de", "human", "de") + local u = unit.create(f, r, 1) + local filename = "1814.txt" + + local file = io.open(filename, "w+") + file:write('ERESSEA ' .. itoa36(f.id) .. ' "' .. f.password .. '"\n') + file:write('EINHEIT ' .. itoa36(u.id) .. "\n") + file:write("NACH W\n") + file:write("ARBEITEN\n") + file:close() + + read_orders(filename) + process_orders() + init_reports() + write_report(f) + assert_true(find_in_report(f, "Die Einheit kann keine weiteren langen Befehle", "cr")) + assert_true(find_in_report(f, "entdeckt, dass es keinen Weg nach Westen gibt")) +end diff --git a/src/gamecode/economy.c b/src/gamecode/economy.c index 7d19c80a7..0c4110401 100644 --- a/src/gamecode/economy.c +++ b/src/gamecode/economy.c @@ -3349,7 +3349,9 @@ produce(struct region *r) continue; if (fval(u, UFL_LONGACTION) && u->thisorder==NULL) { + /* this message was already given in laws.setdefaults cmistake(u, u->thisorder, 52, MSG_PRODUCE); + */ continue; } diff --git a/src/gamecode/laws.c b/src/gamecode/laws.c index 154ce3b4b..762b4c0ae 100644 --- a/src/gamecode/laws.c +++ b/src/gamecode/laws.c @@ -3335,6 +3335,48 @@ new_units(void) } } +/** Checks for two long orders and issues a warning if necessary. + */ +void check_long_orders(unit *u) { + order *ord; + keyword_t otherorder = MAXKEYWORDS; + + for (ord = u->orders; ord; ord = ord->next) { + if (get_keyword(ord) == NOKEYWORD) { + cmistake(u, ord, 22, MSG_EVENT); + } else if (is_long(ord)) { + keyword_t longorder = get_keyword(ord); + if (otherorder != MAXKEYWORDS) { + switch (longorder) { + case K_CAST: + if (otherorder!=longorder) { + cmistake(u, ord, 52, MSG_EVENT); + } + break; + case K_BUY: + if (otherorder==K_SELL) { + otherorder=K_BUY; + } else { + cmistake(u, ord, 52, MSG_EVENT); + } + break; + case K_SELL: + if (otherorder!=K_SELL && otherorder!=K_BUY) { + cmistake(u, ord, 52, MSG_EVENT); + } + break; + case K_WEREWOLF: + /* don't know what WEREWOLF does... */ + default: + cmistake(u, ord, 52, MSG_EVENT); + } + } else { + otherorder = longorder; + } + } + } +} + static void setdefaults(unit *u) { @@ -3346,9 +3388,14 @@ setdefaults(unit *u) if (hunger) { /* Hungernde Einheiten führen NUR den default-Befehl aus */ set_order(&u->thisorder, default_order(u->faction->locale)); + } else { + check_long_orders(u); } /* check all orders for a potential new long order this round: */ for (ord = u->orders; ord; ord = ord->next) { + if (get_keyword(ord) == NOKEYWORD) + continue; + if (u->old_orders && is_repeated(ord)) { /* this new order will replace the old defaults */ free_orders(&u->old_orders); @@ -3371,9 +3418,6 @@ setdefaults(unit *u) * werden. Da Handel erst nach anderen langen Befehlen kommt, * muß das vorher abgefangen werden. Wir merken uns also * hier, ob die Einheit handelt. */ - case NOKEYWORD: - cmistake(u, ord, 22, MSG_EVENT); - break; case K_BUY: case K_SELL: /* Wenn die Einheit handelt, muß der Default-Befehl gelöscht @@ -3872,7 +3916,8 @@ process(void) porder = punit; while (porder && porder->priority==prio && porder->type==PR_ORDER) { order ** ordp = &u->orders; - if (porder->flags & PROC_THISORDER) ordp = &u->thisorder; + if (porder->flags & PROC_THISORDER) + ordp = &u->thisorder; while (*ordp) { order * ord = *ordp; if (get_keyword(ord) == porder->data.per_order.kword) { @@ -3885,7 +3930,9 @@ process(void) cmistake(u, ord, 224, MSG_MAGIC); ord = NULL; } else if (fval(u, UFL_LONGACTION)) { + /* this message was already given in laws.setdefaults cmistake(u, ord, 52, MSG_PRODUCE); + */ ord = NULL; } else if (fval(r->terrain, SEA_REGION) && u->race != new_race[RC_AQUARIAN] && !(u->race->flags & RCF_SWIM)) { /* error message disabled by popular demand */ diff --git a/src/kernel/order.c b/src/kernel/order.c index 690cd0181..e1513a01f 100644 --- a/src/kernel/order.c +++ b/src/kernel/order.c @@ -361,6 +361,14 @@ parse_order(const char * s, const struct locale * lang) return NULL; } +/** + * Returns true if the order qualifies as "repeated". An order is repeated if it will overwrite the + * old default order. K_BUY is in this category, but not K_MOVE. + * + * \param ord An order. + * \return true if the order is long + * \sa is_exclusive(), is_repeated(), is_persistent() + */ boolean is_repeated(const order * ord) { @@ -420,6 +428,14 @@ is_repeated(const order * ord) return false; } +/** + * Returns true if the order qualifies as "exclusive". An order is exclusive if it makes all other + * long orders illegal. K_MOVE is in this category, but not K_BUY. + * + * \param ord An order. + * \return true if the order is long + * \sa is_exclusive(), is_repeated(), is_persistent() + */ boolean is_exclusive(const order * ord) { @@ -479,6 +495,84 @@ is_exclusive(const order * ord) return false; } +/** + * Returns true if the order qualifies as "long". An order is long if it excludes most other long + * orders. + * + * \param ord An order. + * \return true if the order is long + * \sa is_exclusive(), is_repeated(), is_persistent() + */ +boolean +is_long(const order * ord) +{ + keyword_t kwd = ORD_KEYWORD(ord); + const struct locale * lang = ORD_LOCALE(ord); + param_t param; + + switch (kwd) { + case K_CAST: + case K_BUY: + case K_SELL: + case K_MOVE: + case K_WEREWOLF: + case K_ROUTE: + case K_DRIVE: + case K_WORK: + case K_BESIEGE: + case K_ENTERTAIN: + case K_TAX: + case K_RESEARCH: + case K_SPY: + case K_STEAL: + case K_SABOTAGE: + case K_STUDY: + case K_TEACH: + case K_BREED: + case K_PIRACY: + return true; + + case K_PLANT: + return true; + + case K_FOLLOW: + /* FOLLOW is only a long order if we are following a ship. */ + parser_pushstate(); + init_tokens(ord); + skip_token(); + param = getparam(lang); + parser_popstate(); + + if (param == P_SHIP) return true; + break; + + case K_MAKE: + /* Falls wir MACHE TEMP haben, ignorieren wir es. Alle anderen + * Arten von MACHE zaehlen aber als neue defaults und werden + * behandelt wie die anderen (deswegen kein break nach case + * K_MAKE) - und in thisorder (der aktuelle 30-Tage Befehl) + * abgespeichert). */ + parser_pushstate(); + init_tokens(ord); /* initialize token-parser */ + skip_token(); + param = getparam(lang); + parser_popstate(); + + if (param != P_TEMP) return true; + break; + } + return false; +} + +/** + * Returns true if the order qualifies as "persistent". An order is persistent if it will be + * included in the template orders. @-orders, comments and most long orders are in this category, + * but not K_MOVE. + * + * \param ord An order. + * \return true if the order is persistent + * \sa is_exclusive(), is_repeated(), is_persistent() + */ boolean is_persistent(const order * ord) { diff --git a/src/kernel/order.h b/src/kernel/order.h index 29f273863..f840ad7fc 100644 --- a/src/kernel/order.h +++ b/src/kernel/order.h @@ -51,6 +51,7 @@ extern char * getcommand(const order * ord); extern boolean is_persistent(const order *ord); extern boolean is_exclusive(const order *ord); extern boolean is_repeated(const order * ord); +extern boolean is_long(const order *ord); extern char * write_order(const order * ord, char * buffer, size_t size);